diff options
| author | Ryan Johnson <rjohnson@mozilla.com> | 2021-04-29 16:16:42 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-04-29 16:16:42 -0700 |
| commit | 95aca4b4d8fa62815d4bd412fff1a364f842814a (patch) | |
| tree | 5e57661720fe9058d5c7db637e764800b50f9060 /files/uk/web/javascript/closures | |
| parent | ee3b1c87e3c8e72ca130943eed260ad642246581 (diff) | |
| download | translated-content-95aca4b4d8fa62815d4bd412fff1a364f842814a.tar.gz translated-content-95aca4b4d8fa62815d4bd412fff1a364f842814a.tar.bz2 translated-content-95aca4b4d8fa62815d4bd412fff1a364f842814a.zip | |
remove retired locales (#699)
Diffstat (limited to 'files/uk/web/javascript/closures')
| -rw-r--r-- | files/uk/web/javascript/closures/index.html | 471 |
1 files changed, 0 insertions, 471 deletions
diff --git a/files/uk/web/javascript/closures/index.html b/files/uk/web/javascript/closures/index.html deleted file mode 100644 index 1abe835739..0000000000 --- a/files/uk/web/javascript/closures/index.html +++ /dev/null @@ -1,471 +0,0 @@ ---- -title: Closures -slug: Web/JavaScript/Closures -tags: - - Замикання -translation_of: Web/JavaScript/Closures ---- -<div>{{jsSidebar("Intermediate")}}</div> - -<p class="summary">Замикання - це функції, що посилаються на незалежні (вільні) змінні (змінні, які використовуються локально, але визначені в обмеженій області видимості). Іншими словами, ці функції "пам'ятають" середовище, в якому вони були створені.</p> - -<h2 id="Лексичне_середовище">Лексичне середовище</h2> - -<p>Розглянемо наступне:</p> - -<div> -<pre class="brush: js notranslate">function init() { - var name = "Mozilla"; // name - це локальна змінна, створена в функції init - function displayName() { // displayName() - це внутрішня функція, замикання - alert(name); // використовує змінну, оголошену в батьківській функції - } - displayName(); -} -init();</pre> -</div> - -<p><code>init()</code> створює локальну змінну <code>name</code> та функцію <code>displayName()</code>. <code>displayName()</code> є вкладеною функцією, що оголошена всередині <code>init()</code> та є доступною тільки в тілі цієї функції. <code>displayName()</code> не має локальних змінних, проте вкладені функції мають доступ до змінних зовнішніх функцій саме тому <code>displayName()</code> може використовувати імена змінних, оголошених в батьківській функції <code>init()</code>.</p> - -<p>{{JSFiddleEmbed("https://jsfiddle.net/78dg25ax/", "js,result", 200)}}</p> - -<p><a href="http://jsfiddle.net/xAFs9/3/" title="http://jsfiddle.net/xAFs9/">Запустіть</a> цей код і побачите, що alert() всередині функції displayName() успішно відображає змінну name, оголошену в батьківській функції. Це є прикладом лексичного середовища (lexical environment), яке описує, яким чином парсер звертається до змінних, коли функції вкладені. Слово <em>лексичне</em> посилається на факт, що лексичне середовище використовує розташування, де в коді була створена змінна задля визначення середовища, де ця змінна доступна. Вкладені функції мають доступ до змінних, оголошених у їх зовнішній області.</p> - -<h2 id="Замикання">Замикання</h2> - -<p>Тепер розглянемо наступний приклад:</p> - -<pre class="brush: js notranslate">function makeFunc() { - var name = "Mozilla"; - function displayName() { - alert(name); - } - return displayName; -} - -var myFunc = makeFunc(); -myFunc(); -</pre> - -<p>Результат запуску цього коду буде таким самим, як і в попередньому прикладі функції <code>init()</code>: рядок "Mozilla" буде відображений в спливаючому повідомленні <font face="consolas, Liberation Mono, courier, monospace">alert</font>(). Цікавою відмінністю є те, що внутрішня функція <code>displayName() </code>буде повернена з зовнішньої функції перш ніж вона буде виконана. </p> - -<p>Той факт, що цей код досі працює, може здаватись неочевидним. В деяких мовах програмування локальні змінні існують лише протягом часу виконання функції. Як тільки <code>makeFunc()</code> завершила своє виконання, слід очікувати, що змінна <font face="consolas, Liberation Mono, courier, monospace"><span style="background-color: #eeeeee;">name</span></font> не буде більше існувати. Однак у JavaScript цей підхід інакший, оскільки даний код працює, як і очікувалось.</p> - -<p>Це відбувається тому що ці функції в JavaScript утворюють замикання. <em>Замикання</em> це комбінація функції і лексичного середовища (або просто середовища), всередині якого функція була оголошена. Середовище складається з будь-яких локальних змінних, які були в області видимості в той час, коли замикання було створене. В цьому випадку, <code>myFunc</code> є посиланням на екземпляр функції <font face="consolas, Liberation Mono, courier, monospace"><span style="background-color: #eeeeee;">displayName</span></font> створений, коли <font face="consolas, Liberation Mono, courier, monospace"><span style="background-color: #eeeeee;">makeFunc</span></font> була виконана. Екземпляр <code>displayName</code> включає посилання на власне лексичне середовище в середині якого існує змінна <code>name</code>. З цієї причини, коли <code>myFunc</code> викликається, змінна <code>name</code> залишається доступною для використання і "Mozilla" передається до <code>alert</code>. </p> - -<p>Ось трохи цікавіший приклад — a <code>makeAdder</code> function:</p> - -<pre class="brush: js notranslate">function makeAdder(x) { - return function(y) { - return x + y; - }; -} - -var add5 = makeAdder(5); -var add10 = makeAdder(10); - -console.log(add5(2)); // 7 -console.log(add10(2)); // 12 -</pre> - -<p>У цьому прикладі ми визначили функцію <code>makeAdder(x),</code> яка приймає єдиний аргумент <code>x</code> і повертає нову функцію. Функція, яку вона повертає, приймає єдиний аргумент <code>y</code>, і повертає суму <code>x</code> і <code>y</code>.</p> - -<p>По суті, <code>makeAdder</code> це фабрика функцій — вона створює функції, які можуть додавати певне значення до свого аргументу. У наведеному вище прикладі ми використовуємо нашу фабричну функцію для створення двох нових функцій — одна додає 5 до свого аргументу, і інша додає 10.</p> - -<p><code>add5</code> і <code>add10</code> є прикладами замикання. Вони поділяють одне визначення тіла функції, але зберігають різні лексичні середовища. В лексичному середовищі <code>add5</code> <code>x</code> - це 5, а в лексичному середовищі <code>add10</code> <code>x</code>- це 10.</p> - -<h2 id="Замикання_на_практиці">Замикання на практиці</h2> - -<p>Замикання корисні, оскільки вони дозволяють зв'язати деякі дані (лексичне середовище) з функцією, яка працює з цими даними. Це має очевидні паралелі з об'єктно-орієнтованим програмуванням, де об'єкти дозволяють нам зв'язати деякі дані (властивості об'єкта) з одним або декількома методами.</p> - -<p>Отже, замикання можна використовувати всюди, де зазвичай використовується об'єкт з одним єдиним методом. </p> - -<p>Ситуації, в яких ви можливо захочете зробити це, особливо поширені в web-розробці. Велика частина front-end коду, написана на JavaScript, основана на обробці подій. Ви визначаєте деяку поведінку, а потім прикріплюєте її до події, яка запускається користувачем (наприклад, клік або натискання клавіші). Код, як правило, прив'язується до події як зворотній виклик <strong>(callback)</strong> - функція, яка виконується у відповідь на виникнення події.</p> - -<p>Наприклад припустимо, що ми хочемо додати кілька кнопок на сторінку, які змінюватимуть розмір тексту. Один із способів зробити це - вказати <code>font-size</code> елементу <code>body</code> у пікселях, а потім встановити розмір інших елементів на сторінці (таких як заголовки) за допомогою відносних одиниць виміру <code>em</code>:</p> - -<pre class="brush: css notranslate">body { - font-family: Helvetica, Arial, sans-serif; - font-size: 12px; -} - -h1 { - font-size: 1.5em; -} - -h2 { - font-size: 1.2em; -} -</pre> - -<p>Такі інтерактивні кнопки розміру тексту можуть змінювати властивість <code>font-size</code> елемента <code>body</code>, і налаштування будуть підхоплені іншими елементами на сторінці завдяки відносним одиницям.</p> - -<p>Ось JavaScript:</p> - -<pre class="brush: js notranslate">function makeSizer(size) { - return function() { - document.body.style.fontSize = size + 'px'; - }; -} - -var size12 = makeSizer(12); -var size14 = makeSizer(14); -var size16 = makeSizer(16); -</pre> - -<p> Тепер <code>size12</code>, <code>size14</code>, і <code>size16</code> є функціями, які будуть змінювати розмір тексту елемента body відповідно до 12, 14 та 16 пікселів. Ми можемо прив'язати їх до кнопок таким чином:</p> - -<pre class="brush: js notranslate">document.getElementById('size-12').onclick = size12; -document.getElementById('size-14').onclick = size14; -document.getElementById('size-16').onclick = size16; -</pre> - -<pre class="brush: html notranslate"><a href="#" id="size-12">12</a> -<a href="#" id="size-14">14</a> -<a href="#" id="size-16">16</a> -</pre> - -<p>{{JSFiddleEmbed("https://jsfiddle.net/vnkuZ/","","200")}}</p> - -<h2 id="Емуляція_приватних_методів_з_замиканнями">Емуляція приватних методів з замиканнями</h2> - -<p>В таких мовах як Java є можливість об'являти методи приватними, що означає їх здатність бути викликаними лише іншими методами того ж класу. </p> - -<p>JavaScript не має власного способу зробити це, але є можливість емуляції приватних методів використовуючи замикання. Приватні методи корисні не лише для обмеження доступу до коду: вони також забезпечують потужний спосіб управління глобальним простором імен, зберігаючи несуттєві методи від захаращення публічного інтерфейсу до вашого коду.</p> - -<p>Ось як визначити деякі загальнодоступні функції, які можуть отримати доступ до приватних функцій та змінних, використовуючи замикання, також відомі як модель модуля (<a class="external" href="http://www.google.com/search?q=javascript+module+pattern" title="http://www.google.com/search?q=javascript+module+pattern">module pattern</a>).</p> - -<pre class="brush: js notranslate">var counter = (function() { - var privateCounter = 0; - function changeBy(val) { - privateCounter += val; - } - return { - increment: function() { - changeBy(1); - }, - decrement: function() { - changeBy(-1); - }, - value: function() { - return privateCounter; - } - }; -})(); - -console.log(counter.value()); // logs 0 -counter.increment(); -counter.increment(); -console.log(counter.value()); // logs 2 -counter.decrement(); -console.log(counter.value()); // logs 1 -</pre> - -<p>Тут багато чого відбувається. В попередніх пригладах кожне замикання мало своє власне оточення. Тут, однак, існує одне лексичне середовище, яке поділяють три функції: <code>counter.increment</code>, <code>counter.decrement</code>, and <code>counter.value</code>.</p> - -<p>Спільне лексичне середовище створюється в тілі анонімної функції, яка викликається одразу після створення. Навколишнє середовище містить два приватні складові: змінну <code>privateCounter</code> і функцію <code>changeBy</code>. Жодна із цих приватних складових не може бути доступна безпосередньо за межами анонімної функції. Натомість до них мають бути доступні три загальнодоступні функції, які повертаються з анонімної обгортки.</p> - -<p>Ці три функції - це замикання, які мають одне і те ж середовище. Завдяки лексичному оточенню JavaScript, кожна з них має доступ до змінної privateCounter та функції changeBy.</p> - -<p>Ми визначаємо анонімну функцію, яка створює лічильник, а потім ми викликаємо її негайно і присвоюємо результат змінній лічильника.Ми можемо зберігати цю функцію в окремій змінній makeCounter і використовувати її для створення декількох лічильників</p> - -<pre class="brush: js notranslate">var makeCounter = function() { - var privateCounter = 0; - function changeBy(val) { - privateCounter += val; - } - return { - increment: function() { - changeBy(1); - }, - decrement: function() { - changeBy(-1); - }, - value: function() { - return privateCounter; - } - } -}; - -var counter1 = makeCounter(); -var counter2 = makeCounter(); -alert(counter1.value()); /* Alerts 0 */ -counter1.increment(); -counter1.increment(); -alert(counter1.value()); /* Alerts 2 */ -counter1.decrement(); -alert(counter1.value()); /* Alerts 1 */ -alert(counter2.value()); /* Alerts 0 */ -</pre> - -<p>Зауважте, як кожен з двох лічильників підтримує свою незалежність від іншого. Його середовище під час виклику функції makeCounter () щоразу відрізняється. Змінна замикання privateCounter містить кожен раз інший екземпляр.</p> - -<p>Використання замикання таким чином забезпечує ряд переваг, які зазвичай пов'язані з об'єктно-орієнтованим програмуванням, зокрема приховування даних та інкапсуляція.</p> - -<h2 id="Ланцюжок_замикань">Ланцюжок замикань</h2> - -<p>Кожне замикання має три оточення:</p> - -<ul> - <li>Local Scope (власне оточення)</li> - <li>Outer Functions Scope (зовнішнє функціональне оточення)</li> - <li>Global Scope (глобальне оточення)</li> -</ul> - -<p>Поширеною помилкою є не усвідомлення того, що у випадку, коли зовнішня функція сама є вкладеною функцією, доступ до оточення її зовнішньої функції включає вкладене оточення її як зовнішньої функції - ефективно створюючи ланцюг областей оточення. Для демонстрації розглянемо наступний приклад коду.</p> - -<pre class="notranslate">// global scope -var e = 10; -function sum(a){ - return function(b){ - return function(c){ - // outer functions scope - return function(d){ - // local scope - return a + b + c + d + e; - } - } - } -} - -console.log(sum(1)(2)(3)(4)); // log 20 - -// You can also write without anonymous functions: - -// global scope -var e = 10; -function sum(a){ - return function sum2(b){ - return function sum3(c){ - // outer functions scope - return function sum4(d){ - // local scope - return a + b + c + d + e; - } - } - } -} - -var s = sum(1); -var s1 = s(2); -var s2 = s1(3); -var s3 = s2(4); -console.log(s3) //log 20 -</pre> - -<p>У наведеному вище прикладі є низка вкладених функцій, всі з яких мають доступ до області оточення зовнішніх функцій. У цьому контексті можна сказати, що замикання мають доступ до всіх зовнішніх областей функцій.</p> - -<h2 id="Creating_closures_in_loops_A_common_mistake">Creating closures in loops: A common mistake</h2> - -<p>До введення ключового слова let у ECMAScript 2015, загальна проблема із замиканням виникала, коли вони створювалися всередині циклу. Розглянемо наступний приклад: </p> - -<pre class="brush: html notranslate"><p id="help">Helpful notes will appear here</p> -<p>E-mail: <input type="text" id="email" name="email"></p> -<p>Name: <input type="text" id="name" name="name"></p> -<p>Age: <input type="text" id="age" name="age"></p> -</pre> - -<pre class="brush: js notranslate">function showHelp(help) { - document.getElementById('help').innerHTML = help; -} - -function setupHelp() { - var helpText = [ - {'id': 'email', 'help': 'Your e-mail address'}, - {'id': 'name', 'help': 'Your full name'}, - {'id': 'age', 'help': 'Your age (you must be over 16)'} - ]; - - for (var i = 0; i < helpText.length; i++) { - var item = helpText[i]; - document.getElementById(item.id).onfocus = function() { - showHelp(item.help); - } - } -} - -setupHelp(); -</pre> - -<p>{{JSFiddleEmbed("https://jsfiddle.net/v7gjv/", "", 200)}}</p> - -<p>Масив helpText визначає три корисні підказки, кожну пов'язану з ID поля Input. Цикл циклично перераховує ці визначення, підключаючи подію onfocus до кожного, що показує пов'язаний метод help.</p> - -<p>Якщо ви спробуєте цей код, ви побачите, що він не працює, як очікувалося. Незалежно від того, на якому полі ви зосереджені, відображатиметься повідомлення про ваш вік.</p> - -<p>Причиною цього є те, що функції, призначені для фокусування, - це замикання; вони складаються з визначення функції та захопленого оточення з області видимосты функцій setupHelp. Три замикання були створені циклом, але кожне ділиться тим самим єдиним середовищем, яке має змінну зі змінними значеннями (item.help).</p> - -<p>Коли виконуються зворотні виклики onfocus, доступ до item.help в цей момент викликає таку поведінку (дійсно, оскільки значення змінної доступно / обчислюється лише під час виконання), оскільки цикл до цього часу виконувався, а об'єкт змінної елемента (розділений усіма трьома замиканнями) був залишений, вказуючи на останній запис у списку довідкового тексту.</p> - -<p>Одне рішення в цьому випадку - використовувати більше замикань: зокрема, використовувати фабрику функцій, як описано раніше:</p> - -<pre class="brush: js notranslate">function showHelp(help) { - document.getElementById('help').innerHTML = help; -} - -function makeHelpCallback(help) { - return function() { - showHelp(help); - }; -} - -function setupHelp() { - var helpText = [ - {'id': 'email', 'help': 'Your e-mail address'}, - {'id': 'name', 'help': 'Your full name'}, - {'id': 'age', 'help': 'Your age (you must be over 16)'} - ]; - - for (var i = 0; i < helpText.length; i++) { - var item = helpText[i]; - document.getElementById(item.id).onfocus = makeHelpCallback(item.help); - } -} - -setupHelp(); -</pre> - -<p>{{JSFiddleEmbed("https://jsfiddle.net/v7gjv/1/", "", 300)}}</p> - -<p>Це працює як очікувалося. Замість того, щоб зворотні виклики мали спільний доступ до одного середовища, функція makeHelpCallback створює нове середовище для кожного з них, в якій довідка посилається на відповідний рядок із масиву helpText.</p> - -<p>Ще один спосіб написати вищезгадане - використання анонімних замикань:</p> - -<pre class="brush: js notranslate">function showHelp(help) { - document.getElementById('help').innerHTML = help; -} - -function setupHelp() { - var helpText = [ - {'id': 'email', 'help': 'Your e-mail address'}, - {'id': 'name', 'help': 'Your full name'}, - {'id': 'age', 'help': 'Your age (you must be over 16)'} - ]; - - for (var i = 0; i < helpText.length; i++) { - (function() { - var item = helpText[i]; - document.getElementById(item.id).onfocus = function() { - showHelp(item.help); - } - })(); // Immediate event listener attachment with the current value of item (preserved until iteration). - } -} - -setupHelp();</pre> - -<p>Якщо ви не хочете більше використовувати замикання, можете скористатися ключовим словом let з ES6:</p> - -<pre class="brush: js notranslate">function showHelp(help) { - document.getElementById('help').innerHTML = help; -} - -function setupHelp() { - var helpText = [ - {'id': 'email', 'help': 'Your e-mail address'}, - {'id': 'name', 'help': 'Your full name'}, - {'id': 'age', 'help': 'Your age (you must be over 16)'} - ]; - - for (var i = 0; i < helpText.length; i++) { - let item = helpText[i]; - document.getElementById(item.id).onfocus = function() { - showHelp(item.help); - } - } -} - -setupHelp();</pre> - -<p>Цей приклад використовує let замість var, таким чином кожне замикання прив'язує блочну змінну,це означає, що додаткові замикання не потрібні.</p> - -<p>Іншою альтернативою може бути використання forEach () для повторення масиву helpText та приєднання слухача до кожного <p>, як показано:</p> - -<pre class="notranslate">function showHelp(help) { - document.getElementById('help').innerHTML = help; -} - -function setupHelp() { - var helpText = [ - {'id': 'email', 'help': 'Your e-mail address'}, - {'id': 'name', 'help': 'Your full name'}, - {'id': 'age', 'help': 'Your age (you must be over 16)'} - ]; - - helpText.forEach(function(text) { - document.getElementById(text.id).onfocus = function() { - showHelp(text.help); - } - }); -} - -setupHelp(); -</pre> - -<h2 id="Performance_considerations">Performance considerations</h2> - -<p>Нерозумно створювати функції в межах інших функцій, якщо замикання не потрібне для конкретного завдання, оскільки це негативно вплине на продуктивність сценарію як з точки зору швидкості обробки, так і споживання пам'яті.</p> - -<p>Наприклад, при створенні нового об'єкта / класу методи, як правило, повинні бути пов'язані з прототипом об'єкта, а не визначені в конструкторі об'єктів. Причина полягає в тому, що щоразу, коли конструктор викликається, методи отримують перепризначення (тобто для кожного створення об'єкта).</p> - -<p>Розглянемо наступний непрактичний, але показовий випадок:</p> - -<pre class="brush: js notranslate">function MyObject(name, message) { - this.name = name.toString(); - this.message = message.toString(); - this.getName = function() { - return this.name; - }; - - this.getMessage = function() { - return this.message; - }; -} -</pre> - -<p>Попередній код не використовує переваги замикань, а тому може виглядати:</p> - -<pre class="brush: js notranslate">function MyObject(name, message) { - this.name = name.toString(); - this.message = message.toString(); -} -MyObject.prototype = { - getName: function() { - return this.name; - }, - getMessage: function() { - return this.message; - } -}; -</pre> - -<p>Однак переробляти прототип не рекомендується, тому наступний приклад ще кращий, оскільки він додається до існуючого прототипу:</p> - -<pre class="brush: js notranslate">function MyObject(name, message) { - this.name = name.toString(); - this.message = message.toString(); -} -MyObject.prototype.getName = function() { - return this.name; -}; -MyObject.prototype.getMessage = function() { - return this.message; -}; -</pre> - -<p>Код вище також може бути записаний чистіше з тим же результатом:</p> - -<pre class="brush: js notranslate">function MyObject(name, message) { - this.name = name.toString(); - this.message = message.toString(); -} -(function() { - this.getName = function() { - return this.name; - }; - this.getMessage = function() { - return this.message; - }; -}).call(MyObject.prototype); -</pre> - -<p>In the three previous examples, the inherited prototype can be shared by all objects and the method definitions need not occur at every object creation. See <a href="/en-US/docs/Web/JavaScript/Guide/Details_of_the_Object_Model">Details of the Object Model</a> for more details. У трьох попередніх прикладах успадкований прототип може бути спільним для всіх об'єктів, і визначення методів не повинно виникати при кожному створенні об'єкта. Докладніше див. Деталі Об'єктної Моделі (<a href="/en-US/docs/Web/JavaScript/Guide/Details_of_the_Object_Model">Details of the Object Model</a>).</p> |
