diff options
author | Peter Bengtsson <mail@peterbe.com> | 2020-12-08 14:43:23 -0500 |
---|---|---|
committer | Peter Bengtsson <mail@peterbe.com> | 2020-12-08 14:43:23 -0500 |
commit | 218934fa2ed1c702a6d3923d2aa2cc6b43c48684 (patch) | |
tree | a9ef8ac1e1b8fe4207b6d64d3841bfb8990b6fd0 /files/uk/web/javascript/a_re-introduction_to_javascript | |
parent | 074785cea106179cb3305637055ab0a009ca74f2 (diff) | |
download | translated-content-218934fa2ed1c702a6d3923d2aa2cc6b43c48684.tar.gz translated-content-218934fa2ed1c702a6d3923d2aa2cc6b43c48684.tar.bz2 translated-content-218934fa2ed1c702a6d3923d2aa2cc6b43c48684.zip |
initial commit
Diffstat (limited to 'files/uk/web/javascript/a_re-introduction_to_javascript')
-rw-r--r-- | files/uk/web/javascript/a_re-introduction_to_javascript/index.html | 949 |
1 files changed, 949 insertions, 0 deletions
diff --git a/files/uk/web/javascript/a_re-introduction_to_javascript/index.html b/files/uk/web/javascript/a_re-introduction_to_javascript/index.html new file mode 100644 index 0000000000..83db347460 --- /dev/null +++ b/files/uk/web/javascript/a_re-introduction_to_javascript/index.html @@ -0,0 +1,949 @@ +--- +title: Повторне введення в JavaScript (JS-підручник) +slug: Web/JavaScript/A_re-introduction_to_JavaScript +translation_of: Web/JavaScript/A_re-introduction_to_JavaScript +--- +<div>{{jsSidebar}}</div> + +<h2 id="Вступ">Вступ</h2> + +<p>Чому повторне введення? Тому що {{Glossary ("JavaScript")}} відомий тим, що він є <a href="http://javascript.crockford.com/javascript.html">найпопулярнішою у світі мовою програмування</a>. Його часто висміюють як іграшку, але під його шаром оманливої простоти чекають потужні мовні особливості. JavaScript зараз використовується неймовірним числом популярних додатків, які показують, що більш глибоке знання цієї технології є важливим вмінням для будь-якого веб-розробника або розробника мобільних пристроїв.</p> + +<p>Варто почати з огляду історії мови. JavaScript був створений у 1995 році Бренданом Айком, коли він був інженером у Netscape. JavaScript був вперше випущений з Netscape 2 на початку 1996 року. Спочатку він мав назву LiveScript, але він був перейменований у злополучному маркетинговому рішенні, яке спробувало скористатися популярністю Java-мови Sun Microsystem - незважаючи на те, що вони мають мало спільного. З тих пір це стало джерелом плутанини.</p> + +<p>Трохи пізніше Microsoft випустила дуже схожу та практично сумісну мову JScript який йшов разом з Internet Explorer 3. Через пару місяців Netscape відправила мову JavaScript до <a class="external" href="http://www.ecma-international.org/">Ecma International</a>, Європейську організацію, яка займається стандартами, яка випустила першу версію стандарту {{Glossary("ECMAScript")}} в 1997 року. Стандарт отримав значуще оновлення в <a class="external" href="http://www.ecma-international.org/publications/standards/Ecma-262.htm">ECMAScript edition 3</a> в 1999 році, і залишається найстабільнішим до сьогоднішнього дня. Четверта версія була відхилена, через проблеми з ускладненням в мові. Більшість речей з четвертого видання послужили основою для стандарту ECMAScript edition 5, оприлюднений в грудні 2009 року, і для 6-го основного видання стандарту, оприлюдненого в червні 2015 року.</p> + +<div class="note"> +<p>На замітку: На далі по тексту ми будемо називати мову ECMAScript як "JavaScript".</p> +</div> + +<p>На відміну від більшості мов програмування, JavaScript не наслідує концепцію введення(input) та виведенн(output). Він спроектований таким чином, щоб запускатися як мова сценаріїв, вбудованих в середовище виконання. Найпопулярніше середовище виконання це браузер, однак інтепретатори JavaScript присутні також в Adobe Acrobat, Adobe Photoshop, SVG images, Yahoo's Widget engine, і навіть в серверному середовищі наприклад <a href="http://nodejs.org/" title="nodejs.org">Node.js</a>.</p> + +<h2 id="Загальний_огляд">Загальний огляд</h2> + +<p>JavaScript це мультіпарадігматична, динамічна мова з типами та операторами, стандартними вбудованими об'єктами та методами. ЇЇ синтаксис базован на мовах Java та C - взагалі, більша частина структури цих мов закладена в JavaScript. JavaScript підтримує об'єктно-орієнтований підхід через прототипи об'єктів, замість класів (дивись більше про <a href="https://developer.mozilla.org/uk/docs/Web/JavaScript/Inheritance_and_the_prototype_chain" title="prototypical inheritance">успадкування через прототипи</a> та ES2015 {{jsxref("Classes")}}). JavaScript також підтримує функціональне програмування — тому що функції це об'єкти, їх можна тримати в змінних та передавати як будь-які інші об'єкти.</p> + +<p>Давайте почнемо огляд з блоків будови будь-якої мови: з типів. програми JavaScript керують значеннями-даними та ці значення належать до якогось типу. JavaScript має наступні типи данних:</p> + +<ul> + <li>{{jsxref("Number")}}</li> + <li>{{jsxref("String")}}</li> + <li>{{jsxref("Boolean")}}</li> + <li>{{jsxref("Function")}}</li> + <li>{{jsxref("Object")}}</li> + <li>{{jsxref("Symbol")}} (нове в ES2015)</li> +</ul> + +<p>... О, ще й {{jsxref("undefined")}} та {{jsxref("null")}}, які є ... досить відокремленими типами. Та {{jsxref("Array")}}, який є спеціальним видом об'єкту. Також {{jsxref("Date")}} та{{jsxref("RegExp")}}, які є вбудованними видами об'єктів. Та, якщо бути технічно акуратними, функція це всього-навсього спеціальний тип об'єкту. Тож діаграмма типів має виглядати наступним чином:</p> + +<ul> + <li>{{jsxref("Number")}}</li> + <li>{{jsxref("String")}}</li> + <li>{{jsxref("Boolean")}}</li> + <li>{{jsxref("Symbol")}} (нове в ES2015)</li> + <li>{{jsxref("Object")}} + <ul> + <li>{{jsxref("Function")}}</li> + <li>{{jsxref("Array")}}</li> + <li>{{jsxref("Date")}}</li> + <li>{{jsxref("RegExp")}}</li> + </ul> + </li> + <li>{{jsxref("null")}}</li> + <li>{{jsxref("undefined")}}</li> +</ul> + +<p>Також є вбудований {{jsxref("Error")}} тип. На першій діаграммі усе видається простішим, та далі ми обговоримо усі типи, що були описані.</p> + +<h2 id="Числа_Numbers">Числа (Numbers)</h2> + +<p>Numbers в JavaScript є "значення подвійної точністі 64-бітового формату IEEE 754", згідно специфікаціїї. Це має деякі цікаві наслідки. В JavaScript <em><strong>немає цілих чисел (integer)</strong></em>, тож вам треба бути уважнішими з аріфметикою в порівнянні з математикою мов C або Java. Таким чином, <em>ціле число насправді є неявним плаваючим</em>.</p> + +<p>Також, обережніше з такими випадками:</p> + +<pre class="brush: js notranslate">0.1 + 0.2 == 0.30000000000000004; +</pre> + +<p>На практиці, цілі (integer) значення представляються як 32-бітні цілі, та деякі реалізації навіть зберігають їх таким чином до тих пір, <span class="tlid-translation translation" lang="uk"><span title="">поки не буде потреби виконати інструкцію, дійсну для числа (Number), але не для 32-бітного цілого числа.</span> <span title="">Це може бути важливо для побітових (bit-wise) операцій.</span></span></p> + +<p><span class="tlid-translation translation" lang="uk"><span title="">Підтримуються стандартні </span></span>арифметичні оператори (<a href="/uk/docs/Web/JavaScript/Reference/Operators#Arithmetic_operators">arithmetic operators</a>), <span class="tlid-translation translation" lang="uk"><span title="">включаючи додавання, віднімання, модуль (або залишок) тощо.</span> <span title="">Існує також вбудований об'єкт, про який ми не згадували раніше, так званий {{jsxref ("Math")}}, який забезпечує розширені математичні функції та константи:</span></span></p> + +<pre class="brush: js notranslate">Math.sin(3.5); +var circumference = 2 * Math.PI * r; +</pre> + +<p>Ви можете конвертувати рядки (string) в цілі числа (integer) за допомогою вбудованої {{jsxref("Global_Objects/parseInt", "parseInt()")}} функції. Вона приймає опційним другим аргументом основу системи числення (яку бажано передавати завжди):</p> + +<pre class="brush: js notranslate">parseInt('123', 10); // 123 +parseInt('010', 10); // 10 +</pre> + +<p>У браузерах попередніх версій рядки, що починаються з "0", розглядаються, як числа з основою 8 (radix 8), але це вже не є стандартом з 2013 року. Вас може здивувати результат наступних дій в старших браузерах:</p> + +<pre class="brush: js notranslate">parseInt('010'); // 8 +parseInt('0x10'); // 16 +</pre> + +<p>В цьому зразку коду ми бачимо {{jsxref("Global_Objects/parseInt", "parseInt()")}} функції приймають перший string як число в вісімковій системі (octal) через передуючий "0", да другий string приймається як число в шістнадцятковій системі (hexadecimal) через передуючий "0x". <em>Шістнадцяткова нотація все ще оброблюється в нових браузерахю Тількі вісімкову було прибрано з мови</em>.</p> + +<p>Якщо бажаєте конвертувати бінарне число в звичайне ціле (integer), просто зміність базу (base):</p> + +<pre class="brush: js notranslate">parseInt('11', 2); // 3 +</pre> + +<p>Таким же чином, ви можете конвертувати числа із плаваючою комою, використавши вбудовані функції {{jsxref("Global_Objects/parseFloat", "parseFloat()")}}. На відміну від своїх двоюрідних сестер {{jsxref("Global_Objects/parseInt", "parseInt()")}} , <code>parseFloat()</code> завжди використовує десятичну базу.</p> + +<p>Ви також можете використовувати унарний операнд <code>+</code> для конвертації в числа (numbers):</p> + +<pre class="brush: js notranslate">+ '42'; // 42 ++ '010'; // 10 ++ '0x10'; // 16 +</pre> + +<p>Спеціальне значення, що називається {{jsxref("NaN")}} (скорочення від "Not a Number" - не число) повертається якщо строка є не-числом (non-numeric):</p> + +<pre class="brush: js notranslate">parseInt('hello', 10); // NaN +</pre> + +<p><code>NaN</code> токсичний! Якщо ви зробите його вхідними даними для будь-якої математичної операції - результатом буде також <code>NaN</code>:</p> + +<pre class="brush: js notranslate">NaN + 5; // NaN +</pre> + +<p>Вы можете тестувати <code>NaN</code>, використовуючи вбудовану функцію {{jsxref("Global_Objects/isNaN", "isNaN()")}}:</p> + +<pre class="brush: js notranslate">isNaN(NaN); // true +</pre> + +<p>JavaScript також має спеціальні значення {{jsxref("Infinity")}} та <code>-Infinity</code>:</p> + +<pre class="brush: js notranslate"> 1 / 0; // Infinity +-1 / 0; // -Infinity +</pre> + +<p>Ви можете тестувати <code>Infinity</code>, <code>-Infinity</code> <code>та</code><code>NaN</code> значення, використвовуючи вбудовані вбудовані функції {{jsxref("Global_Objects/isFinite", "isFinite()")}}:</p> + +<pre class="brush: js notranslate">isFinite(1 / 0); // false +isFinite(-Infinity); // false +isFinite(NaN); // false +</pre> + +<div class="note">{{jsxref("Global_Objects/parseInt", "parseInt()")}} та {{jsxref("Global_Objects/parseFloat", "parseFloat()")}} функції розкладають string допоки не зустрінуть символ, що не є валідним для заданого числового формату, потім повертає число, що було розгорнуто із string до цієї точки. Однак оператор "+" конвертує string до <code>NaN</code> якщо було виявлене некоректне значення. Просто спробуйте зробити парсінг для "10.2abc" кожним з методів у консолі та ви зрозумієте різницю краще.</div> + +<h2 id="Строки_Strings">Строки (Strings)</h2> + +<p>Строки в JavaScript є послідовністю знаків - <a href="/uk/docs/Web/JavaScript/Guide/Values,_variables,_and_literals#Unicode">Unicode characters</a>. Це радісна новина для усіх, хто мав справи з інтернаціоналізацією ( internationalization). Якщо бути точнішими, це послідовність одиниць кодування UTF-16; кожна така одиниця представлена 16-бітовим числом. Кожний Unicode знак представлен однією чи двома одиницями коду.</p> + +<p>Якщо бажаєте представити строку із одного символа-знака, ви просто використовуєте строку, що складається з одного символа.</p> + +<p>Що дізнатись довжину строки (в одиницях коду), зверніться до його <code><a href="/uk/docs/Web/JavaScript/Reference/Global_Objects/String/length">length</a></code> властивості:</p> + +<pre class="brush: js notranslate">'hello'.length; // 5 +</pre> + +<p>Ось наша перша зустріч з об'єктами JavaScript! Ви помітили, що ви можете використовувати строку як {{jsxref("Object", "objects", "", 1)}} також? У строк є {{jsxref("String", "methods", "#Methods", 1)}} які дозволяють Вам керувати строками та мати доступ до інформації про строку:</p> + +<pre class="brush: js notranslate">'hello'.charAt(0); // "h" +'hello, world'.replace('hello', 'goodbye'); // "goodbye, world" +'hello'.toUpperCase(); // "HELLO" +</pre> + +<h2 id="Інші_типи">Інші типи</h2> + +<p>JavaScript розрізняє {{jsxref("null")}}, якие є значенням, що показує свідому відсутність значення (та доступне лише через ключеве слово <code>null</code>), та {{jsxref("undefined")}}, яке є значенням типу <code>undefined</code> та вказує на неініціалізоване значення, яке ще не отримало присвоєння значення. Ми поговоримо про змінні пізніше, а зараз зазначимо, що в JavaScript можлива об'ява змінної без присвоєння значення. Якщо ви зробите це, тип змінної буде <code>undefined</code>. Взагалі, <code>undefined</code> це константа.</p> + +<p>В JavaScript є булевий тип, з можливими значеннями<code> true</code> та <code>false</code> (обидва є ключовими словами) Будь-яке значення може бути перетворено в булевий тип, згідно наступних правил:</p> + +<ol> + <li><code>false</code>, <code>0</code>, пуста строка (<code>""</code>), <code>NaN</code>, <code>null</code>, <code>та</code><code>undefined</code> стають <code>false.</code></li> + <li>все інше - <code>true.</code></li> +</ol> + +<p>Ви можете зробити цю конвертацію безпосередньо за допомогою <code>Boolean()</code> функції:</p> + +<pre class="brush: js notranslate">Boolean(''); // false +Boolean(234); // true +</pre> + +<p>Однак, це майже не використовується, тому що JavaScript <span class="tlid-translation translation" lang="uk"><span title="">буде мовчки виконувати це перетворення</span></span> коли очікується булеве значення, як в <code>if</code> твердженні (дивися нижче). Тому ми інколи говоримо просто "вірне значення" та "хибне значення", маючи на увазі значення, що стає <code>true</code> та <code>false</code> відповідно, при перетворенні на булеве. <span class="tlid-translation translation" lang="uk"><span title="">Як альтернативу, такі значення можна назвати відповідно "правдоподібними" та "хибними".</span></span></p> + +<p>Підтримуються такі булеві операції як <code>&&</code> (<em>логіч</em><em>ний ТА</em>), <code>||</code> (<em>логічній АБО</em>), <code>та</code><code>!</code> (<em>логічний НІ</em>); дивися про це нижче.</p> + +<h2 id="Змінні">Змінні</h2> + +<p>Нові змінні в JavaScript оголошуються за допомогою одного з трьох ключових слів: <code><a href="/uk/docs/Web/JavaScript/Reference/Statements/let">let</a></code>, <code><a href="/uk/docs/Web/JavaScript/Reference/Statements/const">const</a></code>, або <code><a href="/uk/docs/Web/JavaScript/Reference/Statements/var" title="/en/JavaScript/Reference/Statements/var">var</a>. </code><br> + <br> + <strong><code>let</code> </strong>дозволяє оголосити змінну рівня блоку. Оголошена таким чином змінна доступна на рівні функціонального блоку, до якого вона <em>входить</em>. </p> + +<pre class="brush: js notranslate">let a; +let name = 'Simon'; +</pre> + +<p>Наступний приклад демонструє область осяжності (scope) змінної, що оголошена за допомогою <code><strong>let</strong></code>:</p> + +<pre class="brush: js notranslate">// myLetVariable is *not* visible out here + +for (let myLetVariable = 0; myLetVariable < 5; myLetVariable++) { + // myLetVariable is only visible in here +} + +// myLetVariable is *not* visible out here + +</pre> + +<p><code><strong>const</strong></code><strong> </strong>дозволяє оголосити змінні, чиї значення не збираються бути змінені. Такі змінні доступні з функційного блоку, де були оголошені.</p> + +<pre class="brush: js notranslate">const Pi = 3.14; // змінна Pi встановлена +Pi = 1; // викине помилку (error), тому що ви не можете змінювати постійні змінні (сonstant variable).</pre> + +<p><br> + <code><strong>var</strong></code> є самим загальним ключовим словом оголошень змінних. Він не має обмежень, що мають попередні два ключових слова. Це тому що це був єдиний спосіб оголошувати змінні в JavaScript. Змінна, що оголошена за допомогою <strong><code>var</code> </strong>доступна із функційного блоку, де була оголошена.</p> + +<pre class="brush: js notranslate">var a; +var name = 'Simon';</pre> + +<p>Наступний приклад демонструє область осяжності (scope) змінної, що оголошена за допомогою <strong><code>var</code>:</strong></p> + +<pre class="brush: js notranslate">// myVarVariable *є* видимою звідси + +for (var myVarVariable = 0; myVarVariable < 5; myVarVariable++) { + // myVarVariable є видимою для усієї функції +} + +// myVarVariable *є* видимою звідси +</pre> + +<p>Якщо ви оголосити змінну без присвоєння їй значення, її тип буде <code>undefined</code>.</p> + +<p>Важлива різниця між JavaScript та іншими мовами, такими як Java це те, що в JavaScript, блоки не мають області осяжності (scope); тільки функції мають область осяжності. Тому, якщо змінна визначена у складі виразу за допомогою <code>var</code> (наприклад, всередені структури контроля <code>if</code>), вона буде видимою для зовнішньої функції. Однак, починаючи з ECMAScript 2015, <code><a href="/uk/docs/Web/JavaScript/Reference/Statements/let">let</a></code> <code>та</code><code><a href="/uk/docs/Web/JavaScript/Reference/Statements/const">const</a></code> оголошення дозволяють створювати блочно-обмежені змінні.</p> + +<h2 id="Оператори">Оператори</h2> + +<p>Числові оператори JavaScript це +, <code>-</code>, <code>*</code>, <code>/</code> <code>та</code><code>%</code> що є оператором решти (<a href="/uk/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Remainder_%28%29">що не слід плутати з модулем числа (modulo)</a>.) Значення присвоюються за допомогою =, та є також вирази компоновочного присвоєння такі як <code>+=</code> and <code>-=</code>. Це розширення виразу <code>x = x <em>operator</em> y</code>.</p> + +<pre class="brush: js notranslate">x += 5; +x = x + 5; +</pre> + +<p>Ви можете використовувати <code>++</code> and <code>--</code> щоб збільшувати та зменьшувати відповідно. Вони можуть бути як в префіксному так і в постфіксному положенні відносно значень.</p> + +<p><a href="/uk/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Addition" title="/en/JavaScript/Reference/Operators/String_Operators"><code>+</code> оператор</a> також робить складання строк:</p> + +<pre class="brush: js notranslate">'hello' + ' world'; // "hello world" +</pre> + +<p>Якщо ви додаєте строку до числа (або інше значення) усе конвертуюеться насамперед в строку. Це може Вас здивувати:</p> + +<pre class="brush: js notranslate">'3' + 4 + 5; // "345" + 3 + 4 + '5'; // "75" +</pre> + +<p>Додавання пустої строки до якогось значення є зручним шляхом конвертування цього значення у строку.</p> + +<p><a href="/uk/docs/Web/JavaScript/Reference/Operators/Comparison_Operators" title="/en/JavaScript/Reference/Operators/Comparison_Operators">Порівняння</a> в JavaScript робляться за допомогою <code><</code>, <code>></code>, <code><=</code> and <code>>=</code>. Це працює як для строк, так і для чисел. Рівність менш прямолінійна. Оператор подвійне-рівно (double-equals) призводить до примусової конвертації (coercion), якщо значення порівняння різних типів, інколи - з дивним результатом:</p> + +<pre class="brush: js notranslate">123 == '123'; // true +1 == true; // true +</pre> + +<p>Щоб запобігти коерції, використовуйте потрійне-рівне:</p> + +<pre class="brush: js notranslate">123 === '123'; // false +1 === true; // false +</pre> + +<p>Є також оператори <code>!=</code> <code>та</code><code>!==</code>.</p> + +<p>JavaScript також має <a href="/uk/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators" title="/en/JavaScript/Reference/Operators/Bitwise_Operators">побітові операції</a>. Якщо хочете їх використовувати - вони до Ваших послуг.</p> + +<h2 id="Control_structures">Control structures</h2> + +<p>JavaScript має схожий набір структур контроля з іншими мовами сімейства C. Ствердження умов підтримуються за допомогою <code>if</code> та <code>else</code>; Можете організовувати їх у ланцюги за бажанням:</p> + +<pre class="brush: js notranslate">var name = 'kittens'; +if (name == 'puppies') { + name += ' woof'; +} else if (name == 'kittens') { + name += ' meow'; +} else { + name += '!'; +} +name == 'kittens meow'; +</pre> + +<p>JavaScript має <code>while</code> цикли та <code>do-while</code> цикли. Перший добре підходить для загального цикла; другий для циклів, де Ви бажаєте бути впевненими що тіло циклу буде виконано хоча б раз:</p> + +<pre class="brush: js notranslate">while (true) { + // an infinite loop! +} + +var input; +do { + input = get_input(); +} while (inputIsNotValid(input)); +</pre> + +<p> <code>for</code> цикл в JavaScript це теж саме, що і в С та Java мовах: це дозволяє впровадити усю інформацію про цикл в єдиній лінії коду.</p> + +<pre class="brush: js notranslate">for (var i = 0; i < 5; i++) { + // Буде виконано 5 разів +} +</pre> + +<p>JavaScript також має два інших відомих варіанта циклу: <a href="/uk/docs/Web/JavaScript/Reference/Statements/for...of">for...of</a></p> + +<pre class="brush: js notranslate">for (let value of array) { + // робиться щось з value +} +</pre> + +<p>та <a href="/uk/docs/Web/JavaScript/Reference/Statements/for...in">for...in</a>:</p> + +<pre class="brush: js notranslate">for (let property in object) { + // робиться щось з property object +} +</pre> + +<p>Оператори <code>&&</code> та <code>||</code> використовують логіку скороченого обчислення , це означає що чи буде обчислюватись другий операнд залежить від результату обчислення першого операнда. Дуже зручно перевіряти об'єкти на належність до null перед доступом до його атрибутів:</p> + +<pre class="brush: js notranslate">var name = o && o.getName(); +</pre> + +<p>Або для кешування значень (коли хибні значення є недійсними):</p> + +<pre class="brush: js notranslate">var name = cachedName || (cachedName = getName()); +</pre> + +<p>JavaScript має тернарний оператор для виразів умов:</p> + +<pre class="brush: js notranslate">var allowed = (age > 18) ? 'yes' : 'no'; +</pre> + +<p>Твердження <code>switch</code> може бути використано для багатогілкових умов на основі чисели чи строк:</p> + +<pre class="brush: js notranslate">switch (action) { + case 'draw': + drawIt(); + break; + case 'eat': + eatIt(); + break; + default: + doNothing(); +} +</pre> + +<p>Якщо не додати <code>break</code> твердження, виконання "провалиться" на наступний рівень. Це досить рідко може бути тим, чого ви хотіли — насправді<span class="tlid-translation translation" lang="uk"><span title=""> варто конкретно позначити навмисний "прорив" коментарем, якщо ви дійсно мали на увазі це для сприяння налагодженню</span></span> (debugging):</p> + +<pre class="brush: js notranslate">switch (a) { + case 1: // провал нижче + case 2: + eatIt(); + break; + default: + doNothing(); +} +</pre> + +<p>Завершення за допомогою default за бажанням. Ви можете мати вирази в обидвах частинах switch та cases якщо бажаєте; порівняння буде робитися за допомогою оператора <code>===</code>:</p> + +<pre class="brush: js notranslate">switch (1 + 3) { + case 2 + 2: + yay(); + break; + default: + neverhappens(); +} +</pre> + +<h2 id="Обєкти">Об'єкти</h2> + +<p>JavaScript об'єкти можна уявляти як прості колекції пар ім'я-значення (name-value pairs). Таким чином, вони схожі на:</p> + +<ul> + <li>Dictionaries в Python.</li> + <li>Hashes в Perl та Ruby.</li> + <li>Hash tables в C та C++.</li> + <li>HashMaps в Java.</li> + <li>Associative arrays в PHP.</li> +</ul> + +<p><span class="tlid-translation translation" lang="uk"><span title="">Те, що ця структура даних настільки широко використовується, є свідченням її універсальності</span></span> . Через те, що усе (основні типи даних) в JavaScript це об'єкт, <span class="tlid-translation translation" lang="uk"><span title="">будь-яка програма JavaScript, природно, передбачає велику кількість пошукових запитів хеш-таблиць.</span></span> Як добре, що запити такі швидкі!</p> + +<p> частина "ім'я" це строка JavaScript, а "значення" може бути будь яким з типів JavaScript — включно об'єктами. Це дозволяє Вам будувати структури даних необмеженої складності.</p> + +<p>Є два основних способи створити пустий об'єкт:</p> + +<pre class="brush: js notranslate">var obj = new Object(); +</pre> + +<p>Та:</p> + +<pre class="brush: js notranslate">var obj = {}; +</pre> + +<p>Це семантично одинаково; другий спосіб називається синтаксис буквального об'єкта (object literal syntax), та є більш зручним. Цей синтаксис також базовий для формату JSON та повинен бути бажаним завжди.</p> + +<p>Синтаксис буквального об'єкту може бути використаний щоб ініціалізувати об'єкт у всій повноті:</p> + +<pre class="brush: js notranslate">var obj = { + name: 'Carrot', + 'for': 'Max', + details: { + color: 'orange', + size: 12 + } +} +</pre> + +<p>Доступ до атрибутів може бути у вигляді ланцюга:</p> + +<pre class="brush: js notranslate">obj.details.color; // orange +obj['details']['size']; // 12 +</pre> + +<p><span class="tlid-translation translation" lang="uk"><span title="">Наступний приклад створює об'єкт-прототип, Person та екземпляр цього прототипу, You.</span></span></p> + +<pre class="brush: js notranslate">function Person(name, age) { + this.name = name; + this.age = age; +} + +// Define an object +var You = new Person('You', 24); +// We are creating a new person named "You" +// (that was the first parameter, and the age..) +</pre> + +<p><span class="tlid-translation translation" lang="uk"><span title="">Після створення, до властивостей об'єкта можна отримати доступ одним із двох способів:</span></span></p> + +<pre class="brush: js notranslate">obj.name = 'Simon'; +var name = obj.name; +</pre> + +<p>та...</p> + +<pre class="brush: js notranslate">obj['name'] = 'Simon'; +var name = obj['name']; +</pre> + +<p><span class="tlid-translation translation" lang="uk"><span title="">Вони також семантично рівнозначні.</span> <span title="">Другий метод має перевагу в тому, що ім'я властивості надається у вигляді рядка, а значить, його можна обчислити під час виконання.</span> <span title="">Однак використання цього методу запобігає застосуванню деяких оптимізацій двигуна JavaScript та мініфікаторів.</span> <span title="">Він також може бути використаний для встановлення та отримання властивостей із іменами, </span></span><a href="/uk/docs/Web/JavaScript/Reference/Lexical_grammar#Keywords" title="/en/JavaScript/Reference/Reserved_Words">зарезервованими словами</a>:</p> + +<pre class="brush: js notranslate">obj.for = 'Simon'; // Syntax error, тому що 'for' зарезервоване слово +obj['for'] = 'Simon'; // спрацьовує добре +</pre> + +<div class="note"> +<p>Починаючи з ECMAScript 5, зарезервовані слова можна використовувати <span class="tlid-translation translation" lang="uk"><span title="">як імена властивостей об'єкта "в буфі".</span> <span title="">Це означає, що при визначенні об’єктних літералів їх не потрібно "вбирати" в лапки.</span> <span title="">Див. Специфікацію ES5.</span></span></p> +</div> + +<p><span class="tlid-translation translation" lang="uk"><span title="">Детальніше про об'єкти та прототипи див. : </span></span><a href="/uk/docs/Web/JavaScript/Reference/Global_Objects/Object/prototype">Object.prototype</a><span class="tlid-translation translation" lang="uk"><span title="">.</span> <span title="">Пояснення прототипів об’єктів та ланцюгів прототипів об'єктів див .: </span></span><a href="https://developer.mozilla.org/uk/docs/Web/JavaScript/Inheritance_and_the_prototype_chain">Inheritance and the prototype chain</a>.</p> + +<h2 id="Масиви">Масиви</h2> + +<p>Arrays in JavaScript are actually a special type of object. They work very much like regular objects (numerical properties can naturally be accessed only using <code>[]</code> syntax) but they have one magic property called '<code>length</code>'. This is always one more than the highest index in the array.</p> + +<p>One way of creating arrays is as follows:</p> + +<pre class="brush: js notranslate">var a = new Array(); +a[0] = 'dog'; +a[1] = 'cat'; +a[2] = 'hen'; +a.length; // 3 +</pre> + +<p>A more convenient notation is to use an array literal:</p> + +<pre class="brush: js notranslate">var a = ['dog', 'cat', 'hen']; +a.length; // 3 +</pre> + +<p>Note that <code>array.length</code> isn't necessarily the number of items in the array. Consider the following:</p> + +<pre class="brush: js notranslate">var a = ['dog', 'cat', 'hen']; +a[100] = 'fox'; +a.length; // 101 +</pre> + +<p>Remember — the length of the array is one more than the highest index.</p> + +<p>If you query a non-existent array index, you'll get a value of <code>undefined</code> returned:</p> + +<pre class="brush: js notranslate">typeof a[90]; // undefined +</pre> + +<p>If you take the above into account, you can iterate over an array using the following:</p> + +<pre class="brush: js notranslate">for (var i = 0; i < a.length; i++) { + // Do something with a[i] +} +</pre> + +<p>You can iterate over an array using a <code><a href="/uk/docs/Web/JavaScript/Reference/Statements/for...in" title="/en/JavaScript/Reference/Statements/for...in">for...in</a></code> loop. Note that if someone added new properties to <code>Array.prototype</code>, they will also be iterated over by this loop. Therefore this method is "not" recommended.</p> + +<p>Another way of iterating over an array that was added with ECMAScript 5 is <code><a href="/uk/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach">forEach()</a></code>:</p> + +<pre class="brush: js notranslate" style="font-size: 14px;">['dog', 'cat', 'hen'].forEach(function(currentValue, index, array) { + // Do something with currentValue or array[index] +}); +</pre> + +<p>If you want to append an item to an array simply do it like this:</p> + +<pre class="brush: js notranslate">a.push(item);</pre> + +<p>Arrays come with a number of methods. See also the <a href="/uk/docs/Web/JavaScript/Reference/Global_Objects/Array">full documentation for array methods</a>.</p> + +<table> + <thead> + <tr> + <th scope="col">Method name</th> + <th scope="col">Description</th> + </tr> + </thead> + <tbody> + <tr> + <td><code>a.toString()</code></td> + <td>Returns a string with the <code>toString()</code> of each element separated by commas.</td> + </tr> + <tr> + <td><code>a.toLocaleString()</code></td> + <td>Returns a string with the <code>toLocaleString()</code> of each element separated by commas.</td> + </tr> + <tr> + <td><code>a.concat(item1[, item2[, ...[, itemN]]])</code></td> + <td>Returns a new array with the items added on to it.</td> + </tr> + <tr> + <td><code>a.join(sep)</code></td> + <td>Converts the array to a string — with values delimited by the <code>sep</code> param</td> + </tr> + <tr> + <td><code>a.pop()</code></td> + <td>Removes and returns the last item.</td> + </tr> + <tr> + <td><code>a.push(item1, ..., itemN)</code></td> + <td>Adds one or more items to the end.</td> + </tr> + <tr> + <td><code>a.reverse()</code></td> + <td>Reverses the array.</td> + </tr> + <tr> + <td><code>a.shift()</code></td> + <td>Removes and returns the first item.</td> + </tr> + <tr> + <td><code>a.slice(start[, end])</code></td> + <td>Returns a sub-array.</td> + </tr> + <tr> + <td><code>a.sort([cmpfn])</code></td> + <td>Takes an optional comparison function.</td> + </tr> + <tr> + <td><code>a.splice(start, delcount[, item1[, ...[, itemN]]])</code></td> + <td>Lets you modify an array by deleting a section and replacing it with more items.</td> + </tr> + <tr> + <td><code>a.unshift(item1[, item2[, ...[, itemN]]])</code></td> + <td>Prepends items to the start of the array.</td> + </tr> + </tbody> +</table> + +<h2 id="Функції">Функції</h2> + +<p>Along with objects, functions are the core component in understanding JavaScript. The most basic function couldn't be much simpler:</p> + +<pre class="brush: js notranslate">function add(x, y) { + var total = x + y; + return total; +} +</pre> + +<p>This demonstrates a basic function. A JavaScript function can take 0 or more named parameters. The function body can contain as many statements as you like, and can declare its own variables which are local to that function. The <code>return</code> statement can be used to return a value at any time, terminating the function. If no return statement is used (or an empty return with no value), JavaScript returns <code>undefined</code>.</p> + +<p>The named parameters turn out to be more like guidelines than anything else. You can call a function without passing the parameters it expects, in which case they will be set to <code>undefined</code>.</p> + +<pre class="brush: js notranslate">add(); // NaN +// You can't perform addition on undefined +</pre> + +<p>You can also pass in more arguments than the function is expecting:</p> + +<pre class="brush: js notranslate">add(2, 3, 4); // 5 +// added the first two; 4 was ignored +</pre> + +<p>That may seem a little silly, but functions have access to an additional variable inside their body called <a href="/uk/docs/Web/JavaScript/Reference/Functions/arguments" title="/en/JavaScript/Reference/Functions_and_function_scope/arguments"><code>arguments</code></a>, which is an array-like object holding all of the values passed to the function. Let's re-write the add function to take as many values as we want:</p> + +<pre class="brush: js notranslate">function add() { + var sum = 0; + for (var i = 0, j = arguments.length; i < j; i++) { + sum += arguments[i]; + } + return sum; +} + +add(2, 3, 4, 5); // 14 +</pre> + +<p>That's really not any more useful than writing <code>2 + 3 + 4 + 5</code> though. Let's create an averaging function:</p> + +<pre class="brush: js notranslate">function avg() { + var sum = 0; + for (var i = 0, j = arguments.length; i < j; i++) { + sum += arguments[i]; + } + return sum / arguments.length; +} + +avg(2, 3, 4, 5); // 3.5 +</pre> + +<div class="syntaxbox">This is pretty useful, but it does seem a little verbose. To diminish this code a bit more we can look at substituting the use of the arguments array through <a href="/uk/docs/Web/JavaScript/Reference/Operators/Spread_operator">Spread syntax</a>. In this way we can pass in any number of arguments into the function while keeping our code minimal. The <strong>spread operator</strong> is used in function declarations with the format: <strong>...[variable] </strong>and it will include within that variable the entire list of uncaptured arguments that the function was called with. We will also replace the <strong>for </strong>loop with a <strong>for...of</strong> loop to return the values within our variable.</div> + +<div class="syntaxbox"></div> + +<pre class="brush: js notranslate">function avg(...args) { + var sum = 0; + for (let value of args) { + sum += value; + } + return sum / args.length; +} + +avg(2, 3, 4, 5); // 3.5 +</pre> + +<div class="note"> +<div class="syntaxbox">In the above code the variable <strong>args</strong> holds all the values that were passed into the function. <br> +<br> +It is important to note that wherever the spread operator is placed in a function declaration it will store all arguments <em>after</em> its declaration, but not before.</div> + +<div class="syntaxbox"><em>a.e. function</em> <em>avg(</em><strong>firstValue, </strong><em>...args</em><strong><em>)</em> </strong>will store the first value passed into the function in the <strong>firstValue </strong>variable and the remaining arguments in <strong>args</strong></div> +</div> + +<div class="syntaxbox">Another useful function but it does lead us to a new problem. The <code>avg()</code> function takes a comma separated list of arguments — but what if you want to find the average of an array? You could just rewrite the function as follows:</div> + +<div class="syntaxbox"></div> + +<pre class="brush: js notranslate">function avgArray(arr) { + var sum = 0; + for (var i = 0, j = arr.length; i < j; i++) { + sum += arr[i]; + } + return sum / arr.length; +} + +avgArray([2, 3, 4, 5]); // 3.5 +</pre> + +<p>But it would be nice to be able to reuse the function that we've already created. Luckily, JavaScript lets you call a function and call it with an arbitrary array of arguments, using the {{jsxref("Function.apply", "apply()")}} method of any function object.</p> + +<pre class="brush: js notranslate">avg.apply(null, [2, 3, 4, 5]); // 3.5 +</pre> + +<p>The second argument to <code>apply()</code> is the array to use as arguments; the first will be discussed later on. This emphasizes the fact that functions are objects too.</p> + +<p>JavaScript lets you create anonymous functions.</p> + +<pre class="brush: js notranslate">var avg = function() { + var sum = 0; + for (var i = 0, j = arguments.length; i < j; i++) { + sum += arguments[i]; + } + return sum / arguments.length; +}; +</pre> + +<p>This is semantically equivalent to the <code>function avg()</code> form. It's extremely powerful, as it lets you put a full function definition anywhere that you would normally put an expression. This enables all sorts of clever tricks. Here's a way of "hiding" some local variables — like block scope in C:</p> + +<pre class="brush: js notranslate">var a = 1; +var b = 2; + +(function() { + var b = 3; + a += b; +})(); + +a; // 4 +b; // 2 +</pre> + +<p>JavaScript allows you to call functions recursively. This is particularly useful for dealing with tree structures, such as those found in the browser DOM.</p> + +<pre class="brush: js notranslate">function countChars(elm) { + if (elm.nodeType == 3) { // TEXT_NODE + return elm.nodeValue.length; + } + var count = 0; + for (var i = 0, child; child = elm.childNodes[i]; i++) { + count += countChars(child); + } + return count; +} +</pre> + +<p>This highlights a potential problem with anonymous functions: how do you call them recursively if they don't have a name? JavaScript lets you name function expressions for this. You can use named IIFEs (Immediately Invoked Function Expressions) as shown below:</p> + +<pre class="brush: js notranslate">var charsInBody = (function counter(elm) { + if (elm.nodeType == 3) { // TEXT_NODE + return elm.nodeValue.length; + } + var count = 0; + for (var i = 0, child; child = elm.childNodes[i]; i++) { + count += counter(child); + } + return count; +})(document.body); +</pre> + +<p>The name provided to a function expression as above is only available to the function's own scope. This allows more optimizations to be done by the engine and results in more readable code. The name also shows up in the debugger and some stack traces, which can save you time when debugging.</p> + +<p>Note that JavaScript functions are themselves objects — like everything else in JavaScript — and you can add or change properties on them just like we've seen earlier in the Objects section.</p> + +<h2 id="Custom_objects">Custom objects</h2> + +<div class="note">For a more detailed discussion of object-oriented programming in JavaScript, see <a href="/uk/docs/Web/JavaScript/Introduction_to_Object-Oriented_JavaScript" title="https://developer.mozilla.org/en/Introduction_to_Object-Oriented_JavaScript">Introduction to Object Oriented JavaScript</a>.</div> + +<p>In classic Object Oriented Programming, objects are collections of data and methods that operate on that data. JavaScript is a prototype-based language that contains no class statement, as you'd find in C++ or Java (this is sometimes confusing for programmers accustomed to languages with a class statement.) Instead, JavaScript uses functions as classes. Let's consider a person object with first and last name fields. There are two ways in which the name might be displayed: as "first last" or as "last, first". Using the functions and objects that we've discussed previously, we could display the data like this:</p> + +<pre class="example-bad brush: js notranslate">function makePerson(first, last) { + return { + first: first, + last: last + }; +} +function personFullName(person) { + return person.first + ' ' + person.last; +} +function personFullNameReversed(person) { + return person.last + ', ' + person.first; +} + +s = makePerson('Simon', 'Willison'); +personFullName(s); // "Simon Willison" +personFullNameReversed(s); // "Willison, Simon" +</pre> + +<p>This works, but it's pretty ugly. You end up with dozens of functions in your global namespace. What we really need is a way to attach a function to an object. Since functions are objects, this is easy:</p> + +<pre class="brush: js notranslate">function makePerson(first, last) { + return { + first: first, + last: last, + fullName: function() { + return this.first + ' ' + this.last; + }, + fullNameReversed: function() { + return this.last + ', ' + this.first; + } + }; +} + +s = makePerson('Simon', 'Willison'); +s.fullName(); // "Simon Willison" +s.fullNameReversed(); // "Willison, Simon" +</pre> + +<p>There's something here we haven't seen before: the <code><a href="/uk/docs/Web/JavaScript/Reference/Operators/this" title="/en/JavaScript/Reference/Operators/this">this</a></code> keyword. Used inside a function, <code>this</code> refers to the current object. What that actually means is specified by the way in which you called that function. If you called it using <a href="/uk/docs/Web/JavaScript/Reference/Operators/Object_initializer#Accessing_properties" title="/en/JavaScript/Reference/Operators/Member_Operators">dot notation or bracket notation</a> on an object, that object becomes <code>this</code>. If dot notation wasn't used for the call, <code>this</code> refers to the global object.</p> + +<p>Note that <code>this</code> is a frequent cause of mistakes. For example:</p> + +<pre class="brush: js notranslate">s = makePerson('Simon', 'Willison'); +var fullName = s.fullName; +fullName(); // undefined undefined +</pre> + +<p>When we call <code>fullName()</code> alone, without using <code>s.fullName()</code>, <code>this</code> is bound to the global object. Since there are no global variables called <code>first</code> or <code>last</code> we get <code>undefined</code> for each one.</p> + +<p>We can take advantage of the <code>this</code> keyword to improve our <code>makePerson</code> function:</p> + +<pre class="brush: js notranslate">function Person(first, last) { + this.first = first; + this.last = last; + this.fullName = function() { + return this.first + ' ' + this.last; + }; + this.fullNameReversed = function() { + return this.last + ', ' + this.first; + }; +} +var s = new Person('Simon', 'Willison'); +</pre> + +<p>We have introduced another keyword: <code><a href="/uk/docs/Web/JavaScript/Reference/Operators/new" title="/en/JavaScript/Reference/Operators/new">new</a></code>. <code>new</code> is strongly related to <code>this</code>. It creates a brand new empty object, and then calls the function specified, with <code>this</code> set to that new object. Notice though that the function specified with <code>this</code> does not return a value but merely modifies the <code>this</code> object. It's <code>new</code> that returns the <code>this</code> object to the calling site. Functions that are designed to be called by <code>new</code> are called constructor functions. Common practice is to capitalize these functions as a reminder to call them with <code>new</code>.</p> + +<p>The improved function still has the same pitfall with calling <code>fullName()</code> alone.</p> + +<p>Our person objects are getting better, but there are still some ugly edges to them. Every time we create a person object we are creating two brand new function objects within it — wouldn't it be better if this code was shared?</p> + +<pre class="brush: js notranslate">function personFullName() { + return this.first + ' ' + this.last; +} +function personFullNameReversed() { + return this.last + ', ' + this.first; +} +function Person(first, last) { + this.first = first; + this.last = last; + this.fullName = personFullName; + this.fullNameReversed = personFullNameReversed; +} +</pre> + +<p>That's better: we are creating the method functions only once, and assigning references to them inside the constructor. Can we do any better than that? The answer is yes:</p> + +<pre class="brush: js notranslate">function Person(first, last) { + this.first = first; + this.last = last; +} +Person.prototype.fullName = function() { + return this.first + ' ' + this.last; +}; +Person.prototype.fullNameReversed = function() { + return this.last + ', ' + this.first; +}; +</pre> + +<p><code>Person.prototype</code> is an object shared by all instances of <code>Person</code>. It forms part of a lookup chain (that has a special name, "prototype chain"): any time you attempt to access a property of <code>Person</code> that isn't set, JavaScript will check <code>Person.prototype</code> to see if that property exists there instead. As a result, anything assigned to <code>Person.prototype</code> becomes available to all instances of that constructor via the <code>this</code> object.</p> + +<p>This is an incredibly powerful tool. JavaScript lets you modify something's prototype at any time in your program, which means you can add extra methods to existing objects at runtime:</p> + +<pre class="brush: js notranslate">s = new Person('Simon', 'Willison'); +s.firstNameCaps(); // TypeError on line 1: s.firstNameCaps is not a function + +Person.prototype.firstNameCaps = function firstNameCaps() { + return this.first.toUpperCase(); +}; +s.firstNameCaps(); // "SIMON" +</pre> + +<p>Interestingly, you can also add things to the prototype of built-in JavaScript objects. Let's add a method to <code>String</code> that returns that string in reverse:</p> + +<pre class="brush: js notranslate">var s = 'Simon'; +s.reversed(); // TypeError on line 1: s.reversed is not a function + +String.prototype.reversed = function reversed() { + var r = ''; + for (var i = this.length - 1; i >= 0; i--) { + r += this[i]; + } + return r; +}; + +s.reversed(); // nomiS +</pre> + +<p>Our new method even works on string literals!</p> + +<pre class="brush: js notranslate">'This can now be reversed'.reversed(); // desrever eb won nac sihT +</pre> + +<p>As mentioned before, the prototype forms part of a chain. The root of that chain is <code>Object.prototype</code>, whose methods include <code>toString()</code> — it is this method that is called when you try to represent an object as a string. This is useful for debugging our <code>Person</code> objects:</p> + +<pre class="brush: js notranslate">var s = new Person('Simon', 'Willison'); +s; // [object Object] + +Person.prototype.toString = function() { + return '<Person: ' + this.fullName() + '>'; +} + +s.toString(); // "<Person: Simon Willison>" +</pre> + +<p>Remember how <code>avg.apply()</code> had a null first argument? We can revisit that now. The first argument to <code>apply()</code> is the object that should be treated as '<code>this</code>'. For example, here's a trivial implementation of <code>new</code>:</p> + +<pre class="brush: js notranslate">function trivialNew(constructor, ...args) { + var o = {}; // Create an object + constructor.apply(o, args); + return o; +} +</pre> + +<p>This isn't an exact replica of <code>new</code> as it doesn't set up the prototype chain (it would be difficult to illustrate). This is not something you use very often, but it's useful to know about. In this snippet, <code>...args</code> (including the ellipsis) is called the "<a href="/uk/docs/Web/JavaScript/Reference/Functions/rest_parameters">rest arguments</a>" — as the name implies, this contains the rest of the arguments.</p> + +<p>Calling</p> + +<pre class="brush: js notranslate">var bill = trivialNew(Person, 'William', 'Orange');</pre> + +<p>is therefore almost equivalent to</p> + +<pre class="brush: js notranslate">var bill = new Person('William', 'Orange');</pre> + +<p><code>apply()</code> has a sister function named <a href="/uk/docs/Web/JavaScript/Reference/Global_Objects/Function/call" title="/en/JavaScript/Reference/Global_Objects/Function/call"><code>call</code></a>, which again lets you set <code>this</code> but takes an expanded argument list as opposed to an array.</p> + +<pre class="brush: js notranslate">function lastNameCaps() { + return this.last.toUpperCase(); +} +var s = new Person('Simon', 'Willison'); +lastNameCaps.call(s); +// Is the same as: +s.lastNameCaps = lastNameCaps; +s.lastNameCaps(); +</pre> + +<h3 id="Inner_functions">Inner functions</h3> + +<p>JavaScript function declarations are allowed inside other functions. We've seen this once before, with an earlier <code>makePerson()</code> function. An important detail of nested functions in JavaScript is that they can access variables in their parent function's scope:</p> + +<pre class="brush: js notranslate">function betterExampleNeeded() { + var a = 1; + function oneMoreThanA() { + return a + 1; + } + return oneMoreThanA(); +} +</pre> + +<p>This provides a great deal of utility in writing more maintainable code. If a function relies on one or two other functions that are not useful to any other part of your code, you can nest those utility functions inside the function that will be called from elsewhere. This keeps the number of functions that are in the global scope down, which is always a good thing.</p> + +<p>This is also a great counter to the lure of global variables. When writing complex code it is often tempting to use global variables to share values between multiple functions — which leads to code that is hard to maintain. Nested functions can share variables in their parent, so you can use that mechanism to couple functions together when it makes sense without polluting your global namespace — "local globals" if you like. This technique should be used with caution, but it's a useful ability to have.</p> + +<h2 id="Замикання">Замикання</h2> + +<p>This leads us to one of the most powerful abstractions that JavaScript has to offer — but also the most potentially confusing. What does this do?</p> + +<pre class="brush: js notranslate">function makeAdder(a) { + return function(b) { + return a + b; + }; +} +var x = makeAdder(5); +var y = makeAdder(20); +x(6); // ? +y(7); // ? +</pre> + +<p>The name of the <code>makeAdder()</code> function should give it away: it creates new 'adder' functions, each of which when called with one argument adds it to the argument that it was created with.</p> + +<p>What's happening here is pretty much the same as was happening with the inner functions earlier on: a function defined inside another function has access to the outer function's variables. The only difference here is that the outer function has returned, and hence common sense would seem to dictate that its local variables no longer exist. But they <em>do</em> still exist — otherwise the adder functions would be unable to work. What's more, there are two different "copies" of <code>makeAdder()</code>'s local variables — one in which <code>a</code> is 5 and one in which <code>a</code> is 20. So the result of those function calls is as follows:</p> + +<pre class="brush: js notranslate">x(6); // returns 11 +y(7); // returns 27 +</pre> + +<p>Here's what's actually happening. Whenever JavaScript executes a function, a 'scope' object is created to hold the local variables created within that function. It is initialized with any variables passed in as function parameters. This is similar to the global object that all global variables and functions live in, but with a couple of important differences: firstly, a brand new scope object is created every time a function starts executing, and secondly, unlike the global object (which is accessible as <code>this</code> and in browsers as <code>window</code>) these scope objects cannot be directly accessed from your JavaScript code. There is no mechanism for iterating over the properties of the current scope object, for example.</p> + +<p>So when <code>makeAdder()</code> is called, a scope object is created with one property: <code>a</code>, which is the argument passed to the <code>makeAdder()</code> function. <code>makeAdder()</code> then returns a newly created function. Normally JavaScript's garbage collector would clean up the scope object created for <code>makeAdder()</code> at this point, but the returned function maintains a reference back to that scope object. As a result, the scope object will not be garbage collected until there are no more references to the function object that <code>makeAdder()</code> returned.</p> + +<p>Scope objects form a chain called the scope chain, similar to the prototype chain used by JavaScript's object system.</p> + +<p>A <strong>closure</strong> is the combination of a function and the scope object in which it was created. Closures let you save state — as such, they can often be used in place of objects. You can find <a href="http://stackoverflow.com/questions/111102/how-do-javascript-closures-work">several excellent introductions to closures</a>.</p> + +<div class="jfk-bubble gtx-bubble"> +<div class="jfk-bubble-content-id" id="bubble-18"> +<div id="gtx-host" style="max-width: 400px;"></div> +</div> + +<div class="jfk-bubble-closebtn-id jfk-bubble-closebtn"></div> + +<div class="jfk-bubble-arrow-id jfk-bubble-arrow jfk-bubble-arrowup" style="left: 313.5px;"> +<div class="jfk-bubble-arrowimplbefore"></div> + +<div class="jfk-bubble-arrowimplafter"></div> +</div> +</div> |