diff options
author | Peter Bengtsson <mail@peterbe.com> | 2020-12-08 14:42:52 -0500 |
---|---|---|
committer | Peter Bengtsson <mail@peterbe.com> | 2020-12-08 14:42:52 -0500 |
commit | 074785cea106179cb3305637055ab0a009ca74f2 (patch) | |
tree | e6ae371cccd642aa2b67f39752a2cdf1fd4eb040 /files/ru/web/javascript/a_re-introduction_to_javascript | |
parent | da78a9e329e272dedb2400b79a3bdeebff387d47 (diff) | |
download | translated-content-074785cea106179cb3305637055ab0a009ca74f2.tar.gz translated-content-074785cea106179cb3305637055ab0a009ca74f2.tar.bz2 translated-content-074785cea106179cb3305637055ab0a009ca74f2.zip |
initial commit
Diffstat (limited to 'files/ru/web/javascript/a_re-introduction_to_javascript')
-rw-r--r-- | files/ru/web/javascript/a_re-introduction_to_javascript/index.html | 900 |
1 files changed, 900 insertions, 0 deletions
diff --git a/files/ru/web/javascript/a_re-introduction_to_javascript/index.html b/files/ru/web/javascript/a_re-introduction_to_javascript/index.html new file mode 100644 index 0000000000..f2c82c85a0 --- /dev/null +++ b/files/ru/web/javascript/a_re-introduction_to_javascript/index.html @@ -0,0 +1,900 @@ +--- +title: Повторное введение в JavaScript (JS учебник) +slug: Web/JavaScript/A_re-introduction_to_JavaScript +translation_of: Web/JavaScript/A_re-introduction_to_JavaScript +--- +<div>{{jsSidebar}}</div> + +<p>Почему повторное введение? Потому что {{Glossary("JavaScript")}} известен тем, что является <a href="http://javascript.crockford.com/javascript.html">самым неправильно понятым языком программирования в мире</a>. Его часто называют игрушкой, но под слоем обманчивой простоты ожидают мощные языковые возможности. В настоящее время JavaScript используется невероятным количеством высококлассных приложений, показывая, что углублённое знание этой технологии является важным навыком для любого веб или мобильного разработчика.</p> + +<p>Было бы полезно начать с истории языка. JavaScript был создан в 1995 Бренданом Айком, инженером в компании Netscape. Первый релиз состоялся вместе с выходом браузера Netscape 2 в начале 1996 года. Сначала язык назывался LiveScript, но затем был переименован в связи с маркетинговыми целями, чтобы сыграть на популярности языка Java компании Sun Microsystem — несмотря на это языки практически не имеют ничего общего друг с другом. Так было положено начало путаницы между этими языками.</p> + +<p>Чуть позже Microsoft выпустила очень похожий и практически совместимый язык JScript, который шёл вместе с IE3. Через пару месяцев Netscape отправил язык в <a href="http://www.ecma-international.org/">Ecma International</a>, Европейскую организацию занимающуюся стандартами, которая выпустила первую версию стандарта <a href="https://developer.mozilla.org/en/JavaScript/Language_Resources" title="en/ECMAScript">ECMAScript</a> в 1997. Стандарт получил значимое обновление в <a href="http://www.ecma-international.org/publications/standards/Ecma-262.htm">ECMAScript edition 3</a> в 1999, и остается самым стабильным до сегодняшнего дня. Четвертая версия была отклонена, из-за проблем с усложнениями в языке. Многие вещи из четвертого издания послужили основой для стандарта ECMAScript 5 (декабрь 2009) и ECMAScript 6 (июнь 2015).</p> + +<div class="note"> +<p><strong>На заметку</strong>: Далее по тексту мы будем называть язык ECMAScript как "JavaScript".</p> +</div> + +<p>В отличие от большинства языков, JavaScript не следует концепции ввода (input) и вывода (output). Он спроектирован таким образом, чтобы запускаться как язык сценариев, встроенный в среду исполнения. Самая популярная среда исполнения это браузер, однако интерпретаторы JavaScript присутствуют и в Adobe Acrobat, Photoshop, 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 является отсутствие классов, вместо этого функциональность классов осуществляется прототипами объектов (смотрите ES6 {{jsxref("Classes")}}) . Другое главное отличие в том, что функции это объекты, в которых содержится исполняемый код и которые могут быть переданы куда-либо, как и любой другой объект.</p> + +<p>Начнём с основы любого языка: с типов данных. Программы на JavaScript оперируют значениями, и все эти значения принадлежат к определенному типу. Типы данных в JavaScript:</p> + +<ul> + <li><a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Number" title="en/Core_JavaScript_1.5_Reference/Global_Objects/Number">Числа</a></li> + <li><a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/String" title="en/Core_JavaScript_1.5_Reference/Global_Objects/String">Строки</a></li> + <li><a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Boolean" title="en/Core_JavaScript_1.5_Reference/Global_Objects/Boolean">Логические типы</a></li> + <li><a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Function" title="en/Core_JavaScript_1.5_Reference/Global_Objects/Function">Функции</a></li> + <li><a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Object" title="en/Core_JavaScript_1.5_Reference/Global_Objects/Object">Объекты</a></li> + <li><a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Symbol">Символы</a> (новый тип из шестой редакции)</li> +</ul> + +<p>Да, еще <a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/undefined">Undefined</a> и <a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/null">Null</a>, которые немного обособлены. И <a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Array" title="en/Core_JavaScript_1.5_Reference/Global_Objects/Array">Массивы</a>, которые являются особым видом объектов. А также <a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Date" title="en/Core_JavaScript_1.5_Reference/Global_Objects/Date">Даты</a> и <a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/RegExp" title="en/Core_JavaScript_1.5_Reference/Global_Objects/RegExp">Регулярные выражения</a>, тоже являющиеся объектами. И, если быть технически точным, функции это тоже особый вид объекта. Поэтому схема типов выглядит скорее так:</p> + +<ul> + <li><a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Number">Числа</a></li> + <li><a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/String">Строки</a></li> + <li><a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Boolean">Логические типы</a></li> + <li><a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Symbol">Символы</a> (новый тип из шестой редакции)</li> + <li><a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Object">Объекты</a> + <ul> + <li><a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Function">Функции</a></li> + <li><a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Array">Массивы</a></li> + <li><a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Date">Даты</a></li> + <li><a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/RegExp">Регулярные выражения</a></li> + </ul> + </li> + <li><a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/null">Null</a></li> + <li><a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/undefined">undefined</a></li> +</ul> + +<p>Также есть несколько встроенных типов <a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Error" title="en/Core_JavaScript_1.5_Reference/Global_Objects/Error">Ошибок</a>. Чтобы было проще, рассмотрим подробнее первую схему.</p> + +<h2 id="Числа">Числа</h2> + +<p>Числа в JavaScript — это "64-битные значения двойной точности формата IEEE 754", согласно спецификации. Это имеет интересные последствия. В JavaScript нет такой вещи, как целое число, поэтому с арифметикой нужно быть начеку, если вы привыкли к вычислениям в языках C или Java. Взгляните на пример:</p> + +<pre class="brush: js notranslate">0.1 + 0.2 == 0.30000000000000004 +</pre> + +<p>На практике целые значения это 32-битные целые (и хранятся таким образом в некоторых браузерных реализациях), что может быть важно для побитовых операций.</p> + +<p>Поддерживаются стандартные <a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators" title="en/Core_JavaScript_1.5_Reference/Operators/Arithmetic_Operators">арифметические операторы</a>, включая сложение, вычитание, остаток от деления и т.д. Есть ещё встроенный объект, который я забыл упомянуть, называемый <a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Math" title="en/Core_JavaScript_1.5_Reference/Global_Objects/Math">Math</a>, который содержит более продвинутые математические функции и константы:</p> + +<pre class="brush: js notranslate">Math.sin(3.5); +var circumference = Math.PI * (r + r); +</pre> + +<p>Вы можете преобразовать строку в целое число, используя встроенную функцию <code><a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/parseInt" title="en/Core_JavaScript_1.5_Reference/Global_Functions/parseInt">parseInt()</a></code>. Её необязательный второй параметр — основание системы счисления, которое следует всегда явно указывать:</p> + +<pre class="brush: js notranslate">parseInt("123", 10); // 123 +parseInt("010", 10); // 10 +</pre> + +<p>Если вы не предоставите основание, то можете получить неожиданные результаты:</p> + +<pre class="brush: js notranslate">parseInt("010"); // 8 +parseInt("0x10"); // 16 +</pre> + +<p>Это случилось потому, что функция <code><a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/parseInt">parseInt()</a></code> расценила строку как восьмеричную из-за начального 0, а шестнадцатеричную - из-за начального "0x".</p> + +<p>Если хотите преобразовать двоичное число в десятичное целое, просто смените основание:</p> + +<pre class="brush: js notranslate">parseInt("11", 2); // 3 +</pre> + +<p>Вы можете аналогично парсить дробные числа, используя встроенную функцию <code><a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/parseFloat" title="en/JavaScript/Reference/Global Objects/parseFloat">parseFloat()</a></code>, которая использует всегда основание 10 в отличие от родственной <a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/parseInt" title="en/JavaScript/Reference/Global Objects/parseInt"><code>parseInt()</code></a>.</p> + +<p>Также можно использовать унарный оператор <strong><code>+</code></strong> для преобразования значения в число:</p> + +<pre class="brush: js notranslate">+ "42"; // 42 ++ "0x10"; // 16 +</pre> + +<p>Специальное значение <a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/NaN">NaN</a> (сокращение от "Not a Number") возвращается, если строка не является числом:</p> + +<pre class="brush: js notranslate">parseInt("hello", 10); // NaN +</pre> + +<p><code>NaN</code> "заразителен": любая математическая операция над <code>NaN</code> возвращает <code>NaN</code>:</p> + +<pre class="brush: js notranslate">NaN + 5; // NaN +</pre> + +<p>Проверить значение на <code>NaN</code> можно встроенной функцией <code><a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/isNaN" title="en/Core_JavaScript_1.5_Reference/Global_Functions/isNaN">isNaN()</a></code>:</p> + +<pre class="brush: js notranslate">isNaN(NaN); // true +</pre> + +<p>JavaScript также имеет специальные значения <code><a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Infinity" title="en/Core_JavaScript_1.5_Reference/Global_Properties/Infinity">Infinity</a></code> (бесконечность) и<code> -Infinity</code>:</p> + +<pre class="brush: js notranslate">1 / 0; // Infinity +-1 / 0; // -Infinity +</pre> + +<p>Проверить значение на <code>Infinity</code>, <code>-Infinity</code> и <code>NaN</code> можно с помощью встроенной функции <code><a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/isFinite" title="en/Core_JavaScript_1.5_Reference/Global_Functions/isFinite">isFinite()</a></code>:</p> + +<pre class="brush: js notranslate">isFinite(1/0); // false +isFinite(-Infinity); // false +isFinite(NaN); // false +</pre> + +<div class="note"><strong>Примечание:</strong> функции <a href="/ru/docs/Web/JavaScript/Reference/Global_Objects/parseInt" title="en/JavaScript/Reference/Global Objects/parseInt"><code>parseInt()</code></a> и <code><a href="/ru/docs/Web/JavaScript/Reference/Global_Objects/parseFloat" title="en/JavaScript/Reference/Global Objects/parseFloat">parseFloat()</a></code> обрабатывают строку до тех пор, пока не будет встречен символ, не являющийся корректным для заданного числового формата, затем эти функции возвращают число, полученное в результате обработки вплоть до этого символа. А оператор "+" просто возвращает <code>NaN</code>, если в строке есть хоть один некорректный символ. Попробуйте сами в консоли преобразовать строку "10.2abc" каждым из методов, и различие станет ясным.</div> + +<h2 id="Строки">Строки</h2> + +<p>Строки в JavaScript - это последовательности <a href="/ru/docs/Web/JavaScript/Guide/Grammar_and_types" title="en/Core_JavaScript_1.5_Guide/Unicode">символов Unicode</a> (в кодировке UTF-16). Для тех, кто имеет дело с интернационализацией, это должно стать хорошей новостью. Если быть более точным, то строка - это последовательность кодовых единиц, каждая из которых представлена 16-битовым числом, а каждый символ Unicode состоит из 1 или 2 кодовых единиц.</p> + +<p>Чтобы представить единственный символ, используйте строку, содержащую только этот символ.</p> + +<p>Чтобы выяснить длину строки (в кодовых единицах), используйте свойство <code><a href="/ru/docs/Web/JavaScript/Reference/Global_Objects/String/length" title="en/Core_JavaScript_1.5_Reference/Global_Objects/String/length">length</a></code>:</p> + +<pre class="brush: js notranslate">"hello".length; // 5 +</pre> + +<p>Это уже первый шаг для работы с объектами! Мы уже говорили, что и строки можно использовать как объекты? У них тоже есть <a href="/ru/docs/Web/JavaScript/Reference/Global_Objects/String" title="en/Core_JavaScript_1.5_Reference/Global_Objects/String#Methods">методы</a>:</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 дополнительно различает такие типы, как <code>null</code>, который указывает на преднамеренное отсутствующее значение, и <code>undefined</code>, указывающий на неинициализированное значение — то есть, значение, которое даже не было назначено. Мы поговорим о переменных позже, но в JavaScript можно объявить переменную без присвоения ей значения. В этом случае тип переменной будет <code>"undefined"</code>.</p> + +<p>Ещё в JavaScript есть логический (булевый) тип данных, который может принимать два возможных значения <span style="font-family: consolas,monaco,andale mono,monospace;">true</span> или <span style="font-family: consolas,monaco,andale mono,monospace;">false</span> (оба являются ключевыми словами). Любое значение может быть преобразовано в логическое значение в соответствии со следующими правилами:</p> + +<ol> + <li><code>false</code>, <code>0</code>, пустая строка (<code>""</code>), <code>NaN</code>, <code>null</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 может автоматически преобразовывать типы в тех случаях, когда ожидается булевое значение, например в операторе <code>if</code>. Из-за того, что любой тип данных может быть преобразован в булевое значение, иногда говорят, что данные "истинные" или "ложные".</p> + +<p>Для операций с логическими данными используются логические операторы: <code>&&</code> (логическое <em>И</em>), <code>||</code> (логическое <em>ИЛИ</em>), <code>!</code> (логическое <em>НЕ</em>).</p> + +<h2 id="Переменные">Переменные</h2> + +<p>Для объявления новых переменных в JavaScript используются ключевые слова <code><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let">let</a></code>, <code><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const">const</a></code> или <code><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var" title="/en/JavaScript/Reference/Statements/var">var</a></code>.</p> + +<pre class="brush: js notranslate">let a; +let name = "Simon"; +</pre> + +<p><strong><code>let</code> </strong>позволяет объявлять переменные, которые доступны только в <em><strong>блоке</strong>,</em> в котором они объявлены:</p> + +<pre class="brush: js notranslate">// myLetVariable недоступна здесь + +for (let myLetVariable = 0; myLetVariable < 5; myLetVariable++) { + // myLetVariable доступна только здесь +} + +// myLetVariable недоступна здесь</pre> + +<p><code><strong>const</strong></code> позволяет создавать переменные, чьи значения не предполагают изменений. Переменная доступна из <em><strong>блока</strong></em>, в котором она объявлена.</p> + +<pre class="brush: js notranslate">const Pi = 3.14; // в переменную Pi записано значение. +Pi = 1; // вызовет исключение, так как значение константы нельзя изменить.</pre> + +<p><code><strong>var</strong></code> наиболее общее средство объявления переменной. Оно не имеет ограничений, которые имеют два вышеописанных способа. Это потому, что это был изначально единственный способ объявления переменной в JavaScript. Переменная, объявленная с помощью <strong><code>var</code>, </strong>доступна в пределах <strong><em>функции</em></strong>, в которой она объявлена.</p> + +<pre class="brush: js notranslate">var a; +var name = 'Simon';</pre> + +<p>Пример кода с переменной, объявленной с помощью <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 является то, что блоки данных не имеют своей области видимости, она есть только у функций. Поэтому, если объявить переменную через <code>var</code> в блоке данных (например, внутри контролирующей структуры <code>if</code>), то она будет доступна всей функции. Следует отметить, что в новом стандарте ECMAScript Edition 6 появились инструкции <code><a href="/ru/docs/Web/JavaScript/Reference/Statements/let">let</a></code> и <code><a href="/ru/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="/ru/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Remainder_%28%29">не путать с модулем</a>). Значения присваиваются с помощью оператора <code>=</code>, или с помощью составных операторов <code>+=</code> и <code>-=</code>. Это сокращённая запись выражения <code><strong>x = x </strong><em>оператор</em><strong> y</strong></code>.</p> + +<pre class="brush: js notranslate">x += 5 +x = x + 5 +</pre> + +<p>Так же используются операторы инкремента (<code>++</code>) и декремента (<code>--</code>). Которые имеют префиксную и постфиксную форму записи.</p> + +<p><a href="/ru/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators" title="en/Core_JavaScript_1.5_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="/ru/docs/Web/JavaScript/Reference/Operators/Comparison_Operators" title="en/Core_JavaScript_1.5_Reference/Operators/Comparison_Operators">сравнения</a> в JavaScript используются следующие операторы: <code><</code>, <code>></code>, <code><=</code> и <code>>=</code>. Сравнивать можно не только числа, но и строки. Проверка на равенство немного сложнее. Для проверки используют двойной (<code>==</code>) или тройной (<code>===</code>) оператор присваивания. Двойной оператор <code>==</code> осуществляет автоматическое преобразование типов, что может приводить к интересным результатам:</p> + +<pre class="brush: js notranslate">123 == "123"; // true +1 == true; // true</pre> + +<p>Если преобразование нежелательно, то используют оператор строгого равенства:</p> + +<pre class="brush: js notranslate">1 === true; // false +123 === "123"; // false +true === true; // true</pre> + +<p>Для проверки на неравенство используют операторы <code>!=</code> и <code>!==</code>.</p> + +<p>Отдельного внимания стоят <a href="/ru/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators">побитовые операторы</a>, с которыми вы можете ознакомиться в соответствующем разделе.</p> + +<h2 id="Управляющие_структуры">Управляющие структуры</h2> + +<p>Управляющие структуры в JavaScript очень похожи на таковые в языках семейства C. Условные операторы выражены ключевыми словами <code>if</code> и <code>else</code>, которые можно составлять в цепочки:</p> + +<pre class="brush: js notranslate">var name = "kittens"; +if (name == "puppies") { + name += "!"; +} else if (name == "kittens") { + name += "!!"; +} else { + name = "!" + name; +} +name == "kittens!!" +</pre> + +<p>В JavaScript есть три типа циклов: <code>while</code>, <code>do-while</code> и <code>for</code>. While используется для задания обычного цикла, а do-while целесообразно применить в том случае, если вы хотите, чтобы цикл был выполнен хотя бы один раз:</p> + +<pre class="brush: js notranslate">while (true) { + // бесконечный цикл! +} + +var input; +do { + input = get_input(); +} while (inputIsNotValid(input)) +</pre> + +<p>Цикл <code>for</code> похож на такой же в языках C и Java: он позволяет задавать данные для контроля за выполнением цикла:</p> + +<pre class="brush: js notranslate">for (var i = 0; i < 5; i++) { + // Выполнится 5 раз +} +</pre> + +<p>JavaScript также содержит две других известных конструкции: <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of"><code>for</code>...<code>of</code></a></p> + +<pre class="brush: js notranslate">for (let value of array) { + // операции с value +}</pre> + +<p>и <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in"><code>for</code>...<code>in</code></a>:</p> + +<pre class="brush: js notranslate">for (let property in object) { + // операции над свойствами объекта +}</pre> + +<p>Логические операторы <code>&&</code> и <code>||</code> используют "короткий цикл вычисления", это значит, что вычисление каждого последующего оператора зависит от предыдущего. Например, полезно проверить существует ли объект или нет, прежде чем пытаться получить доступ к его свойствам:</p> + +<pre class="brush: js notranslate">var name = o && o.getName(); +</pre> + +<p>Таким способом удобно задавать значения по умолчанию:</p> + +<pre class="brush: js notranslate">var name = otherName || "default"; +</pre> + +<p>К условным операторам в JavaScript принадлежит также тернарный оператор "<code>?</code>" :</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>case</code> не добавить останавливающую инструкцию <code>break</code>, то выполнение перейдёт к следующей инструкции <code>case</code>. Как правило, такое поведение нежелательно, но если вдруг вы решили его использовать, настоятельно рекомендуем писать соответствующий комментарий для облегчения поиска ошибок:</p> + +<pre class="brush: js notranslate">switch(a) { + case 1: // fallthrough + case 2: + eatit(); + break; + default: + donothing(); +} +</pre> + +<p>Вариант <code>default</code> опциональный. Допускается использование выражений как в условии <code>switch</code>, так и в <code>cases</code>. При проверке на равенство используется оператор строгого равенства <code>===</code>:</p> + +<pre class="brush: js notranslate">switch(1 + 3) { + case 2 + 2: + yay(); + break; + default: + neverhappens(); +} +</pre> + +<h2 id="Объекты">Объекты</h2> + +<p>Объекты в JavaScript представляют собой коллекции пар имя-значение (ключ-значение). Они похожи на:</p> + +<ul> + <li>Словари в Python.</li> + <li>Хеши в Perl и Ruby.</li> + <li>Таблицы хешей в C и C++.</li> + <li>HashMaps в Java.</li> + <li>Ассоциативные массивы в PHP.</li> +</ul> + +<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>Обе эти записи делают одно и то же. Вторая запись называется литералом объекта и более удобная. Такой способ является основой формата 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>Эти два метода равнозначны. Первый метод используется, если мы точно знаем к какому методу нам нужно обратиться. Второй метод принимает в качестве имени свойства строку, и позволяет вычислять имя в процессе вычислений. Следует отметить, что последний метод мешает некоторым движкам и минимизаторам оптимизировать код. Если появится необходимость назначить в качестве имён свойств объекта <a href="/ru/docs/Web/JavaScript/Reference/Lexical_grammar#Keywords" title="en/Core_JavaScript_1.5_Reference/Reserved_Words">зарезервированные слова</a>, то данный метод тоже может пригодиться:</p> + +<pre class="brush: js notranslate">// Вызовет Syntax error, ведь 'for' это зарезервированное слово +obj.for = "Simon"; + +// А тут всё нормально +obj["for"] = "Simon"; + +</pre> + +<div class="note"> +<p><strong>На заметку:</strong> Стандарт EcmaScript 5 позволяет использовать зарезервированные слова в качестве имён свойств объекта без "оборачивания" их в кавычки. Подробнее в <a href="http://es5.github.io/#x7.6.1">спецификации ES5</a>.</p> +</div> + +<p>Больше информации об объектах и прототипах: <a href="/ru/docs/Web/JavaScript/Reference/Global_Objects/Object/prototype">Object.prototype</a>.</p> + +<p>Для получения информации по прототипам объектов и цепям прототипов объектов смотрите <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain">Inheritance and the prototype chain</a>.</p> + +<div class="blockIndicator note"> +<p>Начиная с ECMAScript 2015, ключи объектов могут быть определены переменными с использованием квадратных скобок при создании: <code>{[phoneType]: 12345}</code> допустимо вместо <code>var userPhone = {}; userPhone[phoneType] = 12345</code>.</p> +</div> + +<h2 id="Массивы">Массивы</h2> + +<p>Массивы в JavaScript всего лишь частный случай объектов. Работают они практически одинаково (если именем свойства является число, то доступ к нему можно получить только через вызов в скобках []), только у массивов есть одно удивительное свойство '<code>length</code>' (длина). Оно возвращает число, равное самому большому индексу массива + 1.</p> + +<p>Создать массив можно по старинке:</p> + +<pre class="brush: js notranslate">var a = new Array(); +a[0] = "dog"; +a[1] = "cat"; +a[2] = "hen"; +a.length; // 3 +</pre> + +<p>Но гораздо удобнее использовать литерал массива:</p> + +<pre class="brush: js notranslate">var a = ["dog", "cat", "hen"]; +a.length; // 3 +</pre> + +<p>Запомните, свойство <code>array.length</code> не обязательно будет показывать количество элементов в массиве. Посмотрите пример:</p> + +<pre class="brush: js notranslate">var a = ["dog", "cat", "hen"]; +a[100] = "fox"; +a.length; // 101 +</pre> + +<p>Запомните — длина массива это его самый большой индекс плюс один.</p> + +<p>Если попытаться получить доступ к несуществующему элементу массива, то получите <code>undefined</code>:</p> + +<pre class="brush: js notranslate">typeof a[90]; // undefined +</pre> + +<p>Для перебора элементов массива используйте такой способ:</p> + +<pre class="brush: js notranslate">for (var i = 0; i < a.length; i++) { + // Сделать что-нибудь с элементом a[i] +} +</pre> + +<p>ES2015 представляет более краткий <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of"><code>for</code>...<code>of</code></a> способ обхода по итерируемым объектам, в т.ч. массивам:</p> + +<pre class="notranslate"><code>for (const currentValue of a) { + // С</code>делать что-нибудь с<code> currentValue +}</code> +</pre> + +<p>Перебрать элементы массива также можно с помощью цикла <code><a href="/ru/docs/Web/JavaScript/Reference/Statements/for...in" title="en/Core_JavaScript_1.5_Reference/Statements/for...in">for...in</a></code>. Но, если вдруг будет изменено какое-либо свойство <code>Array.prototype</code>, то оно тоже будет участвовать в выборке. Не используйте данный метод.</p> + +<p>И самый новый способ перебора свойств массива был добавлен в ECMAScript 5 — это метод <a href="/ru/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach">forEach()</a>:</p> + +<pre class="brush: js notranslate">["dog", "cat", "hen"].forEach(function(currentValue, index, array) { +// Сделать что-нибудь с currentValue или array[index] +});</pre> + +<p>Для добавления данных в массив используйте метод <code>push()</code>:</p> + +<pre class="brush: js notranslate">a.push(item);</pre> + +<p>У массивов есть ещё множество полезных методов. С их полным списком вы можете ознакомиться <a href="/ru/docs/Web/JavaScript/Reference/Global_Objects/Array">по ссылке</a>.</p> + +<table style="height: 124px; width: 598px;"> + <thead> + <tr> + <th scope="col">Метод</th> + <th scope="col">Описание</th> + </tr> + </thead> + <tbody> + <tr> + <td><code>a.toString()</code></td> + <td>Возвращает строковое представление массива, где все элементы разделены запятыми.</td> + </tr> + <tr> + <td><code>a.toLocaleString()</code></td> + <td>Возвращает строковое представление массива в соответствии с выбранной локалью.</td> + </tr> + <tr> + <td><code>a.concat(item1[, item2[, ...[, itemN]]])</code></td> + <td>Возвращает новый массив с добавлением указанных элементов.</td> + </tr> + <tr> + <td><code>a.join(sep)</code></td> + <td>Преобразует массив в строку, где в качестве разделителя используется параметр <code>sep</code></td> + </tr> + <tr> + <td><code>a.pop()</code></td> + <td>Удаляет последний элемент массива и возвращает его.</td> + </tr> + <tr> + <td><code>a.push(item1, ..., itemN)</code></td> + <td>Добавляет один или более элементов в конец массива.</td> + </tr> + <tr> + <td><code>a.reverse()</code></td> + <td>Меняет порядок элементов массива на обратный.</td> + </tr> + <tr> + <td><code>a.shift()</code></td> + <td>Удаляет первый элемент массива и возвращает его.</td> + </tr> + <tr> + <td><code>a.slice(start[, end])</code></td> + <td>Возвращает новый массив.</td> + </tr> + <tr> + <td><code>a.sort([cmpfn])</code></td> + <td>Сортирует данные в массиве.</td> + </tr> + <tr> + <td><code>a.splice(start, delcount[, item1[, ...[, itemN]]])</code></td> + <td>Позволяет вырезать из массива его часть и добавлять на это место новые элементы.</td> + </tr> + <tr> + <td><code>a.unshift(item1[, item2[, ...[, itemN]]])</code></td> + <td>Добавляет элементы в начало массива.</td> + </tr> + </tbody> +</table> + +<h2 id="Функции">Функции</h2> + +<p>Наряду с объектами функции также являются ключевыми компонентами языка JavaScript. Базовые функции очень просты:</p> + +<pre class="brush: js notranslate">function add(x, y) { + var total = x + y; + return total; +} +</pre> + +<p>В этом примере показано практически всё, что нужно знать о функциях. Функции в JavaScript могут принимать ноль или более параметров. Тело функции может содержать любые выражения и определять свои собственные переменные, которые будут для этой функции локальными. Инструкция <code>return</code> используется для возврата значения и остановки выполнения функции. Если инструкции return в функции нет (или есть, но не указано возвращаемое значение), то JavaScript возвратит <code>undefined</code>.</p> + +<p>Можно вызвать функцию, вообще не передавая ей параметры. В таком случае будет считаться, что их значения равны <code>undefined</code>:</p> + +<pre class="brush: js notranslate">add(); // NaN +// Нельзя проводить сложение undefined и undefined</pre> + +<p>Можно передать больше аргументов, чем ожидает функция:</p> + +<pre class="brush: js notranslate">add(2, 3, 4); // 5 +// используются только первые два аргумента, "4" игнорируется</pre> + +<p>Это может показаться бессмысленным, но на самом деле функции могут получить доступ к "лишним" аргументам с помощью псевдомассива <a href="/ru/docs/Web/JavaScript/Reference/Functions/arguments" title="En/Core_JavaScript_1.5_Reference/Functions_and_function_scope/arguments"><code>arguments</code></a>, в нём содержатся значения всех аргументов, переданных функции. Давайте напишем функцию, которая принимает неограниченное количество аргументов:</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>Или создадим функцию для вычисления среднего значения:</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> + +<p>Это довольно полезно, но при этом кажется излишне подробным. Для уменьшения количества кода взглянем на замену использования массива аргументов способом <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters">Rest parameter syntax</a>. В этом случае мы можем передавать в функцию любое количество аргументов, сохраняя код минималистичным. <strong>Rest parameter operator</strong> используется в списке параметров функции в формате: <strong>...variable</strong> и включает в себя целый список аргументов, с которыми функция будет вызвана. Мы будем также использовать замену цикла <strong>for</strong> циклом <strong>for...of</strong> для получения значений, которые будет содержать наша переменная.</p> + +<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="blockIndicator note"> +<p>В вышенаписанном коде переменная <strong>args</strong> содержит все значения, которые были переданы в функцию.<br> + <br> + Важно отметить, что где бы ни был размещен <em>rest parameter operator</em> в объявлении функции, он будет содержать все аргументы <em>после</em> его объявления, не раньше. например: <em>function</em> <em>avg(</em><strong>firstValue, </strong><em>...args)</em><strong> </strong>будет хранить первое переданное значение в переменной <strong>firstValue </strong>и оставшиеся в <strong>args</strong>. Это еще одно полезное свойство языка, однако оно ведет нас к новой проблеме. <code>avg()</code> функция принимает список аргументов, разделенный запятыми. Но что если вы хотите найти среднее значение в массиве? Вы можете переписать функцию следующим образом:</p> +</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>На тот случай, если вы хотите использовать первый вариант функции, а не переписывать её заново, то в JavaScript есть возможность вызывать функцию с произвольным массивом аргументов. Для этого используется метод <code><a href="/ru/docs/Web/JavaScript/Reference/Global_Objects/Function/apply" title="en/Core_JavaScript_1.5_Reference/Global_Objects/Function/apply">apply()</a>:</code></p> + +<pre class="brush: js notranslate">avg.apply(null, [2, 3, 4, 5]); // 3.5 +</pre> + +<p>Вторым аргументом метода <code>apply()</code> передаётся массив, который будет передан функции в качестве аргументов. О первом аргументе мы поговорим позже. Наличие у функций методов также говорит о том, что на самом деле они являются объектами.</p> + +<div class="blockIndicator note"> +<p>Этот же результат можно получить, используя <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator">spread operator</a> в вызове функции.</p> + +<p>For instance: <code>avg(...numbers)</code></p> +</div> + +<p>В JavaScript можно создавать анонимные функции:</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>Данная запись семантически равнозначна записи <code>function</code> <code>avg()</code>. Это даёт возможность использовать разные интересные трюки. Вот посмотрите, как можно "спрятать" локальные переменные в функции:</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 есть возможность рекурсивного вызова функции. Это может оказаться полезным при работе с иерархическими (древовидными) структурами данных (например такие, которые встречаются при работе с <a href="/ru/DOM" title="en/DOM">DOM</a>).</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>Тут мы сталкиваемся с проблемой: как вызвать функцию рекурсивно, если у неё нет имени? Для этого в JavaScript есть именованные функциональные выражения <a href="https://developer.mozilla.org/en-US/docs/Glossary/IIFE">IIFEs (Immediately Invoked Function Expressions)</a>. Вот пример использования именованной самовызывающейся функции:</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>Имя функции в примере доступно только внутри самой функции. Это улучшает оптимизацию и читаемость кода.</p> + +<h2 id="Собственные_объекты">Собственные объекты</h2> + +<div class="note"><strong>На заметку:</strong> Для более подробной информации по объектно-ориентированному программированию в JavaScript смотрит <a href="/ru/Web/JavaScript/Introduction_to_Object-Oriented_JavaScript" title="https://developer.mozilla.org/en/Introduction_to_Object-Oriented_JavaScript">Введение в объектно-ориентированный JavaScript</a>.</div> + +<p>В классическом Объектно-Ориентированном Программировании (ООП) объекты — это коллекции данных и методов, которые этими данными оперируют. JavaScript - это язык, основанный на прототипах, и в его определении нет понятия классов, таких, как в языках C++ или Java. (Иногда это может запутать программистов, знакомых с языками, в которых есть классы.) Вместо классов JavaScript использует функции. Давайте представим объект с личными данными, содержащий поля с именем и фамилией. Есть два типа отображения имён: "Имя Фамилия" или "Фамилия, Имя". С помощью объектов и функций можно сделать следующее:</p> + +<div class="blockIndicator warning"> +<pre class="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> +</div> + +<p>Работает, но сам код никуда не годится. С таким подходом у вас будут десятки функций, засоряющих глобальный объект. Это можно исправить, прикрепив функцию к объекту. Это просто, ведь все функции и есть объекты:</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>А вот кое-что новенькое: ключевое слово '<code><a href="/ru/docs/Web/JavaScript/Reference/Operators/this" title="en/Core_JavaScript_1.5_Reference/Operators/Special_Operators/this_Operator">this</a></code>'. Когда '<code>this</code>' используется внутри функции, оно ссылается на текущий объект. Значение ключевого слова зависит от способа вызова функции. Если вызвать функцию с обращением к объекту через <a href="/ru/docs/Web/JavaScript/Reference/Operators/Property_Accessors" title="en/Core_JavaScript_1.5_Reference/Operators/Member_Operators">точку или квадратные скобки</a>, то '<code>this</code>' получится равным данному объекту. В ином случае '<code>this</code>' будет ссылаться на глобальный объект. Это часто приводит к ошибкам. Например:</p> + +<pre class="brush: js notranslate">s = makePerson("Simon", "Willison") +var fullName = s.fullName; +fullName(); // undefined undefined +</pre> + +<p>При вызове <code>fullName()</code>, '<code>this</code>' получает ссылку на глобальный объект. А так как в глобальном объекте не определены переменные <code>first</code> и <code>last</code>, то имеем два <code>undefined</code>.</p> + +<p>Используя особенность ключевого слова '<code>this</code>', можно улучшить код функции <code>makePerson</code>:</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>В примере мы использовали новое ключевое слово: '<code><a href="/ru/docs/Web/JavaScript/Reference/Operators/new" title="en/Core_JavaScript_1.5_Reference/Operators/Special_Operators/new_Operator">new</a></code>'. Оно тесно связано с '<a href="/ru/docs/Web/JavaScript/Reference/Operators/this">this</a>'. Данное ключевое слово создаёт новый пустой объект, а потом вызывает указанную функцию, а <code>this</code> получает ссылку на этот новый объект. Функции, которые предназначены для вызова с '<code>new</code>' называются конструкторами. Существует соглашение, согласно которому все функции-конструкторы записываются с заглавной буквы.</p> + +<p>Мы доработали наш код в предыдущем примере, но всё равно остался один неприятный момент с самостоятельным вызовом <code>fullName()</code>.</p> + +<p>Каждый раз, когда с помощью конструктора создаётся новый объект, мы заново создаём и две новые функции. Гораздо удобнее создать эти функции отдельно и дать доступ к ним конструктору:</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>Уже лучше: мы создали функции-методы только один раз, а при новом вызове функции-конструктора просто ссылаемся на них. Можно сделать ещё лучше? Конечно:</p> + +<pre class="brush: js notranslate">function Person(first, last) { + this.first = first; + this.last = last; +} +<code>Person.prototype.fullName = function fullName() {</code> + return this.first + ' ' + this.last; +} +<code>Person.prototype.fullNameReversed = function fullNameReversed() {</code> + return this.last + ', ' + this.first; +} +</pre> + +<p><code>Person.prototype</code> это объект, доступ к которому есть у всех экземпляров класса <code>Person</code>. Он создаёт особую цепочку прототипов. Каждый раз, когда вы пытаетесь получить доступ к несуществующему свойству объекта <code>Person</code>, JavaScript проверяет, существует ли свойство в <code>Person.prototype</code>. В результате все, что передано в <code>Person.prototype</code>, становится доступным и всем экземплярам этого конструктора через <code>this</code> объект.</p> + +<p>Это очень мощный инструмент. JavaScript позволяет изменять прототипы в любое время, это значит, что можно добавлять новые методы к существующим объектам во время выполнения программы:</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() { + return this.first.toUpperCase() +} +s.firstNameCaps(); // "SIMON" +</pre> + +<p>Занимательно то, что добавлять свойства в прототип можно и для встроенных объектов JavaScript. Давайте добавим новый метод <code>reversed </code>классу <code>String</code>, этот метод будет возвращать строку задом наперёд:</p> + +<pre class="brush: js notranslate">var s = "Simon"; +s.reversed()<code>; // TypeError on line 1: s.reversed is not a function</code> + +String.prototype.reversed = function <code>reversed</code>() { + var r = ""; + for (var i = this.length - 1; i >= 0; i--) { + r += this[i]; + } + return r; +} +s.reversed(); // "nomiS" +</pre> + +<p>Данный метод будет работать даже на литералах строки!</p> + +<pre class="brush: js notranslate">"This can now be reversed".reversed(); +// desrever eb won nac sihT +</pre> + +<p>Как уже упоминалось, prototype формирует часть цепочки. Конечным объектом этой цепочки прототипов является <code>Object.prototype</code>, методы которого включают и <code>toString()</code> — тот метод, который вызывается тогда, когда надо получить строковое отображение объекта. Вот что можно сделать с нашими объектами <code>Person</code>:</p> + +<pre class="brush: js notranslate">var s = new Person("Simon", "Willison"); +s.toString(); // [object Object] + +Person.prototype.toString = function() { + return '<Person: ' + this.fullName() + '>'; +} + +s.toString(); // "<Person: Simon Willison>"</pre> + +<p>Помните, мы вызывали <code>avg.apply()</code> с первым аргументом равным null? Теперь мы можем сделать так: первым аргументом, переданным методу <code>apply()</code> будет объект, который примет значение '<code>this</code>'. Вот к примеру упрощённая реализация '<code>new</code>':</p> + +<pre class="brush: js notranslate">function trivialNew(constructor, ...args) { + var o = {}; // Создаём новый объект + constructor.apply(o, args); + return o; +} +</pre> + +<p>Это не точная копия <code>new</code>, так как она не устанавливает цепочку прототипов (это сложно ). Метод <code><a href="/ru/docs/Web/JavaScript/Reference/Global_Objects/Function/apply">apply()</a></code> применяется не очень часто, но знать его важно. В примере выше, запись <code>...args</code> (включая многоточие) называется "<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters">rest arguments</a>"— она включает в себя все оставшиеся аргументы.</p> + +<p>Вызов</p> + +<pre class="brush: js notranslate">var bill = trivialNew(Person, 'William', 'Orange');</pre> + +<p>практически полностью эквивалентен этому:</p> + +<pre class="brush: js notranslate">var bill = new Person('William', 'Orange');</pre> + +<p>В JavaScript метод <code><a href="/ru/docs/Web/JavaScript/Reference/Global_Objects/Function/apply">apply()</a></code> имеет похожий метод <a href="/ru/docs/Web/JavaScript/Reference/Global_Objects/Function/call" title="en/Core_JavaScript_1.5_Reference/Global_Objects/Function/call"><code>call()</code></a>, который тоже позволяет устанавливать '<code>this</code>', но принимает список, а не массив аргументов.</p> + +<pre class="brush: js notranslate">function lastNameCaps() { + return this.last.toUpperCase(); +} +var s = new Person("Simon", "Willison"); +lastNameCaps.call(s); +// Аналогично записи: +s.lastNameCaps = lastNameCaps; +s.lastNameCaps(); <code>// WILLISON</code></pre> + +<h2 id="Вложенные_функции">Вложенные функции</h2> + +<p>Объявлять новые функции можно и внутри других функций. Мы использовали этот приём чуть выше, создавая функцию <code>makePerson()</code>. Главная особенность вложенных функций в том, что они получают доступ к переменным, объявленным в их функции-родителе:</p> + +<pre class="brush: js notranslate">function parentFunc() { + var a = 1; + + function nestedFunc() { + var b = 4; // parentFunc can't use this + return a + b; + } + return nestedFunc(); // 5 +}</pre> + +<p>Это очень полезное свойство, которое делает сопровождение кода более удобным. Если ваша функция в своей работе использует другие функции, которые больше нигде не используются, то можно просто вложить вспомогательные функции в основную. Это сократит количество функций в глобальном объекте, что довольно неплохо.</p> + +<p>Ещё это отличный способ сократить количество глобальных переменных. Так при написании кода у нас часто будет возникать искушение понасоздавать глобальных переменных, которые будут доступны разным функциям. Всё это усложняет код, делает его менее читаемым. Вложенные функции имеют доступ к переменным своей функции-родителя, и мы можем использовать это для группировки множества функций вместе (естественно в разумных пределах), что позволит держать наш глобальный объект в чистоте и порядке.</p> + +<h2 id="Замыкания_Closures"><a href="/ru/docs/Web/JavaScript/Closures">Замыкания</a> (Closures)</h2> + +<p>Мы подошли к одному из самых мощных и непонятных инструментов JavaScript. Давайте разберёмся.</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>Функция <code>makeAdder</code> создаёт новую функцию, которая прибавляет полученное значение к значению, которые было получено при создании функции.</p> + +<p>Такой же фокус мы наблюдали в предыдущем примере, когда внутренние функции получали доступ к переменным той функции, в которой были объявлены. Только в нашем примере основная функция возвращает вложенную. Поначалу может показаться, что локальные переменные при этом перестанут существовать. Но они продолжают существовать — иначе код попросту не сработал бы. Вдобавок ко всему у нас есть две разные "копии" функции <code>makeAdder</code>, присвоенные разным переменным (одна копия, в которой <code>а</code> - это 5, а во второй <code>а</code> - это 20). Вот что имеем в результате вызова:</p> + +<pre class="brush: js notranslate">x(6); // возвратит 11 +y(7); // возвратит 27 +</pre> + +<p>И вот что произошло: когда JavaScript выполняет функцию, создаётся объект 'scope', который содержит в себе все локальные переменные, объявленные внутри этой функции. Он инициализируется любым значением, переданным функции в качестве параметра. 'Scope' подобен глобальному объекту, который содержит все глобальные переменные и функции, кроме нескольких важных отличий: при каждом вызове функции создаётся новый объект 'scope' и, в отличие от глобального, к объекту 'scope' нельзя получить прямой доступ из вашего кода. И нет способа пройтись по свойствам данного объекта.</p> + +<p>Так что при вызове функции <code>makeAdder</code> создаётся новый объект 'scope' с единственным свойством: <code>a</code>, которому присваивается значение, переданное функции в качестве аргумента. Потом <code>makeAdder</code> возвращает новую анонимную функцию. В любом другом случае 'сборщик мусора' удалил бы объект scope, но возвращаемая функция ссылается на этот объект. В итоге объект scope не удаляется до тех пор, пока существует хотя бы одна ссылка на него.</p> + +<p>Все объекты scope соединяются в цепочку областей видимости, которая похожа на цепочку прототипов в объектной системе JavaScript'а.</p> + +<p><strong>Замыкание</strong> это связка из функции и объекта scope, созданного при её вызове. <a href="http://stackoverflow.com/questions/111102/how-do-javascript-closures-work">Подробнее о замыканиях здесь</a>.</p> |