diff --git a/doc/ru/array/constructor.md b/doc/ru/array/constructor.md index 13f58846..500e0252 100644 --- a/doc/ru/array/constructor.md +++ b/doc/ru/array/constructor.md @@ -9,17 +9,17 @@ new Array(3); // Результат: [] new Array('3') // Результат: ['3'] -В случае, когда в конструктор `Array` передаётся один аргумент и этот аргумент имеет тип `Number`, конструктор возвращает новый, *заполненный случайными значениями*, массив, имеющий длину равную значению переданного аргумента. Стоит заметить, что в этом случае будет установлено только свойство `length` нового массива, индексы массива фактически не будут проинициализированы. +В случае, когда в конструктор `Array` передаётся только один аргумент, и этот аргумент имеет тип `Number` — конструктор возвращает новый, *разреженный* (_прим. перев._ — *заполненный случайными значениями*) массив, имеющий длину, равную значению переданного аргумента. Стоит заметить, что в этом случае будет установлено лишь свойство `length` нового массива, а реальные индексы массива не будут инициализированы. var arr = new Array(3); arr[1]; // не определён, undefined 1 in arr; // false, индекс не был установлен -Поведение, которое позволяет изначально установить только размер массива, может пригодиться лишь в нескольких случаях, таких как повторение строк, за счёт чего избегается использование цикла `for`. +Возможность заранее установить размер массива может пригодиться лишь в некоторых случаях — например, для повторения строки — так вы сможете обойтись без использования цикла `for`. new Array(count + 1).join(stringToRepeat); ### Заключение -Использование конструктора `Array` нужно избегать, насколько это возможно. Литералы определённо предпочтительнее — это краткая запись и она имеет более понятный синтаксис, так что при этом даже улучшается читабельность кода. +Использования конструктора `Array` следует избегать. Литералы, безусловно, намного предпочтительнее — это краткая запись, её синтаксис «чище» — а это, в свою очередь, повышает читабельность кода. diff --git a/doc/ru/array/general.md b/doc/ru/array/general.md index a9012e9b..2eabc201 100644 --- a/doc/ru/array/general.md +++ b/doc/ru/array/general.md @@ -1,29 +1,29 @@ -##  Итерации по массивам и свойства +## Перебор массивов и свойств объектов -Несмотря на то, что массивы в JavaScript являются объектами, нет достаточных оснований для использования [цикла `for in`](#object.forinloop) для итерации по элементам массива. Фактически, существует несколько весомых причин **против** использования `for in` в массивах. +Несмотря на то, что массивы в JavaScript являются объектами, **нельзя найти** весомых аргументов использовать [цикл `for in`](#object.forinloop) для перебора элементов массива. Наоборот, есть множество убедительных причин **против** использования циклов `for in` при переборе массивов. -> **Замечание:** Массивы в JavaScript **не** являются *ассоциативными массивами*. Для связывания ключей и значений в JavaScript есть только [объекты](#object.general). И при том, что ассоциативные массивы **сохраняют** заданный порядок, объекты **не** делают этого. +> **Замечание:** Массивы в JavaScript **не** являются *ассоциативными массивами*. Для группировки ключей и значений по парам в JavaScript есть только [объекты](#object.general). При этом, ассоциативные массивы по определению **сохраняют** заданный порядок свойств, объекты JavaScript этого **не** делают. -Во время выполнения `for in` циклически перебираются все свойства объекта, находящиеся в цепочке прототипов. Единственный способ исключить ненужные свойства — использовать [`hasOwnProperty`](#object.hasownproperty), а это **в 20 раз** медленнее обычного цикла `for`. +Поскольку во время выполнения `for in` циклически перебираются все свойства объекта, находящиеся в его цепочке прототипов, а единственный способ исключить ненужные свойства — использовать [`hasOwnProperty`](#object.hasownproperty) — в действии такой цикл **до 20 раз** медленнее обычного цикла `for`. ### Итерирование -Для достижения лучшей производительности при итерации по массивам, лучше всего использовать обычный цикл `for`. +Наилучшей производительности при переборе массивов можно достичь используя обычный цикл `for`. var list = [1, 2, 3, 4, 5, ...... 100000000]; for(var i = 0, l = list.length; i < l; i++) { console.log(list[i]); } -В примере выше есть один дополнительный приём, с помощью которого кэшируется величина длины массива: `l = list.length`. +В примере выше есть один дополнительный приём, которым кэшируется длина массива: `l = list.length`. -Несмотря на то, что свойство `length` определено в самом массиве, поиск этого свойства накладывает дополнительные расходы на каждой итерации цикла. Пусть в этом случае новые движки JavaScript теоретически **могут** применить оптимизацию, но нет никакого способа узнать, будет оптимизирован код на новом движке или нет. +Несмотря на то, что свойство `length` определено в самом массиве, поиск этого свойства в прототипе объекта накладывает дополнительные расходы на каждую итерацию цикла. Да, в этом случае современные движки JavaScript и **теоретически** могут применить оптимизацию, но нет способа предугадать наверняка, будет ли оптимизирован код на используемом движке, или нет. -Фактически, отсутствие кэширования может привести к выполнению цикла в **два раза медленнее**, чем при кэшировании длины +Фактически, цикл без кэширования может выполняться в **два раза медленнее**, нежели цикл с закэшированной длиной. ### Свойство `length` -Хотя *геттер* свойства `length` просто возвращает количество элементов содержащихся в массиве, *сеттер* можно использовать для **обрезания** массива. +Хотя *геттер* свойства `length` просто возвращает количество элементов содержащихся в массиве, его *сеттер* можно использовать для **обрезания** массива. var foo = [1, 2, 3, 4, 5, 6]; foo.length = 3; @@ -32,9 +32,9 @@ foo.length = 6; foo; // [1, 2, 3] -Присвоение свойству `length` меньшей величины урезает массив, однако присвоение большего значения не даст никакого эффекта. +Присвоение свойству `length` величины, меньшей чем текущая его длина, урезает массив, однако присвоение большего значения не производит никакого эффекта. ### Заключение -Для оптимальной работы кода рекомендуется всегда использовать простой цикл `for` и кэшировать свойство `length`. Использование `for in` с массивами является признаком плохого кода, обладающего предпосылками к ошибкам и может привести к низкой скорости его выполнения. +Для оптимальной работы кода рекомендуется всегда использовать обычный цикл `for` и кэшировать свойство `length`. Использование `for in` с массивами является признаком плохого кода, содержащего потенциальные ошибки, а также приводит к низкой скорости его выполнения. diff --git a/doc/ru/core/delete.md b/doc/ru/core/delete.md index 7db3010b..5058236b 100644 --- a/doc/ru/core/delete.md +++ b/doc/ru/core/delete.md @@ -1,37 +1,31 @@ ## Оператор `delete` -Если говорить коротко, то JavaScript *невозможно* удалить глобальную переменную, функцию или любой другой объект, которому задан атрибут `DontDelete` . +Если говорить кратко — в JavaScript невозможно удалить глобальную переменную или функцию или любую другую сущность, у которой установлен атрибут `DontDelete`. -### Глобальный код и код функции +### Глобальный код и код внутри функций -Если переменная или функция определена в глобальной области видимости (scope) или в -[области видимости функции](#function.scopes), это значит что она является -свойством (property) глобального или же Activation объекта. -Подобные свойства имеют набор атрибутов, одним из которых и является -упомянутый ранее `DontDelete`. Объявление переменных и функций в коде -функции или глобально всегда создает свойство с атрибутом `DontDelete`, и поэтому -не может быть удалено. +Когда переменная или функция определена в глобальной области видимости или в [области видимости функции](#function.scopes), её судьба предопределена: (_прим. перев._ — внутри движка JavaScript) она является свойством (`property`) либо объекта `Activation`, либо объекта `Global`. Подобные сущности имеют набор внутренних атрибутов, одним из которых и является упомянутый ранее `DontDelete`. Переменные и объявления функций, замеченные движком в глобальной области или в коде функций, создаются с атрибутом `DontDelete` и поэтому не могут быть удалены. // глобальная переменная: - var a = 1; // задается DontDelete + var a = 1; // установлен DontDelete delete a; // false a; // 1 // обычная функция: - function f() {} // задается DontDelete + function f() {} // установлен DontDelete delete f; // false typeof f; // "function" - // переназначение не поможет: + // переопределение не помогает: f = 1; delete f; // false f; // 1 -### Явные свойства +### Установленные пользователем свойства -Явно заданные свойство могут быть удалены обычным способом. +Свойства, установленные явно, могут быть безпрепятственно удалены: - // явно заданные свойства: + // явно установим свойства: var obj = {x: 1}; obj.y = 2; delete obj.x; // true @@ -39,28 +33,24 @@ obj.x; // undefined obj.y; // undefined -В примере выше, `obj.x` и `obj.y` могут быть удалены потому что у них не задан -атрибут `DontDelete`. Именно поэтому следующий пример тоже сработает. +В приведённом примере свойства `obj.x` и `obj.y` могут быть удалены, поскольку у них отсутствует атрибут `DontDelete`. По этой же причине работает и пример ниже: - // работает хорошо везде, кроме IE: + // работает нормально, но не в IE var GLOBAL_OBJECT = this; GLOBAL_OBJECT.a = 1; a === GLOBAL_OBJECT.a; // true - просто глобальная переменная delete GLOBAL_OBJECT.a; // true GLOBAL_OBJECT.a; // undefined -Здесь мы используем небольшой трюк, чтобы удалить `a`. [`this`](#function.this) здесь -относится к глобальному объекту и мы явно указали удаление переменной `a` как свойства -глобального объекта, что и позволи нам ее удалить. +Здесь, чтобы удалить `a`, мы используем один трюк. В этом коде [`this`](#function.this) ссылается на объект `Global` и мы явно описываем переменную `a` под видом его свойства, что и позволяет нам её успешно удалить. -Из-за нескольких багов в IE (как минимум 6-8) предыдущий код работать в нем не будет. +Internet Explorer (по крайней мере, с 6-го по 8-й) содержит баги, из-за которых такой код не сработает. -### Аргументы функций и встроенные модули (built-ins) +### Аргументы функций и встроенные свойства -Обычным аргументам функций [`arguments` objects](#function.arguments) и -встроенным свойствам также задан атрибут `DontDelete`. +Обычные аргументы функций, [объект `arguments`](#function.arguments), а также встроенные свойства, объединены общей особенностью: у всех у них установен атрибут `DontDelete`. - // аргументы функции и свойства: + // аргументы функций и свойства: (function (x) { delete arguments; // false @@ -75,14 +65,12 @@ })(1); -### Host объекты +### Хост-объекты -Host объект - это объект, предоставляемый окружением. К таким объектам относятся `window`, -`document`, `location` и так далее. -Для host объектов поведение оператора `delete` может быть непредсказуемым. -Согласно спецификации, таким объектам позволено реализовывать любой вид поведения. +(_прим. перев._ — Хост-объекты — это объекты, которые, в неком окружении, дополняют функциональность языка JavaScript, не являясь частью его спецификации. В случае браузера это объекты `window`, `document`, `setTimeout` и т.п.) + +Поведение оператора `delete` может быть полностью непредсказуемым, когда вы примененяете его к таким хост-объектам. С позволения спецификации, хост-объекты вольны вести себя как им только вздумается. ### Заключение -Оператор `delete` часто обладает непредсказуемым поведением и безопасно использовать -его можно лишь для удаления явно заданных свойств обычных объектов. \ No newline at end of file +Оператор `delete` часто ведёт себя непредсказуемо и может использоваться относительно безопасно только для удаления пользовательских свойств у обычных объектов. diff --git a/doc/ru/core/eval.md b/doc/ru/core/eval.md index 8051a5a2..66d4f435 100644 --- a/doc/ru/core/eval.md +++ b/doc/ru/core/eval.md @@ -1,6 +1,6 @@ ## Почему нельзя использовать `eval` -Функция `eval` выполняет строку кода JavaScript в локальной области видимости. +Функция `eval` выполнит переданную строку в качестве кода JavaScript в локальной области видимости: var foo = 1; function test() { @@ -11,7 +11,7 @@ test(); // 3 foo; // 1 -Но `eval` исполняется в локальной области видимости только тогда, когда он вызывается **напрямую** *и при этом* имя вызываемой функции именно `eval`. +но только тогда, когда функция `eval` вызывается явно *и при этом* имя вызываемой функции *идентично* `eval`: var foo = 1; function test() { @@ -23,17 +23,17 @@ test(); // 2 foo; // 3 -**Любой ценой** избегайте использования функции `eval`. 99.9% случаев её "использования" могут достигаться **без её участия**. +**Любой ценой** избегайте функции `eval`. 99.9% «трюков» с её «использованием» могут быть запросто решены и **без её участия**. ### `eval` под прикрытием -Обе [функции работы с интервалами времени](#other.timeouts) `setTimeout` и `setInterval` могут принимать строку в качестве первого аргумента. Эта строка **всегда** будет выполняться в глобальной области видимости, поскольку `eval` в этом случае вызывается *не напрямую*. +Обе [функции работы с интервалами времени](#other.timeouts) `setTimeout` и `setInterval` могут принимать строку в качестве первого аргумента. Эта строка **всегда** будет выполняться в глобальной области видимости, поскольку `eval` в этом случае вызывается *неявно*. ### Проблемы с безопасностью -Кроме всего прочего, функция `eval` — это проблема в безопасности, поскольку исполняется **любой** переданный в неё код; **никогда** не следует использовать её со строками из неизвестных или недоверенных источников. +Кроме всего прочего, функция `eval` — это дыра в безопасности, поскольку она выполняет **любой** переданный в неё код; **никогда** не используйте её со строками из неизвестных или недоверительных источников. ### Заключение -Никогда не стоит использовать `eval`: любое применение такого кода поднимает вопросы о качестве его работы, производительности и безопасности. Если вдруг для работы вам необходима `eval`, эта часть должна тут же ставиться под сомнение и **не** должна использоваться в первую очередь — необходимо найти *лучший способ*, которому не требуются вызовы `eval`. +Никогда не используйте `eval`: любой код с участием этой функции автоматически порождает вопросы о качестве его работы, производительности и безопасности. Если вдруг для работы вам необходим `eval`, эта часть кода должна тут же ставиться под сомнение и в первую очередь *исключаться* из проекта — необходимо найти *лучший способ*, которому не требуются вызовы `eval`. diff --git a/doc/ru/core/semicolon.md b/doc/ru/core/semicolon.md index e36be8de..6714c970 100644 --- a/doc/ru/core/semicolon.md +++ b/doc/ru/core/semicolon.md @@ -1,8 +1,8 @@ ## Автоматическая вставка точек с запятой -Хоть JavaScript и имеет синтаксис, подобный языкам семейства C, он при этом **не** принуждает вас ставить точки с запятой в исходном коде — вы всегда можете их опустить. +Несмотря на то, что синтаксис JavaScript подобен языкам семейства C, он **не принуждает** вас ставить точки с запятой в исходном коде — вам всегда позволено обойтись без них. -При этом JavaScript — не язык без точек с запятой, они на самом деле нужны ему, чтобы он мог разобраться в вашем коде. Поэтому парсер JavaScript **автоматически** вставляет их в те места, где сталкивается с ошибкой парсинга из-за их отсутствия. +При этом JavaScript — не из тех языков, в которых отсутствуют точки с запятой: на самом деле они очень нужны ему — для того, чтобы он мог разобраться в вашем коде. Поэтому парсер JavaScript **автоматически** вставляет их в те места, где сталкивается с ошибкой парсинга, вызванной их отсутствием. var foo = function() { } // ошибка разбора, ожидается точка с запятой @@ -14,7 +14,7 @@ }; // ошибки нет, парсер продолжает test() -Автоматическая вставка точек с запятой считается одним из **наибольших** упущений в проекте языка, поскольку она *может* изменить поведение кода. +Автоматическая вставка точек с запятой считается одним из **наибольших** упущений в проекте языка, поскольку такая вставка *действительно может* влиять на поведение кода. ### Как это работает @@ -22,7 +22,7 @@ (function(window, undefined) { function test(options) { - log('тестируем!') + log('проверяем!') (options.list || []).forEach(function(i) { @@ -47,7 +47,7 @@ })(window) -Ниже представлен результат игры парсера в "угадалки". +Ниже представлен результат игры парсера в «угадалки». (function(window, undefined) { function test(options) { @@ -62,7 +62,7 @@ 'и ещё одну на всякий случай' ); // <- вставлена - return; // <- вставлена, в результате + return; // <- вставлена, в результате // оператор return разбит на два блока { // теперь парсер считает этот блок отдельным @@ -78,24 +78,24 @@ })(window); //<- вставлена -> **Замечание:** Парсер JavaScript некорректно обрабатывает оператор `return`, за которым следует новая строка; кстати, причина может быть и не в автоматической вставке точек с запятой, но это в любом случае нежелательный побочный эффект +> **Замечание:** Парсер JavaScript некорректно обрабатывает оператор `return`, за которым следует новая строка; Причина может скрываться и не за автоматической вставкой точек с запятой — но без сомнений — этот побочный эффект нежелателен. -Парсер радикально подменил поведение изначального кода, а в определённых случаях он сделал **абсолютно неправильные выводы**. +Парсер радикально поменял поведение изначального кода, а в определённых случаях он вообще сделал **абсолютно неправильные выводы**. -### "Висящие" скобки +### «Висящие» скобки -Если парсер встречает "висящую" скобку, то он **не** вставляет точку с запятой. +Если парсер встречает «висящую» открывающую скобку, он **не** вставляет точку с запятой. log('тестируем!') (options.list || []).forEach(function(i) {}) -Такой код трансформируется в строку +Такой код трансформируется в одну склеенную строку. log('тестируем!')(options.list || []).forEach(function(i) {}) -**Чрезвычайно** высоки шансы, что `log` возвращает **не** функцию; таким образом, эта строка вызовет `TypeError` с сообщением о том, что `undefined не является функцией`. +Шансы на то, что `log` **не** вернёт функцию, **крайне** высоки; так что выполнение этой строки кода породит `TypeError`, приправив его сообщением о том, что `undefined не является функцией`. ### Заключение -Настоятельно рекомендуем **никогда** не забывать ставить точку с запятой; также рекомендуется оставлять скобки на одной строке с соответствующим оператором и никогда не опускать их для выражений с использованием `if` / `else`. Оба этих совета не только повысят читабельность вашего кода, но и предотвратят от изменения поведения кода, произведённого парсером втихую. +Настоятельно рекомендуем **никогда** не забывать ставить точку с запятой; также рекомендуется оставлять скобки на одной строке с соответствующим оператором и всегда использовать их в выражениях `if` / `else`. Оба этих совета не только повысят читабельность вашего кода, но и предотвратят вас от изменений в поведении кода, сделанных парсером без вашего ведома. diff --git a/doc/ru/core/undefined.md b/doc/ru/core/undefined.md index 092aec61..71689fed 100644 --- a/doc/ru/core/undefined.md +++ b/doc/ru/core/undefined.md @@ -1,31 +1,33 @@ ## `undefined` и `null` -В JavaScript есть два отдельных типа для представления `ничего`, при этом более полезным из них является `undefined`. +В JavaScript используется два отдельных типа для описания *ничего* — `null` и `undefined`, при этом более полезным из них оказывается `undefined`. -### Тип `undefined` +### Значение `undefined` `undefined` — это тип с единственным возможным значением: `undefined`. -Кроме этого, в языке определена глобальная переменная со значением `undefined`, и эта переменная так и называется — `undefined`. Не являясь константой, она не является и ключевым словом. Из этого следует, что её значение можно с лёгкостью переопределить. +Кроме этого, в языке определена глобальная переменная со значением `undefined`, причём эта переменная так и называется — `undefined`. Не являясь константой, она не является и ключевым словом. Из этого следует, что её значение можно с лёгкостью переопределить. -> **ES5 Замечание:** в ECMAScript 5 переменная `undefined` **больше не** *доступна на запись* в strict-режиме, однако она всё так же может быть перегружена по имени, например - функцией с именем `undefined`. +> **Замечание по ES5:** в ECMAScript 5 переменная `undefined` **больше не** *доступна на запись* в strict-режиме, однако она всё ещё может быть перегружена по имени, например — функцией с именем `undefined`. -Несколько случаев, когда возвращается `undefined`: +Список случаев, когда код возвращает значение `undefined`: - - При попытке доступа к глобальной переменной `undefined` (если она не изменена). + - При попытке доступа к глобальной переменной `undefined` (если она не была переопределена). + - При попытке доступа к переменной, которая *ещё не была* инициализирована каким-либо значением. - Неявный возврат из функции при отсутствии в ней оператора `return`. - - Из операторов `return`, которые ничего не возвращают. - - В результате поиска несуществующего свойства у объекта (и доступа к нему). - - Параметры, которые не были переданы в функцию явно. - - При доступе ко всему, чьим значением является `undefined`. + - Из оператора `return`, который не возвращает явного значения. + - В результате поиска несуществующего свойства у объекта (и/или доступа к нему). + - При попытке доступа к аргументу функции, который не был передан в неё явно. + - При попытке доступа к чему-либо, чьим значением является `undefined`. + - В результате вычисления любого выражения, соответствующего форме `void(выражение)`. -### Обработка изменений значения `undefined` +### Защита от потенциальных изменений значения `undefined` -Поскольку глобальная переменная `undefined` содержит копию настоящего *значения* `undefined`, присвоение этой переменной нового значения **не** изменяет значения *типа* `undefined`. +Поскольку глобальная переменная `undefined` содержит копию реального *значения* `undefined`, присвоение этой переменной нового значения **не** изменяет значения у *типа* `undefined`. -Но при этом, чтобы сравнить что-либо со *значением* `undefined`, прежде нужно получить значение самой *переменной* `undefined`. +Получается, чтобы проверить что-либо с *типом* `undefined` (на соответствие *значению* `undefined`), прежде нужно узнать изначальное значение самой *переменной* `undefined`. -Чтобы защитить код от переопределения переменной `undefined`, часто используется техника [анонимной обёртки](#function.scopes), которая использует отсутствующий аргумент. +Для того, чтобы защитить код от случайного переопределения переменной `undefined`, часто используют технику [анонимной обёртки](#function.scopes), в которую добавляют аргумент и намеренно не передают его значение. var undefined = 123; (function(something, foo, undefined) { @@ -43,11 +45,11 @@ })('Hello World', 42); -Единственная разница между этими вариантами в том, что последняя версия будет больше на 4 байта при минификации, а в первом случае внутри анонимной обёртки нет дополнительного оператора `var`. +Единственная разница между этими вариантами в том, что последняя версия при минификации будет занимать больше на 4 байта, поскольку в первом случае внутри анонимной обёртки нет дополнительного оператора `var`. -### Использование `null` +### Применение `null` -Хотя `undefined` в контексте языка JavaScript чаще используется в качестве традиционного *null*, настоящий `null` (и тип и литерал) является в большей или меньшей степени просто другим типом данных. +Хотя `undefined` в контексте языка JavaScript чаще используется в роли традиционного *null*, настоящий `null` (и тип и литерал) является, в какой-то степени, просто другим типом данных. -Он используется во внутренних механизмах JavaScript (например для определения конца цепочки прототипов за счёт присваивания `Foo.prototype = null`). Но в большинстве случаев тип `null` может быть заменён на `undefined`. +Он используется во внутренних механизмах JavaScript (в случаях вроде установки конца цепочки прототипов, через присваивание `Foo.prototype = null`). Но практически во всех случаях тип `null` может быть заменён на равносильный ему `undefined`. diff --git a/doc/ru/function/arguments.md b/doc/ru/function/arguments.md index c96c76fc..8a564585 100644 --- a/doc/ru/function/arguments.md +++ b/doc/ru/function/arguments.md @@ -4,17 +4,17 @@ > **Замечание:** В случае, если переменная `arguments` уже была объявлена в области видимости функции либо путём присвоения через выражение `var`, либо являясь формальным параметром, объект `arguments` не будет создан. -Объект `arguments` **не** является наследником `Array`. Он, конечно же, очень похож на массив и даже содержит свойство `length` — но он не наследует `Array.prototype`, а представляет собой `Object`. +Объект `arguments` **не** является ни экземпляром, ни наследником `Array`. Он, конечно же, очень похож на массив, и даже обладает свойством `length` — но он не наследует `Array.prototype`, и если внимательно присмотреться, он окажется обычным `Object`. -По этой причине, у объекта `arguments` **отсутствуют** стандартные методы массивов, такие как `push`, `pop` или `slice`. Хотя итерация с использованием обычного цикла `for` по аргументам работает вполне корректно, вам придётся конвертировать этот объект в настоящий массив типа `Array`, чтобы применять к нему стандартные методы массивов. +По этой причине, у объекта `arguments` **отсутствуют** стандартные методы массивов, такие как `push`, `pop` или `slice`. Пусть перебор с использованием обычного цикла `for` по аргументам работает вполне корректно, но вам придётся конвертировать этот объект в настоящий массив типа `Array`, чтобы обрести возможность применять к нему стандартные методы массивов. -### Конвертация в массив +### Преобразование в массив -Указанный код вернёт новый массив типа `Array`, содержащий все элементы объекта `arguments`. +Этот код вернёт новый массив типа `Array`, содержащий все элементы объекта `arguments`. Array.prototype.slice.call(arguments); -Эта конвертация занимает **много времени** и использовать её в критических частях кода **не рекомендуется**. +Будьте внимательны — это преобразование занимает **много времени** и поэтому **не рекомендуется** использовать его в чувствительных к производительности частях кода. ### Передача аргументов @@ -27,7 +27,7 @@ // делаем здесь что-нибудь } -Другой трюк — использовать и `call` и `apply` вместе, чтобы быстро создать несвязанную обёртку: +Другой трюк — использовать и `call` и `apply` вместе, чтобы создать обёртку, отвязанную от объекта, и при этом выполняющуюся приемлемо быстро: function Foo() {} @@ -35,7 +35,7 @@ console.log(this, a, b, c); }; - // Создаём несвязанную версию "method" + // Создаём несвязанную версию метода // Она принимает параметры: this, arg1, arg2...argN Foo.method = function() { @@ -47,9 +47,9 @@ ### Формальные аргументы и индексы аргументов -Объект `arguments` создаёт по *геттеру* и *сеттеру* и для всех своих свойств и для формальных параметров функции. +Объект `arguments` создаёт по одному *геттеру* и по одному *сеттеру* как для всех своих свойств, так и для формальных параметров функции. -В результате, изменение формального параметра также изменит значение соответствующего свойства объекта `arguments` и наоборот. +В результате, изменение формального параметра повлечёт за собой изменение значения соответствующего свойства объекта `arguments` и наоборот. function foo(a, b, c) { arguments[0] = 2; @@ -64,15 +64,15 @@ } foo(1, 2, 3); -### Мифы и правда о производительности +### Разоблачение мифов о производительности -Объект `arguments` создаётся во всех случаях, лишь за двумя исключениями — когда он переопределён внутри функции (по имени) или когда одним из её параметров является переменная с таким именем. Неважно, используется при этом сам объект или нет. +Объект `arguments` создаётся во всех случаях, за одним лишь исключением — когда он переопределён по имени внутри функции, или когда одним из её параметров является переменная с таким именем. При этом ееважно, используется ли сам объект в коде функции или нет. -*Геттеры* и *сеттеры* создаются **всегда**; так что их использование практически никак не влияет на производительность. +*Геттеры* и *сеттеры* создаются **всегда**; так что их использование практически никак не влияет на производительность, и тем более никак не влияет на неё в реальном коде, где обычно происходят вещи посерьёзнее обычных прочтений и переопределений свойств объекта `arguments`. -> **ES5 Замечание:** Эти *геттеры* и *сеттеры* не создаются в strict-режиме. +> **Замечание по ES5:** Эти *геттеры* и *сеттеры* не создаются в strict-режиме. -Однако, есть один момент, который может радикально понизить производительность современных движков JavaScript. Этот момент — использование `arguments.callee`. +Однако, есть одна такая тайна, что её незнание может радикально понизить производительность кода в современных движках JavaScript. Эта тайна — опасность использования `arguments.callee`. function foo() { arguments.callee; // сделать что-либо с этим объектом функции @@ -81,15 +81,15 @@ function bigLoop() { for(var i = 0; i < 100000; i++) { - foo(); // При обычных условиях должна бы была быть развёрнута... + foo(); // должна была бы «развернуться» } } -В коде выше, функция `foo` не может [быть развёрнута][1] (а могла бы), потому что для корректной работы ей необходима ссылка и на себя и на вызвавший её объект. Это не только кладёт на лопатки механизм развёртывания, но и нарушает принцип инкапсуляции, поскольку функция становится зависима от конкретного контекста вызова. +В коде выше, функция `foo` не может [быть «развёрнута»][1] (а могла бы), потому что для корректной работы ей необходима ссылка как на себя, так и на вызвавший её объект. Такой код не только кладёт на лопатки механизм развёртывания, но и нарушает принцип инкапсуляции, поскольку функция становится зависима от конкретного контекста вызова. **Крайне не рекомендуется** использовать `arguments.callee` или какое-либо из его свойств. **Никогда**. -> **ES5 Замечание:** В strict-режиме использование `arguments.callee` породит `TypeError`, поскольку его использование принято устаревшим. +> **Замечание по ES5:** В strict-режиме любое использование `arguments.callee` вызовет `TypeError`, поскольку свойство принято устаревшим. [1]: http://en.wikipedia.org/wiki/Inlining diff --git a/doc/ru/function/closures.md b/doc/ru/function/closures.md index b96ccf61..5b3c50cf 100644 --- a/doc/ru/function/closures.md +++ b/doc/ru/function/closures.md @@ -1,8 +1,8 @@ ## Замыкания и ссылки -Одним из самых мощных инструментов JavaScript'а считаются возможность создавать *замыкания* — это такой приём, когда наша область видимости **всегда** имеет доступ к внешней области, в которой она была объявлена. Собственно, единственный механизм работы с областями видимости в JavaScript — это [функции](#function.scopes): т.о. объявляя функцию, вы автоматически реализуете замыкания. +Одним из самых мощных инструментов языка JavaScript считают возможность создавать *замыкания*. Это такой приём, когда новые области видимости (например, функций) **постоянно** имеют доступ к внешней области, в которой они были объявлены. Собственно, единственный механизм создания областей видимости в JavaScript — это и есть [функции](#function.scopes): таким образом, объявляя функцию, вы автоматически реализуете замыкания. Или, другими словами: любая объявленная функция по умолчанию ведёт себя как замыкание. -### Эмуляция приватных свойств +### Эмуляция приватных переменных function Counter(start) { var count = start; @@ -21,22 +21,22 @@ foo.increment(); foo.get(); // 5 -В данном примере `Counter` возвращает **два** замыкания: функции `increment` и `get`. Обе эти функции сохраняют **ссылку** на область видимости `Counter` и, соответственно, имеют доступ к переменной `count` из этой самой области. +В данном примере `Counter` возвращает **два** замыкания: функции `increment` и `get`. Обе эти функции сохраняют внутри себя **ссылку** на область видимости `Counter` и, соответственно, имеют свободный доступ к переменной `count`, объявленный в этой самой области. -### Как это работает +### Как работают приватные переменные -Поскольку в JavaScript нельзя присваивать или ссылаться на области видимости, заполучить `count` извне **не** представляется возможным. Единственным способом взаимодействовать с ним остается использование двух замыканий. +Поскольку в JavaScript нельзя присваивать или ссылаться на области видимости, заполучить `count` извне — **не** представляется возможным. Единственный способ взаимодействовать с этой переменной — её изменение внутри двух приведённых выше замыканий. var foo = new Counter(4); foo.hack = function() { count = 1337; }; -В приведенном примере мы **не** изменяем переменную `count` в области видимости `Counter`, т.к. `foo.hack` не объявлен в **данной** области. Вместо этого будет создана или перезаписана *глобальная* переменная `count`; +В приведенном примере мы **не** изменяем переменную `count` из области видимости `Counter`, т.к. `foo.hack` не объявлен в **той же** области. Вместо этого будет создана или перезаписана *глобальная* переменная `count` (_прим. перев._ — замена кода внутри `foo.hack` на `this.count = 1337`, не поможет, конечно же, тоже, поскольку `count` никогда не был свойством объекта `Counter`, а был лишь внутренней переменной); ### Замыкания внутри циклов -Часто встречается ошибка, когда замыкания используют внутри циклов, передавая переменную индекса внутрь. +Существует одна ловушка, которую можно встретить довольно часто — когда замыкания используют внутри циклов, передавая переменную индекса внутрь. for(var i = 0; i < 10; i++) { setTimeout(function() { @@ -44,15 +44,15 @@ }, 1000); } -Данный код **не** будет выводить числа с `0` до `9`, вместо этого число `10` будет выведено десять раз. +Данный код **не** будет выводить числа с `0` до `9` — вместо этого число `10` будет выведено десять раз. -*Анонимная* функция сохраняет **ссылку** на `i` и, когда будет вызвана функция `console.log`, цикл `for` уже закончит свою работу, а в `i` будет содержаться `10`. +*Анонимная* функция сохраняет лишь **ссылку** на `i` — и в тот момент, когда будет вызвана функция `console.log`, цикл `for` уже давно *завершит* свою работу — и именно поэтому в переменной `i` уже будет покоиться последнее значение `10`. Для получения желаемого результата необходимо создать **копию** переменной `i`. -### Во избежание ошибок +### Обход проблемы со ссылкой -Для того, чтобы скопировать значение индекса из цикла, лучше всего использовать [анонимную функцию](#function.scopes) как обёртку. +Для того, чтобы скопировать значение индекса из цикла, лучше всего использовать другую [анонимную функцию](#function.scopes) как обёртку. for(var i = 0; i < 10; i++) { (function(e) { @@ -62,11 +62,11 @@ })(i); } -Анонимная функция-обертка будет вызвана сразу же, и в качестве первого аргумента получит `i`, **значение** которой будет скопировано в параметр `e`. +Анонимная функция-обертка вызывается сразу же, и в качестве первого аргумента получает индекс `i`, **значение** которого будет скопировано в параметр `e`. -Анонимная функция, которая передается в `setTimeout`, теперь содержит ссылку на `e`, значение которой **не** изменяется циклом. +Анонимная функция, которая передается в `setTimeout`, теперь содержит ссылку на переменную `e`, значение которой **не** изменяется циклом. -Еще одним способом реализации является возврат функции из анонимной функции-обертки, поведение этого кода будет таким же, как и в коде из предыдущего примера. +Этот приём можно реализовать и другим способом — возвратив нужную функции из анонимной функции-обертки — поведение такого кода будет идентично поведению кода из предыдущего примера. for(var i = 0; i < 10; i++) { setTimeout((function(e) { @@ -76,5 +76,5 @@ })(i), 1000) } -> **Замечание** от перев. Переменную `e` можно тоже назвать `i`, если вы хотите: это не поменяет поведения кода — внутренняя переменная `i` всё так же будет *копией* внешней переменной +_Прим, перев._ В качестве упражнения на замыкания и анонимные функции, попробуйте заменить оборачивающие вызовы анонимных функций в примерах на варианты с `.call` и `.apply`. diff --git a/doc/ru/function/constructors.md b/doc/ru/function/constructors.md index 7a322aff..05c50e44 100644 --- a/doc/ru/function/constructors.md +++ b/doc/ru/function/constructors.md @@ -1,10 +1,10 @@ ## Конструктор -Создание конструкторов в JavaScript также отличается от большинства других языков. Любая функция, вызванная с использованием ключевого слова `new`, будет конструктором. + Конструкторы в JavaScript тоже ведут себя не так, как в большинстве языков. Любая функция, вызванная с использованием ключевого слова `new`, станет конструктором. -Внутри конструктора (вызываемой функции) `this` будет указывать на новосозданный `Object`. [Прототипом](#object.prototype) этого **нового** объекта будет `prototype` функции, которая была вызвана в качестве конструктора. +Внутри конструктора (вызываемой функции) `this` будет указывать на созданный `Object`. [Прототипом](#object.prototype) этого **нового** экземпляра будет `prototype` функции, вызванной под именем конструктора. -Если вызываемая функция не имеет явного возврата посредством `return`, то вернётся `this` — этот новый объект. +Если вызываемая функция не возвращает явного значения при помощи `return`, она автоматически вернёт `this` — тот самый новый экземпляр. function Foo() { this.bla = 1; @@ -18,7 +18,7 @@ В этом примере `Foo` вызывается в виде конструктора, следовательно прототип созданного объекта будет привязан к `Foo.prototype`. -В случае, когда функция в явном виде возвращает некое значение используя `return`, то в результате выполнения конструктора мы получим именно его, **но только** если возвращаемое значение представляет собой `Object`. +В случае, когда функция в явном виде возвращает некое значение, при выполнении конструктора мы получим именно это значение, **но только** если возвращаемое значение представляет собой `Object`. function Bar() { return 2; @@ -34,18 +34,18 @@ } new Test(); // возвращённый объект -Если же опустить ключевое слово `new`, то функция **не** будет возвращать никаких объектов. +Если же опустить ключевое слово `new`, то функция **не** будет возвращать объекта. function Foo() { - this.bla = 1; // устанавливается глобальному объекту + this.bla = 1; // свойство bla устанавливается глобальному объекту } - Foo(); // undefined + Foo(); // возвращает undefined -Этот пример в некоторых случаях всё-таки может сработать: это связано с поведением [`this`](#function.this) в JavaScript — он будет восприниматься парсером как *глобальный объект*. +Хотя этот пример и будет работать — в связи со сложным поведением [`this`](#function.this) в JavaScript, значение будет присвоено *глобальному объекту* — навряд ли это то, что предполагалось автором. ### Фабрики -Если хотите избавится от необходимости использования `new`, напишите конструктор, возвращающий значение посредством `return`. +Если вы хотите предоставить возможность опускать оператор `new` при создании объектов, возвращайте из соответствующего конструктора явное значение посредством `return`. function Bar() { var value = 1; @@ -62,17 +62,17 @@ new Bar(); Bar(); -В обоих случаях при вызове `Bar` мы получим один и тот же результат — новый объект со свойством `method` (спасибо [замыканию](#function.closures) за это). +В обоих случаях при вызове `Bar` мы получим один и тот же результат — новый объект со свойством `method`, являющимся [замыканием](#function.closures)). -Также следует заметить, что вызов `new Bar()` никак **не** связан с прототипом возвращаемого объекта. Хоть прототип и назначается всем новосозданным объектам, но `Bar` никогда не возвращает этот новый объект. +Ещё следует заметить, что вызов `new Bar()` никак **не** воздействует на прототип возвращаемого объекта. Хоть прототип и назначается всем новосозданным объектам, `Bar` никогда не возвращает этот новый объект (_прим. перев._ — судя по всему, подразумевается, что код `Bar` не может влиять на прототип созданного объекта, и под словами «новый объект» в последнем случае кроется прототип нового объекта, а не сам новый объект). -В предыдущем примере нет функциональных отличий между вызовом конструктора с оператором `new` или без него. +В предыдущем примере нет никаких функциональных различий между вызовом конструктора с оператором `new` и вызовом без него. ### Создание объектов с использованием фабрик -Часто **не** рекомендуют использовать `new`, поскольку если вы его забудете, это может привести к ошибкам. +Многие рекомендуют **не** использовать оператор `new` вовсе — аргументируя это тем, что если вы забудете его написать, такое «необдуманное» действие может «неизбежно» привести к ошибкам. -Чтобы создать новый объект, лучше использовать фабрику и создать новый объект *внутри* этой фабрики. +Чтобы создать новый объект, нам предлагают использовать фабрику и создать новый объект *внутри* этой фабрики. function Foo() { var obj = {}; @@ -89,13 +89,13 @@ return obj; } -Хотя данный пример и сработает, если вы забыли ключевое слово `new`, и благодаря ему легче работать с [приватными переменными](#function.closures), у него есть несколько недостатков +Хотя данный пример и сработает, когда вы забыли ключевое слово `new`, да и благодаря ему вам действительно станет легче работать с [приватными переменными](#function.closures), у него есть несколько недостатков: - 1. Он использует больше памяти, поскольку созданные объекты **не** хранят методы в прототипе и соответственно для каждого нового объекта создаётся копия каждого метода. - 2. Чтобы эмулировать наследование, фабрике нужно скопировать все методы из другого объекта или установить прототипом нового объекта старый. - 3. Разрыв цепочки прототипов просто по причине забытого ключевого слова `new` идёт вразрез с духом языка. + 1. Он использует больше памяти, поскольку созданные объекты **не** хранят методы в прототипе и соответственно для каждого нового объекта создаётся копия каждого метода; + 2. Чтобы эмулировать наследование, фабрике нужно скопировать все методы из другого объекта или установить прототипом нового объекта старый; + 3. Разрыв цепочки прототипов по надуманной необходимости избавиться от использования ключевого слова `new`, идёт вразрез с духом языка; ### Заключение -Хотя забытое ключевое слово `new` и может привести к багам, это точно **не** причина отказываться от использования прототипов. В конце концов, полезнее решить, какой из способов лучше совпадает с требованиями приложения: очень важно выбрать один из стилей создания объектов и после этого **не изменять** ему. +Хотя забытое ключевое слово `new` и правда может привести к багам, это точно **не** причина отказываться от использования прототипов. В конце концов, полезнее решить, какой из способов лучше совпадает с требованиями приложения: крайне важно выбрать один из стилей создания объектов и после этого **не изменять** ему. diff --git a/doc/ru/function/general.md b/doc/ru/function/general.md index eaf48c67..49b19c84 100644 --- a/doc/ru/function/general.md +++ b/doc/ru/function/general.md @@ -1,45 +1,40 @@ -## Выражения и объявление функций +## Про объявление функций и о выражениях с ними -Функции в JavaScript тоже являются объектами (шок, сенсация) — следовательно, их можно передавать и присваивать точно так же, как и любой другой объект. Одним из вариантов использования такой возможности является передача *анонимной функции* как функции обратного вызова в другую функцию — к примеру, для асинхронных вызовов. +Функции в JavaScript являются объектами. Следовательно, их можно передавать и присваивать точно так же, как и любой другой объект. Популярное использование этого замечательного свойства — передача *анонимной функции* в качестве функции обратного вызова в некую другую функцию — к примеру, при описании асинхронных вызовов. ### Объявление `function` // всё просто и привычно function foo() {} -В следующем примере описанная функция [резервируется](#function.scopes) перед запуском всего скрипта; за счёт этого она доступна *в любом месте* кода, вне зависимости от того, где она *определена* — даже если функция вызывается до её фактического объявления в коде. +В следующем примере, ещё перед запуском всего скрипта, для описанной функции [резервируется](#function.scopes) переменная; за счёт этого она становится доступна *в любом месте* кода, вне зависимости от того, где она *определена* — даже если она вызывается заранее, перед её фактическим объявлением в коде (и сколь угодно задолго до такого определения). - foo(); // сработает, т.к. функция будет создана до выполнения кода + foo(); // сработает, т.к. функция будет создана при компиляции, до выполнения кода function foo() {} ### `function` как выражение var foo = function() {}; -В этом примере безымянная и *анонимная* функция присваивается переменной `foo`. +В конце примера ниже переменной `foo` присваивается безымянная *анонимная* функция. foo; // 'undefined' foo(); // вызовет TypeError var foo = function() {}; -Так как в данном примере выражение `var` — это определение функции, переменная с именем `foo` будет заранее зарезервирована перед запуском скрипта (таким образом, `foo` уже будет определена во время его работы). +Поскольку выражение с применением `var` *резервирует* имя переменной `foo` ещё до запуска кода, `foo` уже имеет некое значение во время его исполнения (ошибка «`foo` is not defined» отсутствует). -Но поскольку присвоения исполняются непосредственно во время работы кода, `foo` по умолчанию будет присвоено значение [`undefined`](#core.undefined) (до обработки строки с определением функции): +Но поскольку *сами присвоения* исполняются непосредственно во время работы кода, до выполнения строки с определением функции `foo` будет иметь значение [`undefined`](#core.undefined). - var foo; // переменная неявно резервируется - foo; // 'undefined' - foo(); // вызовет TypeError - foo = function() {}; - -### Выражения с именованными фунциями +### Выражения с именованными функциями -Существует еще нюанс, касающийся именованных функций создающихся через присваивание: +Существует еще один нюанс, касающийся присваиваний именованных функций: var foo = function bar() { bar(); // работает } bar(); // получим ReferenceError -Здесь объект `bar` не доступен во внешней области, так как имя `bar` используется только для присвоения переменной `foo`; однако `bar` можно вызвать внутри функции. Такое поведение связано с особенностью работы JavaScript с [пространствами имен](#function.scopes) - имя функции *всегда* доступно в локальной области видимости самой функции. +Здесь фукнция `bar` не доступна во внешней области видимости, так как она используется только для присвоения переменной `foo`; однако, внутри `bar` она неожиданно оказывается доступна. Такое поведение связано с особенностью работы JavaScript с [разыменованием](#function.scopes) - имя функции *всегда* доступно в локальной области видимости самой функции. diff --git a/doc/ru/function/scopes.md b/doc/ru/function/scopes.md index 0cf88caa..f43f6084 100644 --- a/doc/ru/function/scopes.md +++ b/doc/ru/function/scopes.md @@ -1,6 +1,6 @@ ## Области видимости и пространства имён -Хотя JavaScript нормально понимает синтаксис двух фигурных скобок, окружающих блок, он **не** поддерживает блочную область видимости; всё что остаётся на этот случай в языке — *область видимости функций*. +Хотя JavaScript вполне нормально воспринимает синтаксис двух сопоставимых фигурных скобок, окружающих блок, он **не** поддерживает блочную область видимости; всё, что остаётся на этот случай в языке — *области видимости функций*. function test() { // область видимости for(var i = 0; i < 10; i++) { // не область видимости @@ -9,11 +9,11 @@ console.log(i); // 10 } -> **Замечание:** Нотация `{...}` будет интерпретирована как блочное выражение, а **не** как литерал объекта, если она не используется в присваивании, операторе `return` или в качестве функции. Это замечание, вкупе с [автоматической расстановкой точек с запятой](#core.semicolon), может привести к чрезвычайно хитрым ошибкам. +> **Замечание:** Нотация `{...}` будет интерпретирована как блочное выражение, а **не** как литерал объекта, если она не используется в присваивании, операторе `return` или в качестве определения функции. Это замечание, вкупе с [автоматической расстановкой точек с запятой](#core.semicolon), может привести к чрезвычайно изощрённым ошибкам. -Также JavaScript не знает ничего о различиях в пространствах имён: всё определяется в *глобально доступном* пространстве имён. +Также JavaScript не различает пространств имён: всё определяется на том или ином уровне в единственном *глобально доступном* пространстве имён. -Каждый раз, когда JavaScript обнаруживает ссылку на переменную, он будет искать её всё выше и выше по областям видимости, пока не найдёт её. В случае, если он достигнет глобальной области видимости и не найдет запрошенное имя и там тоже, он ругнётся `ReferenceError`. +Каждый раз, когда JavaScript обнаруживает ссылку на переменную, он будет искать её всё выше и выше по областям видимости, пока не найдёт её. В случае, если он достигнет глобальной области видимости и не найдет запрошенное имя и там тоже, он выбросит `ReferenceError`. ### Проклятие глобальных переменных @@ -23,9 +23,9 @@ // скрипт B var foo = '42' -Вышеприведённые два скрипта **не** приводят к одному результату. Скрипт A определяет переменную по имени `foo` в *глобальной* области видимости, а скрипт B определяет `foo` в текущей области видимости. +Вышеприведённые два скрипта отнюдь **не** приводят к одинаковому результату. Скрипт A определяет переменную по имени `foo` в *глобальной* области видимости, а скрипт B определяет `foo` в текущей области видимости. -Повторимся, это вообще **не** *тот же самый эффект*. Если вы не используете `var` — то вы в большой опасности. +Повторимся, это **совсем не** *тот же самый эффект*. Если вы не используете `var` — вы в большой опасности. // глобальная область видимости var foo = 42; @@ -36,7 +36,7 @@ test(); foo; // 21 -Из-за того что оператор `var` опущен внутри функции, фунция `test` перезапишет значение `foo`. Это поначалу может показаться не такой уж и большой проблемой, но если у вас имеется тысяча строк JavaScript-кода и вы не используете `var`, то вам на пути встретятся страшные и трудноотлаживаемые ошибки — и это не шутка. +Из-за того, что оператор `var` был опущен внутри функции, функция `test` перезапишет значение `foo`. Это поначалу может показаться не такой уж и большой проблемой, но если у вас имеется тысяча строк JavaScript-кода и вы не используете `var`, то вам на пути встретятся самые страшные и трудноотлаживаемые ошибки — и это не шутка. // глобальная область видимости var items = [/* какой-то список */]; @@ -46,12 +46,12 @@ function subLoop() { // область видимости subLoop - for(i = 0; i < 10; i++) { // пропущенный оператор var - // делаем волшебные вещи! + for(i = 0; i < 10; i++) { // пропущен оператор var + // происходят волшебные вещи! } } -Внешний цикл прекратит работу сразу после первого вызова `subLoop`, поскольку `subLoop` перезаписывает глобальное значение переменной `i`. Использование `var` во втором цикле `for` могло бы вас легко избавить от этой ошибки. **Никогда** не забывайте использовать `var`, если только влияние на внешнюю область видимости не является тем, что вы *намерены получить*. +Внешний цикл прекратит работу сразу после первого вызова `subLoop`, поскольку `subLoop` перезаписывает глобальное значение переменной `i`. Использование `var` во втором цикле `for` могло бы легко избавить вас от этой ошибки. **Никогда** не забывайте использовать `var`, если только вы не *осознано намеренны* повлиять на внешнюю область видимости. ### Локальные переменные @@ -75,7 +75,7 @@ ### Всплытие -В JavaScript действует механизм **всплытия** определения. Это значит, что оба определения с использованием `var` и определение `function` будут перенесены наверх заключающей их области видимости. +В JavaScript действует механизм **всплытия** (или *вытягивания*) определения. Это значит, что оба определения с использованием `var` и определение `function` будут перенесены наверх из заключающей их области видимости. bar(); var bar = function() {}; @@ -101,8 +101,8 @@ // определение функции тоже переместилось function test(data) { - var goo, i, e; // потерянная блочная область видимости - // переместилась сюда + var goo, i, e; // упущенная область видимости + // переместила их сюда if (false) { goo = 1; @@ -121,12 +121,11 @@ test(); -Потерянная область видимости блока не только переместит операторы `var` вовне циклов и их тел, но и сделает результаты некоторых конструкций с `if` неинтуитивными. +Потерянная область видимости не только переместит операторы `var` вовне циклов и их тел, но и лишит смысла конструкцию c `if`. -В исходном коде оператор `if` изменял *глобальную переменную* `goo`, когда, как оказалось, он изменяет *локальную переменную* — в результате работы всплытия. +Предполагалось, что в исходном коде оператор `if` изменял *глобальную переменную* `goo`, однако, как оказалось, он изменял *локальную переменную* — в результате работы всплытия. -Если вы не знакомы со *всплытием*, то можете посчитать, что нижеприведённый код должен породить -`ReferenceError`. +Если вы не имели дела с *всплытием*, то можете предположить, что нижеприведённый код должен выбросить `ReferenceError`. // проверить, проинициализована ли SomeImportantThing if (!SomeImportantThing) { @@ -147,25 +146,25 @@ ### Порядок разрешения имён -Все области видимости в JavaScript, включая *глобальную области видимости*, содержат специальную, определённую внутри них, переменную [`this`](#function.this), которая ссылается на *текущий объект*. +Все области видимости в JavaScript, включая *глобальную область видимости*, содержат специальную, определённую внутри них, переменную [`this`](#function.this), которая ссылается на *текущий объект*. Области видимости функций также содержат внутри себя переменную [`arguments`](#function.arguments), которая содержит аргументы, переданные в функцию. Например, когда JavaScript пытается получить доступ к переменной `foo` в области видимости функции, он будет искать её по имени в такой последовательности: - 1. Если в текущей области видимости есть выражение `var foo`, использовать его. - 2. Если один из параметров функции называется `foo`, использовать его. - 3. Если функция сама называется `foo`, использовать её. - 4. Перейти на одну область видимости выше и начать с **п. 1** + 1. Если в текущей области видимости есть выражение `var foo`, использовать эту переменную; + 2. Если один из параметров функции называется `foo`, использовать этот параметр; + 3. Если функция сама называется `foo`, использовать её; + 4. Перейти на одну область видимости выше и повторить, начиная с **п. 1**; -> **Замечание:** Наличие параметра функции с именем `arguments` **не позволит** движку создать объект `arguments`, создающийся по умолчанию. +> **Замечание:** Наличие параметра функции с именем `arguments` **запрещает** движку создать объект `arguments`, создающийся, в других случаях, по умолчанию. ### Пространства имён -Нередкое последствие наличия только одного глобального пространства имён — проблема с перекрытием имён переменных. В JavaScript эту проблему легко избежать, используя *анонимные обёртки*. +Нередко можно столкнуться с таким неприятным последствием единого глобального пространства имён, как проблема с перекрытием имён переменных. В JavaScript эту проблему легко избежать, используя *анонимные обёртки*. (function() { - // самостоятельно созданное "пространство имён" + // самодостаточное «пространство имён» window.foo = function() { // открытое замыкание @@ -173,22 +172,28 @@ })(); // сразу же выполнить функцию -Безымянные функции являются [выражениями](#function.general); поэтому, чтобы вы имели возможность их выполнить, они сперва должны быть разобраны. +Безымянные функции являются отложенными [выражениями](#function.general) (_прим. перев._ — то есть они не выполняются по месту описания, а откладываются движком напоследок); поэтому, чтобы сделать их исполняемыми, сначала следует спровоцировать их разбор. ( // разобрать функцию внутри скобок function() {} ) // и вернуть объект функции () // вызвать результат разбора -Есть другие способы разбора и последующего вызова выражения с функцией; они, хоть и различаются в синтаксисе, но действуют одинаково. +Есть другие способы спровоцировать разбор и последующий вызов выражения с функцией; они, хоть и различаются синтаксисом, действуют одинаково: // Два других способа +function(){}(); (function(){}()); + + ### Заключение -Рекомендуется всегда использовать *анонимную обёртку* для заключения кода в его собственное пространство имён. Это не только защищает код от совпадений имён, но и позволяет создавать более модульные программы. +Рекомендуется всегда использовать *анонимную обёртку*, чтобы заключить код в собственное пространство имён. Это не только защищает ваш код от совпадений имён, но и позволяет создавать модульные программы. -Важно добавить, что использование глобальных переменных считается **плохой практикой**. **Любое** их использование демонстрирует плохое качество кода и может привести к трудноуловимым ошибкам. +Важно добавить, что использование глобальных переменных считается **плохой практикой**. **Любое** их использование демонстрирует плохое качество кода, предполагает его высокую подверженность ошибкам и сложность его разбора. diff --git a/doc/ru/function/this.md b/doc/ru/function/this.md index 6883a546..c9a90368 100644 --- a/doc/ru/function/this.md +++ b/doc/ru/function/this.md @@ -1,50 +1,50 @@ ## Как работает `this` -В JavaScript область ответственности специальной переменной `this` концептуально отличается от того, за что отвечает `this` в других языках программирования. Различают ровно **пять** вариантов того, к чему привязывается `this` в языке. +В JavaScript зона ответственности специальной переменной `this` концептуально отличается от поведения `this` в других языках программирования. Различают ровно **пять** сущностей, к которым в этом языке может быть привязана переменная `this`. ### 1. Глобальная область видимости this; -Когда мы используем `this` в глобальной области, она будет просто ссылаться на *глобальный* объект. +Когда мы используем `this` в глобальной области видимости, она просто ссылается на *глобальный* объект. ### 2. Вызов функции foo(); -Тут `this` также ссылается на *глобальный* объект. +Внутри функции `this` ссылается на *глобальный* объект. -> **ES5 Замечание:** В strict-режиме **теряется** понятие глобальности, поэтому в этом случае `this` будет иметь значение `undefined`. +> **Замечание касательно ES5:** В strict-режиме **отсутствует** понятие глобальной видимости, поэтому `this` в этом случае будет иметь значение `undefined`. ### 3. Вызов метода test.foo(); -В данном примере `this` ссылается на `test`. +Внутри метода `this` ссылается на `test`. ### 4. Вызов конструктора new foo(); -Если перед вызовом функции присутствует ключевое слово `new`, то данная функция будет действовать как [конструктор](#function.constructors). Внутри такой функции `this` будет указывать на *новосозданный* `Object`. +Если перед вызовом функции присутствует ключевое слово `new`, то данная функция будет действовать как [конструктор](#function.constructors). Внутри такой функции `this` будет указывать на *новый созданный* `Object`. ### 5. Переопределение `this` function foo(a, b, c) {} var bar = {}; - foo.apply(bar, [1, 2, 3]); // массив развернётся в a = 1, b = 2, c = 3 - foo.call(bar, 1, 2, 3); // аналогично + foo.apply(bar, [1, 2, 3]); // внутри foo массив развернётся в аргументы + foo.call(bar, 1, 2, 3); // аналогично: a = 1, b = 2, c = 3 Когда мы используем методы `call` или `apply` из `Function.prototype`, то внутри вызываемой функции `this` **явным образом** будет присвоено значение первого передаваемого параметра. -Исходя из этого, в предыдущем примере (строка с `apply`) правило #3 *вызов метода* **не** будет применено, и `this` внутри `foo` будет присвоено `bar`. +Исходя из этого, в предыдущем примере (строка с `apply`), правило №3 *«вызов метода»* **не** применяется, и `this` внутри `foo` будет присвоено `bar`. -> **Замечание:** `this` **нельзя** использовать внутри литералов `{}` (`Object`) для ссылки на сам объект. Т.е. если мы напишем `var obj = {me: this}`, то `me` не будет ссылаться на `obj`, поскольку `this` присваивается только по одному из пяти описанных правил. +> **Замечание:** `this` **нельзя** использовать внутри литералов `{}` (`Object`) для создания ссылки на сам объект. Т.е. если мы напишем `var obj = {me: this}`, то `me` не будет ссылаться на `obj`, поскольку `this` присваивается только по одному из пяти описанных правил. -### Наиболее распространенные ошибки +### Наиболее распространенные ловушки -Хотя большинство из примеров ниже наполнены глубоким смыслом, первый из них можно считать ещё одним упущением в самом языке, поскольку он **вообще** не имеет практического применения. +Хотя большинство из примеров ниже имеют смысл, первый из них можно причислить к упущениям в самом языке, поскольку он **вообще** не имеет практического применения. Foo.method = function() { function test() { @@ -53,7 +53,7 @@ test(); }; -Распространенным заблуждением будет то, что `this` внутри `test` ссылается на `Foo`, но это **не так**. +Распространено заблуждение в том, что `this` внутри `test` ссылается на `Foo`, но это совсем **не так**. Для того, чтобы получить доступ к `Foo` внутри функции `test`, необходимо создать локальную переменную внутри `method`, которая и будет ссылаться на `Foo`. @@ -65,20 +65,18 @@ test(); }; -Подходящее имя для переменной - `that`, его часто используют для ссылки на внешний `this`. В комбинации с [замыканиями](#function.closures) `this` можно пробрасывать в глобальную область или в любой другой объект. +Подходящее имя для такой переменной — `that`, и его часто используют для ссылки на внешний `this`. В комбинации с [замыканиями](#function.closures) такая переменная может использоваться, чтобы «пробрасывать» `this` в глобальную область или в любой другой объект. -> **Замечание** от перев. Кроме `that` также часто встречаются `this_`, `self_` и другие варианты, но лучше принять для себя `that` как стандарт и тогда, возможно, все вокруг тоже будут им пользоваться. +### Присвоение методов -### Назначение методов - -Еще одной фичей, которая **не** работает в `JavaScript`, является создание псевдонимов для методов, т.е. **присвоение** метода объекта переменной. +Еще одной возможностью, которая могла бы работать, но **не** работает в `JavaScript`, является создание псевдонимов (алиасов) для методов, т.е. **присвоение** метода объекта переменной. var test = someObject.methodTest; test(); -Следуя первому правилу `test` вызывается как обычная функция; следовательно `this` внутри него больше не ссылается на `someObject`. +Следуя первому правилу, `test` вызывается как обычная функция; следовательно `this` внутри него больше не ссылается на `someObject`. -Хотя позднее связывание `this` на первый взгляд может показаться плохой идеей, но на самом деле именно благодаря этому работает [наследование прототипов](#object.prototype). +Хотя позднее связывание `this` на первый взгляд может показаться не очень хорошей идеей, на самом деле это именно то, благодаря чему работает [наследование прототипов](#object.prototype). function Foo() {} Foo.prototype.method = function() {}; @@ -90,3 +88,5 @@ В момент, когда будет вызван `method` нового экземпляра `Bar`, `this` будет ссылаться на этот самый экземпляр. +(_прим. перев._ — В случае, если вам необходимо передать `this` в функцию, вы можете использовать `.call`, `.apply` при непосредственно вызове или `.bind` при отложенном — для того, чтобы вызвать эту функцию с указанным `this` в другом месте кода.) + diff --git a/doc/ru/index.json b/doc/ru/index.json index 9e27bf03..9a14c4a9 100644 --- a/doc/ru/index.json +++ b/doc/ru/index.json @@ -9,6 +9,7 @@ "articles": [ "authors", "contributors", + "hosting", "translators", "license" ] @@ -54,12 +55,13 @@ ] }, { - "title": "Нативности", + "title": "Ядро", "dir": "core", "articles": [ "eval", "undefined", - "semicolon" + "semicolon", + "delete" ] }, { diff --git a/doc/ru/intro/authors.md b/doc/ru/intro/authors.md index 3c11a24d..6c8a1b17 100644 --- a/doc/ru/intro/authors.md +++ b/doc/ru/intro/authors.md @@ -1,7 +1,8 @@ ## Авторы -Это руководство является результатом работы двух заядлых пользователей Stack Overflow: [Иво Ветцель /Ivo Wetzel/][1] (автора текста) и [Чжан И Цзян /Zhang Yi Jiang/][2] (дизайнера). +Это руководство является результатом работы двух заядлых пользователей [Stack Overflow][3]: [Иво Ветцель /Ivo Wetzel/][1] (автора текста) и [Чжан И Цзян /Zhang Yi Jiang/][2] (дизайнера). [1]: http://stackoverflow.com/users/170224/ivo-wetzel [2]: http://stackoverflow.com/users/313758/yi-jiang +[3]: http://stackoverflow.com/ diff --git a/doc/ru/intro/hosting.md b/doc/ru/intro/hosting.md new file mode 100644 index 00000000..7ae6e888 --- /dev/null +++ b/doc/ru/intro/hosting.md @@ -0,0 +1,8 @@ +## Хостинг + +JavaScript Garden хостится на [GitHub][3], однако [Cramer Development][1] поддерживают наше зеркало на [JavaScriptGarden.info][2]. + +[1]: http://cramerdev.com/ +[2]: http://javascriptgarden.info/ +[3]: https://github.com/BonsaiDen/JavaScript-Garden + diff --git a/doc/ru/intro/index.md b/doc/ru/intro/index.md index 9debe341..88e5887c 100644 --- a/doc/ru/intro/index.md +++ b/doc/ru/intro/index.md @@ -2,7 +2,7 @@ **JavaScript Гарден** — это постоянно обновляющаяся и растущая документация по самым заковыристым темам языка JavaScript. В ней вы найдёте советы о том, как избежать распространённых ошибок и предсказать появление тех или иных багов. В документации подробно освещены проблемы оптимизации и нерекомендуемые практики с которыми, продираясь к глубинам языка, могут столкнуться даже просвещённые JavaScript-программисты. -JavaScript Гарден **не** имеет цели научить вас языку JavaScript. Вам понадобится реальный опыт работы с языком чтобы понимать темы, рассматриваемые в этом руководстве. Если вам требуется изучить основы языка, пожалуйста внимательно ознакомьтесь с замечательным [руководством][1] на сайте Mozilla Developer Network. +JavaScript Гарден **не cтавит** себе целью научить вас языку JavaScript. Вам понадобится реальный опыт работы с языком чтобы понимать темы, рассматриваемые в этом руководстве. Если вам требуется изучить основы языка, пожалуйста обратитесь к замечательному [руководству][1] на сайте Mozilla Developer Network. [1]: https://developer.mozilla.org/en/JavaScript/Guide diff --git a/doc/ru/intro/translators.md b/doc/ru/intro/translators.md index d08ba2a1..a8c26b07 100644 --- a/doc/ru/intro/translators.md +++ b/doc/ru/intro/translators.md @@ -4,9 +4,10 @@ - [Антон Шевчук][2] - [Максим Лозовой][3] - [Елена Пашкова][4] + - [binariti][5] очень помог со стилистикой текста -[1]: http://shamansir.madfire.net/ +[1]: http://shamansir.github.com/ [2]: http://anton.shevchuk.name/ [3]: http://nixsolutions.com/ [4]: http://nixsolutions.com/ - +[5]: http://habrahabr.ru/users/binariti/ \ No newline at end of file diff --git a/doc/ru/object/forinloop.md b/doc/ru/object/forinloop.md index 3bc5c8d4..7e942471 100644 --- a/doc/ru/object/forinloop.md +++ b/doc/ru/object/forinloop.md @@ -2,9 +2,9 @@ Как и оператор `in`, цикл `for in` проходит по всей цепочке прототипов, обходя свойства объекта. -> **Примечание:** Цикл `for in` **не** обходит те свойства объекта, у которых атрибут `enumerable` установлен в `false`; как пример - свойство `length` у массивов +> **Примечание:** Цикл `for in` **не** обходит те свойства объекта, у которых внутренний атрибут `enumerable` установлен в `false`; как пример - свойство `length` у массивов. - // Испортим Object.prototype + // Подпортим Object.prototype Object.prototype.bar = 1; var foo = {moo: 2}; @@ -12,26 +12,26 @@ console.log(i); // печатает и bar и moo } -Так как изменить поведение цикла `for in` как такового не представляется возможным, то для фильтрации нежелательных свойств объекта внутри этого цикла используют метод [`hasOwnProperty`](#object.hasownproperty) из `Object.prototype`. +Так как изменить поведение цикла `for in` как такового не представляется возможным, то для фильтрации нежелательных свойств объекта внутри этого цикла используется метод [`hasOwnProperty`](#object.hasownproperty) из `Object.prototype`. > **Примечание:** Цикл `for in` всегда обходит всю цепочку прототипов полностью: таким образом, чем больше прототипов (слоёв наследования) в цепочке, тем медленнее работает цикл. ### Использование `hasOwnProperty` в качестве фильтра - // возьмём foo из примера выше + // всё то же foo из примера выше for(var i in foo) { if (foo.hasOwnProperty(i)) { console.log(i); } } -Это единственная версия правильного использования цикла. Благодаря использованию `hasOwnProperty` будет выведено **только** свойство `moo`. Если же убрать `hasOwnProperty`, код становится нестабилен и могут возникнуть ошибки, особенно если кто-то изменил встроенные прототипы, такие как `Object.prototype`. +Это единственно правильная версия выполнения цикла обхода ключей объекта. За счёт использования `hasOwnProperty` будет выведено одно **только** свойство `moo`. Если же вы уберёте проверку `hasOwnProperty`, код станет нестабилен и, если кто-то всё же позволил себе изменить прототипы встроенных типов, такие как `Object.prototype`, вам грозят непредвиденные сюрпризы. -Один из самых популярных фреймворков [Prototype][1] как раз этим и славится, и если вы его подключаете, то не забудьте использовать `hasOwnProperty` внутри цикла `for in`, иначе у вас гарантированно возникнут проблемы. +Один из самых популярных фреймворков [Prototype][1] использует упомянутое расширение `Object.prototype` — и если вы его подключаете — ни в коем случае не забывайте использовать `hasOwnProperty` внутри всех циклов `for in` — иначе у вас гарантированно возникнут проблемы. ### Рекомендации -Рекомендация одна — **всегда** используйте `hasOwnProperty`. Пишите код, который будет в наименьшей мере зависеть от окружения, в котором он будет запущен — не стоит гадать, расширял кто-то прототипы или нет и используется ли в ней та или иная библиотека. +Рекомендация одна — **всегда** используйте `hasOwnProperty`. Пишите код, который будет в наименьшей мере зависеть от окружения, в котором он будет запущен — не стоит гадать, расширял кто-то прототипы или нет и используется ли в нём та или иная библиотека. [1]: http://www.prototypejs.org/ diff --git a/doc/ru/object/general.md b/doc/ru/object/general.md index aa5ad534..a12ef2f2 100644 --- a/doc/ru/object/general.md +++ b/doc/ru/object/general.md @@ -1,6 +1,6 @@ ## Объекты и их свойства -В JavaScript всё ведет себя, как объект, лишь за двумя исключениями — [`null`](#core.undefined) и [`undefined`](#core.undefined). +В JavaScript все значения ведут себя как объекты, лишь за двумя исключениями — [`null`](#core.undefined) и [`undefined`](#core.undefined). false.toString(); // 'false' [1, 2, 3].toString(); // '1,2,3' @@ -9,19 +9,19 @@ Foo.bar = 1; Foo.bar; // 1 -Неверно считать, что числовые литералы нельзя использовать в качестве объектов — это распространённое заблуждение. Его причиной является упущение в парсере JavaScript, благодаря которому применение *точечной нотации* к числу воспринимается им как литерал числа с плавающей точкой. +Среди программистов на JavaScript распростанено заблуждение, что числовые литералы нельзя использовать в роли объектов — оно является неверным и зародилось по причине известного упущения в парсере JavaScript, благодаря которому применение *точечной нотации* к числу воспринимается им как литерал числа с плавающей точкой. 2.toString(); // вызывает SyntaxError -Есть несколько способов обойти этот недостаток и любой из них можно использовать для того, чтобы работать с числами, как с объектами: +Есть несколько способов обойти этот недостаток, и любой из них подойдёт, когда вам действительно нужно добиться поведения объекта от числового значения: 2..toString(); // вторая точка распознаётся корректно 2 .toString(); // обратите внимание на пробел перед точкой (2).toString(); // двойка вычисляется заранее -### Объекты как тип данных +### Объекты как хранилища данных -Объекты в JavaScript могут использоваться как [*хеш-таблицы*][1]: подавляющей частью состоят из именованных свойств (ключей), привязанных к значениям. +Объекты в JavaScript могут использоваться и как [*хеш-таблицы*][1]: большей частью они состоят из именованных свойств (ключей), привязанных к соответствующим значениям. Используя объектный литерал — нотацию `{}` — можно создать простой объект. Новый объект [наследуется](#object.prototype) от `Object.prototype` и не имеет [собственных свойств](#object.hasownproperty). @@ -44,13 +44,13 @@ foo.1234; // SyntaxError foo['1234']; // работает -Обе нотации идентичны по принципу работы — одна лишь разница в том, что использование квадратных скобок позволяет устанавливать свойства динамически и использовать такие имена свойств, какие в других случаях могли бы привести к синтаксической ошибке. +Обе нотации идентичны по принципу работы; разница между ними лишь в том, что использование квадратных скобок позволяет устанавливать свойства динамически и использовать такие имена свойств, какие в других случаях могли бы привести к синтаксической ошибке. ### Удаление свойств -Единственный способ удалить свойство у объекта — использовать оператор `delete`; устанавливая свойство в `undefined` или `null`, вы только заменяете связанное с ним *значение*, но не удаляете *ключ*. +Единственный способ полностью удалить свойство у объекта — использовать оператор `delete`; устанавливая свойство в `undefined` или `null`, вы только заменяете связанное с ним *значение*, но не удаляете *ключ*. -> **Замечание** от перев.: Если ссылок на значение больше нет, то сборщиком мусора удаляется и само значение, но ключ объекта при этом всё так же имеет новое значение. +> **Замечание** от перев.: Если ссылок на значение больше нет, то сборщиком мусора удаляется и само значение, но ключ объекта при этом всё также указывает на новое значение. var obj = { bar: 1, @@ -67,20 +67,20 @@ } } -Приведённый код выведет две строки: `bar undefined` и `foo null` — на самом деле удалено было только свойство `baz` и посему только оно будет отсутствовать в выводе. +Приведённый код выведет две строки — `bar undefined` и `foo null`: на самом деле удалено было только свойство `baz` и поэтому лишь оно будет отсутствовать в выводе. ### Запись ключей var test = { 'case': 'Я — ключевое слово, поэтому меня надо записывать строкой', - delete: 'Я тоже ключевое слово, так что я' // не является ошибкой, бросает SyntaxError только в версиях ECMAScript ниже 5ой версии + delete: 'Я тоже ключевое слово, и меня' // не является ошибкой, бросает SyntaxError только в версиях ECMAScript ниже 5ой версии }; -Свойства объектов могут записываться как явно символами, так и в виде закавыченных строк. В связи с другим упущением в парсере JavaScript, этот код выбросит `SyntaxError` во всех версиях ранее ECMAScript 5. +Ключи для свойств объектов могут записываться как посимвольно без кавычек, так и в виде закавыченных строк. В связи с другим упущением в парсере JavaScript, вышеприведённый код породит `SyntaxError` во всех версиях ранее ECMAScript 5. -Источником ошибки является факт, что `delete` — это *ключевое слово* и поэтому его необходимо записывать как *строчный литерал*: ради уверенности в том, что оно будет корректно опознано более старыми движками JavaScript. +Источником ошибки является факт, что `delete` — это *ключевое слово* и поэтому его *необходимо* записывать как *строчный литерал*, хотя бы ради уверенности, что оно будет корректно опознано более старыми движками JavaScript. -*От перев.:* И еще один пример в пользу строковой нотации, это относится к [JSON][2]: +*От перев.:* Дополнительный пример в пользу строковой нотации, это относится к [JSON][2]: // валидный JavaScript и валидный JSON { diff --git a/doc/ru/object/hasownproperty.md b/doc/ru/object/hasownproperty.md index 89b950ca..dc8b3d9a 100644 --- a/doc/ru/object/hasownproperty.md +++ b/doc/ru/object/hasownproperty.md @@ -1,12 +1,12 @@ ## Функция `hasOwnProperty` -Если вам необходимо проверить, определено ли свойство у *самого объекта*, а **не** в его [цепочке прототипов](#object.prototype), вы можете использовать метод `hasOwnProperty`, который все объекты наследуют от `Object.prototype`. +Если вам необходимо проверить, определено ли свойство у *самого объекта*, а **не** где-то в его [цепочке прототипов](#object.prototype), вы можете использовать метод `hasOwnProperty`, который все объекты наследуют от `Object.prototype`. -> **Примечание:** Для проверки наличия свойства **недостаточно** проверять, эквивалентно ли оно `undefined`. Свойство может вполне себе существовать, но при этом ему может быть присвоено значение `undefined`. +> **Примечание:** Для проверки существования свойства **недостаточно** проверять, эквивалентно ли оно `undefined`. Свойство может вполне себе существовать, но при этом ему может быть присвоено значение `undefined`. -`hasOwnProperty` — единственная функция в JavaScript, которая позволяет получить свойства объекта **без обращения** к цепочке его прототипов. +`hasOwnProperty` — единственная функция в JavaScript, которая помогает получать свойства объекта **без обращения** к цепочке его прототипов. - // испортим Object.prototype + // Подпортим Object.prototype Object.prototype.bar = 1; var foo = {goo: undefined}; @@ -16,11 +16,11 @@ foo.hasOwnProperty('bar'); // false foo.hasOwnProperty('goo'); // true -Только используя `hasOwnProperty` можно гарантировать правильный результат при переборе свойств объекта. И **нет** иного способа для определения свойств, которые определены в *самом* объекте, а не где-то в цепочке его прототипов. +Только используя `hasOwnProperty` можно гарантировать правильный результат при переборе свойств объекта в циклах. И **нет** иного способа для отделения свойств, которые определены в *самом* объекте, а не где-либо в цепочке его прототипов. ### `hasOwnProperty` как свойство -JavaScript **не** резервирует свойство с именем `hasOwnProperty`. Так что, если есть потенциальная возможность, что объект может содержать свойство с таким именем, требуется использовать *внешний* вариант функции `hasOwnProperty` чтобы получить корректные результаты. +JavaScript **не** резервирует свойство с именем `hasOwnProperty`. Так что, если есть потенциальная возможность, что объект может содержать свойство с таким именем — чтобы получить гарантированно ожидаемый результат, требуется использовать *внешний* вариант функции `hasOwnProperty`. var foo = { hasOwnProperty: function() { @@ -35,6 +35,12 @@ JavaScript **не** резервирует свойство с именем `has // и передаём foo в качестве this ({}).hasOwnProperty.call(foo, 'bar'); // true + // Для этих целей также можно использовать функцию hasOwnProperty из прототипа Object + Object.prototype.hasOwnProperty.call(foo, 'bar'); // true + +*От перев.:* Обратите внимание, что последний способ в примере не создаёт новых объектов + ### Заключение -**Единственным** способом проверить существование свойства у объекта является использование метода `hasOwnProperty`. При этом рекомендуется использовать этот метод в **каждом** [цикле `for in`](#object.forinloop) вашего проекта, чтобы избежать возможных ошибок с ошибочным заимствованием свойств из [прототипов](#object.prototype) родительских объектов. Также вы можете использовать конструкцию `{}.hasOwnProperty.call(...)` на случай, если кто-то вздумает расширить [прототипы](#object.prototype) встроенных объектов. +**Единственным** надёжным способом проверить существование свойства у объекта является использование метода `hasOwnProperty`. Рекомендуется использовать этот метод в **любом** [цикле `for in`](#object.forinloop) вашего проекта, дабы избежать потенциальных ошибок с неверным заимствованием свойств из [прототипов](#object.prototype) встроенных объектов. Кроме этого, вы можете использовать конструкцию `{}.hasOwnProperty.call(...)` на случай, если кто-то вздумает расширить [прототипы](#object.prototype) встроенных объектов. + diff --git a/doc/ru/object/prototype.md b/doc/ru/object/prototype.md index a8e75129..cab07941 100644 --- a/doc/ru/object/prototype.md +++ b/doc/ru/object/prototype.md @@ -2,13 +2,13 @@ В JavaScript отсутствует классическая модель наследования — вместо неё используется [*прототипная модель*][1]. -Хотя её часто расценивают как один из недостатков JavaScript, на самом деле прототипная модель наследования намного мощнее классической. К примеру, поверх неё можно предельно легко реализовать классическое наследование, а попытки совершить обратное вынудят вас попотеть. +Хотя её и причисляют к недостаткам JavaScript, на самом деле прототипная модель наследования оказывается *мощнее* классической. К примеру, поверх неё можно предельно легко реализовать классическое наследование, а попытки совершить обратное вынудят вас попотеть. Из-за того, что JavaScript — практически единственный широко используемый язык с прототипным наследованием, придётся потратить некоторое время на осознание различий между этими двумя моделями. Первое важное отличие заключается в том, что наследование в JavaScript выполняется с использованием так называемых *цепочек прототипов*. -> **Замечание:** В результате выполнения конструкции `Bar.prototype = Foo.prototype` оба объекта будут делить друг с другом **один и тот же** прототип. Так что изменение прототипа одного из объектов повлечёт за собой изменение прототипа другого и наоборот — вряд ли это окажется тем, чего вы ожидали. +> **Замечание:** При использовании конструкции `Bar.prototype = Foo.prototype` оба объекта будут делить друг с другом **один и тот же** прототип. Так что изменение прототипа одного из объектов повлечёт за собой изменение прототипа другого и наоборот — вряд ли это то, чего вы ожидали. > **Замечание:** Для объявления наследования вместо `Bar.prototype = Object.create(Foo.prototype)` можно воспользоваться конструкций `Bar.prototype = new Foo()`, но у нее есть пару недостатков: 1) как правило требуется унаследовать только методы и свойства прототипа, а не создавать для этого новый объект; 2) создание объекта может требовать обязательные аргументы. @@ -25,7 +25,7 @@ Bar.prototype = Object.create(Foo.prototype); Bar.prototype.foo = 'Hello World'; - // Убедимся, что Bar является действующим конструктором + // Убедимся, что Bar является настоящим конструктором Bar.prototype.constructor = Bar; var test = new Bar() // создадим новый экземпляр bar @@ -39,31 +39,31 @@ Object.prototype { toString: ... /* и т.д. */ } -В приведённом коде объект `test` наследует оба прототипа: `Bar.prototype` и `Foo.prototype`; следовательно, он имеет доступ к функции `method` которую мы определили в прототипе `Foo`. Также у него есть доступ к свойству `value` **одного уникального** экземпляра `Foo`, который является его прототипом. Важно заметить, что код `new Bar()` **не** создаёт новый экземпляр `Foo`, а повторно вызывает функцию, которая была назначена его прототипом: таким образом все новые экземпляры `Bar` будут иметь **одинаковое** свойство `value`. +В приведённом коде объект `test` будет наследовать оба прототипа: `Bar.prototype` и `Foo.prototype`; следовательно, у него будет доступ к функции `method`, которую мы определили в прототипе `Foo`. Также, у него будет доступ к свойству `value` **одного уникального** экземпляра `Foo`, который является его прототипом. Важно заметить, что код `new Bar()` при вызове **не создаёт** новый экземпляр `Foo`, а повторно вызываеи функцию, которая была назначена его (`Bar`) прототипом: таким образом, все новые экземпляры `Bar` будут иметь **одно и то же** свойство `value` (_прим. перев._ — то есть все ссылки по имени `value`, во всех экземплярах `Bar`, будут указывать на одно и то же место в памяти). -> **Замечание:** Никогда **не** используйте конструкцию `Bar.prototype = Foo`, поскольку ссылка будет указывать не на прототип `Foo`, а на объект функции `Foo`. Из-за этого цепочка прототипов будет проходить через `Function.prototype`, а не через `Foo.prototype` и в результате функция `method` не будет содержаться в цепочке прототипов. +> **Замечание:** Никогда **не используйте** конструкцию `Bar.prototype = Foo`, поскольку ссылка будет указывать не на прототип `Foo`, а на объект-функцию `Foo`. Из-за этого цепочка прототипов будет проходить через `Function.prototype`, а не через `Foo.prototype`, в результате чего функция `method` вообще не будет содержаться в цепочке прототипов. ### Поиск свойств -При обращении к какому-либо свойству объекта, JavaScript проходит **вверх** по цепочке прототипов этого объекта, пока не найдет свойство c запрашиваемым именем. +При обращении к какому-либо свойству объекта, движок JavaScript проходит **вверх** по цепочке прототипов этого объекта, пока не найдет свойство c запрашиваемым именем. -Если он достигнет верхушки этой цепочки (`Object.prototype`) и при этом так и не найдёт указанное свойство, вместо него вернётся значение [undefined](#core.undefined). +Если он достигнет верхушки этой цепочки (а именно `Object.prototype`), и при этом так и не найдёт указанное свойство, вместо него вернётся значение [undefined](#core.undefined). ### Свойство `prototype` -То, что свойство `prototype` используется языком для построения цепочек прототипов, даёт нам возможность присвоить **любое** значение этому свойству. Однако обычные примитивы, если назначать их в качестве прототипа, будут просто-напросто игнорироваться. +Тот факт, что свойство `prototype` используется языком для построения цепочек прототипов, даёт нам возможность присвоить **любое** значение этому свойству. Впрочем, обычные примитивы, если назначать их в качестве прототипов, будут просто-напросто игнорироваться. function Foo() {} - Foo.prototype = 1; // ничего не произойдёт + Foo.prototype = 1; // никакого эффекта Foo.prototype = { "foo":"bar" - }; + }; // это сработает -При этом присвоение объектов, как в примере выше, позволит вам динамически создавать цепочки прототипов. +Но присвоение объектов, как в примерах здесь и выше, работает, и позволяет вам создавать цепочки прототипов динамически. ### Производительность -Поиск свойств, располагающихся относительно высоко по цепочке прототипов, может негативно сказаться на производительности, особенно в критических местах кода. Если же мы попытаемся найти несуществующее свойство, то поиск будет осуществлён вообще по всей цепочке, со всеми вытекающими последствиями. +Поиск свойств, располагающихся относительно высоко по цепочке прототипов, может негативно сказаться на производительности, особенно в критичных к ней местах кода. Если же мы попытаемся найти несуществующее свойство, поиск будет осуществлён по всей цепочке вообще, до самого глубокого конца — со всеми вытекающими последствиями. Вдобавок, при [циклическом переборе](#object.forinloop) свойств объекта, будет обработано **каждое** свойство, существующее в цепочке прототипов. @@ -71,13 +71,13 @@ Часто встречается неверное применение прототипов — расширение прототипа `Object.prototype` или прототипов одного из встроенных объектов JavaScript. -Подобная практика нарушает принцип *инкапсуляции* и имеет соответствующее название — [monkey patching][2]. К сожалению, в основу многих широко распространенных фреймворков, например Prototype, положен принцип изменения базовых прототипов. Вам же стоит запомнить — от хорошей жизни прототипы встроенных объектов не меняют. +Подобная практика нарушает принцип *инкапсуляции* и имеет соответствующее название — [monkey patching][2]. К сожалению, в основу многих широко распространенных фреймворков, например Prototype, положен принцип изменения базовых прототипов. На самом деле — до сих пор не известно разумных причин примешивать во встроенные типы *нестандартную функциональность*. **Единственным** оправданием для расширения встроенных прототипов может быть только воссоздание возможностей более новых движков JavaScript, например функции [`Array.forEach`][4], которая появилась в версии 1.6. ### Заключение -Перед тем, как вы приступите к разработке сложных приложений на JavaScript, вы **должны** полностью осознать как работают прототипы, и как организовывать наследование на их основе. Также, помните о зависимости между длиной цепочек прототипов и производительностью — разрывайте их при необходимости. Кроме того — **никогда** не расширяйте прототипы встроенных объектов (ну, если только для совместимости с новыми возможностями Javascript). +Перед тем, как вы приступите к разработке сложных приложений на JavaScript с использованием прототипов, вы **должны** полностью осознать как работают прототипные цепочки, и как организовывать наследование на их основе. Также, помните о зависимости между длиной цепочек прототипов и производительностью — разрывайте их при необходимости. Кроме того — **никогда** не расширяйте прототипы встроенных объектов, если вы не делаете это для совместимости с новыми возможностями Javascript. [1]: http://ru.wikipedia.org/wiki/%D0%9F%D1%80%D0%BE%D1%82%D0%BE%D1%82%D0%B8%D0%BF%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5 [2]: http://en.wikipedia.org/wiki/Monkey_patch diff --git a/doc/ru/other/timeouts.md b/doc/ru/other/timeouts.md index d5eb03b1..79d63110 100644 --- a/doc/ru/other/timeouts.md +++ b/doc/ru/other/timeouts.md @@ -1,17 +1,17 @@ ### `setTimeout` и `setInterval` -Поскольку JavaScript поддерживает асинхронность, есть возможность запланировать выполнение функции, используя функции `setTimeout` и `setInterval`. +В виду того, что JavaScript умеет совершать асинхронные операции, есть возможность запланировать отложенное выполнение пользовательской функции с помощью предназначенных для этого функций `setTimeout` и `setInterval`. -> **Замечание:** Таймауты **не** являются частью стандарта ECMAScript, они были разработаны как раздел спецификации [DOM][1]. +> **Замечание:** Таймауты **не** являются частью стандарта ECMAScript, они были разработаны будучи частью спецификации[DOM][1]. function foo() {} var id = setTimeout(foo, 1000); // возвращает число > 0 -Функция `setTimeout` возвращает идентификатор таймаута и планирует вызвать `foo` через, **примерно**, тысячу миллисекунд. Функция `foo` при этом будет вызвана ровно **один** раз. +Функция `setTimeout` возвращает идентификатор назначенного таймаута и откладывает вызов `foo` на, **примерно**, тысячу миллисекунд. Функция `foo`, при этом, будет вызвана ровно **один** раз. В зависимости от разрешения таймера в используемом для запуска кода движке JavaScript, а также с учётом того, что JavaScript является однопоточным языком и посторонний код может заблокировать выполнение потока, нет **никакой** гарантии, что переданный код будет выполнен ровно через указанное в вызове `setTimeout` время. -Переданная первым параметром функция будет вызвана как *глобальный объект* — это значит, что оператор [`this`](#function.this) в вызываемой функции будет ссылаться на этот самый объект. +Функция, переданная первым параметром, будет вызвана в контексте *глобального объекта* — это значит, что оператор [`this`](#function.this) в вызываемой функции будет ссылаться на тот самый глобальный объект. function Foo() { this.value = 42; @@ -26,56 +26,69 @@ > **Замечание:** Поскольку `setTimeout` принимает **объект функции** в качестве первого параметра, часто совершается ошибка в использовании `setTimeout(foo(), 1000)`, при котором будет использоваться **возвращённое значение** от вызова функции `foo`, а **не** вызываться сама функция `foo`. В большинстве случаев ошибка пройдёт незамеченной, а в случае если функция возвращает `undefined`, `setTimeout` вообще **не** породит никакой ошибки. -### Поочерёдные вызовы с использованием `setInterval` +### Очереди вызовов с `setInterval` -`setTimeout` вызывает функцию единожды; `setInterval` — как и предполагает название — вызывает функцию **каждые** `X` миллисекунд. И его использование не рекомендуется. +`setTimeout` вызывает функцию единожды; `setInterval` — как и предполагает название — вызывает функцию **каждые** `X` миллисекунд. И использовать его не рекомендуется. -В то время, когда исполняющийся код будет блокироваться во время вызова с таймаутом, `setInterval` будет продолжать планировать последующие вызовы переданной функции. Это может (особенно в случае небольших интервалов) повлечь за собой выстраивание вызовов функций в очередь. +В то время как, при использовании `setTimeout`, исполняющийся в данный момент код будет блокировать запланированный — `setInterval` продолжит планировать последующие вызовы переданной функции. При указании небольших интервалов это повлечь за собой выстраивание функций в ожидающую очередь. function foo(){ - // что-то, что выполняется одну секунду + // что-то, выполняющееся секунду или более } - setInterval(foo, 100); + setInterval(foo, 1000); -В приведённом коде `foo` выполнится один раз и заблокирует этим главный поток на одну секунду. +В приведённом примере `foo` в первый же раз заблокирует своим процессом главный поток на секунду. -Пока `foo` блокирует код, `setInterval` продолжает планировать последующие её вызовы. Теперь, когда первая `foo` закончила выполнение, в очереди будут уже **десять** ожидающих выполнения вызовов `foo`. +Пока `foo` блокирует код, `setInterval` продолжает планировать последующие её вызовы. Теперь, когда первая `foo` закончила выполнение, в очереди будет уже **десяток** ожидающих выполнения вызовов `foo`. + +(_прим. перев._ — Во времена написания и перевода этой документации не существовало функции `requestAnimationFrame`, которая не гарантирует постоянный интервал во времени, но гарантирует свободные ресурсы процессора на каждый вызов — сейчас для некоторых повторяющихся задач, даже не связанных с анимацией, используют именно её). ### Разбираемся с потенциальной блокировкой кода -Самый простой и контролируемый способ — использовать `setTimeout` внутри самой функции. +Самый простой и легко контролируемый способ — использовать `setTimeout` внутри такой функции. function foo(){ - // что-то, выполняющееся одну секунду - setTimeout(foo, 100); + // что-то, выполняющееся одну секунду или более + setTimeout(foo, 1000); } foo(); -Такой способ не только инкапсулирует вызов `setTimeout`, но и предотвращает от очередей блокирующих вызовов и при этом обеспечивает дополнительный контроль. Сама функция `foo` теперь принимает решение, хочет ли она запускаться ещё раз или нет. +Такой способ не только инкапсулирует вызов `setTimeout`, но и предотвращает от очередей блокирующих вызовов и при этом обеспечивает дополнительный контроль: сама функция `foo` теперь принимает решение, хочет ли она запускаться ещё раз или нет. -### Очистка таймаутов вручную +### Сброс таймаутов вручную -Удаление таймаутов и интервалов работает через передачу соответствующего идентификатора либо в функцию `clearTimeout`, либо в функцию `clearInterval` — в зависимости от того, какая функция `set...` использовалась для его получения. +Удалить таймаут или интервал можно посредством передачи соответствующего идентификатора либо в функцию `clearTimeout`, либо в функцию `clearInterval` — в зависимости от того, какая функция `set...` использовалась для его создания. var id = setTimeout(foo, 1000); clearTimeout(id); -### Очистка всех таймаутов +### Сброс всех таймаутов -Из-за того, что встроенного метода для удаления всех таймаутов и/или интервалов не существует, для достижения этой цели приходится использовать брутфорс. +Из-за того, что встроенного метода для удаления всех созданных таймаутов и/или интервалов не существует, даже ради просто приемлимого достижения этой цели приходится использовать силу. - // удаляем "все" таймауты + // сбрасываем «все» таймауты for(var i = 1; i < 1000; i++) { clearTimeout(i); } -Вполне могут остаться таймауты, которые не будут захвачены этим произвольным числом; так что всё же рекомендуется следить за идентификаторами всех создающихся таймаутов, за счёт чего их можно будет удалять индивидуально. +Впрочем, с таким кодом, вполне могут остаться «в живых» таймауты, которые _настолько_ произвольное число не сможет охватить. + +Есть и другой способ воплотить желаемое — условиться, что значения ID, выдающихся таймаутам, постоянно увеличиваются — с каждым новым вызовом `setTimeout`. + + // сбрасываем «все» таймауты + var biggestTimeoutId = window.setTimeout(function(){}, 1), + i; + for(i = 1; i <= biggestTimeoutId; i++) { + clearTimeout(i); + } + +Однако, даже при том, что такой код работает сегодня во всех современных браузерах, нигде не указано и не гарантируется, что значения ID всегда увеличиваются. Поэтому, всё же, рекомендуется следить за *каждым* идентификатором *каждого* создающегося таймаута — это позволит вам уверенно контролировать процесс, сбрасывая их индивидуально. ### Скрытое использование `eval` -`setTimeout` и `setInterval` могут принимать строку в качестве первого параметра. Эту возможность не следует использовать **никогда**, поскольку изнутри при этом производится скрытый вызов `eval`. +`setTimeout` и `setInterval` могут принимать строку в качестве первого параметра. Эту возможность не следует использовать **никогда** — по той лишь причине, что изнутри, при этом, производится скрытый вызов `eval`. -> **Замечание**: Поскольку функции работы с таймаутами **не** определены в стандарте ECMAScript, точная внутренняя механика их работы может различаться от движка к движку. Известно, что Microsoft JScript использует конструктор `Function` вместо `eval`. +> **Замечание**: Поскольку функции работы с таймаутами **не** определены в стандарте ECMAScript, точная внутренняя механика их работы может различаться от движка к движку. Например, Microsoft JScript использует конструктор `Function` вместо `eval`. function foo() { // будет вызвана @@ -89,9 +102,9 @@ } bar(); -Поскольку `eval` в этом случае не вызывается [напрямую](#core.eval), переданная в `setTimeout` строка будет выполнена в *глобальной области видимости*; так что локальная переменная `foo` из области видимости `bar` не будет выполнена. +Поскольку `eval` в этом случае не вызывается [явно](#core.eval), переданная в `setTimeout` строка будет выполнена в *глобальной области видимости*; так что локальная функция `foo` из области видимости `bar` вообще не будет выполнена. -По этим же причинам рекомендуется **не** использовать строку для передачи аргументов в функцию, которая должна быть вызвана из одной из двух функций, работающих с таймаутами. +По этим же причинам **не рекомендуется** использовать строковое представление вызова функции для передачи аргументов в функцию, которая должна быть вызвана одним из двух известных способов назначения таймаутов. function foo(a, b, c) {} @@ -103,13 +116,13 @@ foo(1, 2, 3); }, 1000) -> **Замечание:** При том, что синтаксис `setTimeout(foo, 1000, 1, 2, 3)` разрешено использовать, это крайне не рекомендуется, поскольку может привести к сложно распознаваемым ошибкам при работе с [методами](#function.this). +> **Замечание:** При том, что практически возможно использовать синтаксис `setTimeout(foo, 1000, 1, 2, 3)`, делать это крайне не рекомендуется — такие вызовы могут привести к трудно-отлавливаемым ошибкам при операциях с [методами](#function.this). ### Заключение -**Никогда** не используйте строки как параметры `setTimeout` или `setInterval`. Это явный признак **действительно** плохого кода. Если вызываемой функции необходимо передавать аргументы, лучше передавать *анонимную функцию*, которая самостоятельно будет отвечать за сам вызов. +**Никогда** не используйте строки как параметры для `setTimeout` или `setInterval`. Это явный признак **действительно** плохого кода. Если вызываемой функции необходимо передать аргументы, лучше передавать *анонимную функцию*, которая самостоятельно будет отвечать за сам вызов. -Кроме того, избегайте использования `setInterval` в случаях, когда его планировщик может блокировать выполнение JavaScript. +Кроме того, избегайте использования `setInterval`, поскольку его планировщик не блокируется выполняемым кодом. -[1]: http://ru.wikipedia.org/wiki/Document_Object_Model +[1]: http://ru.wikipedia.org/wiki/Document_Object_Model "Document Object Model" diff --git a/doc/ru/types/casting.md b/doc/ru/types/casting.md index 8e77c79d..7bd47f8f 100644 --- a/doc/ru/types/casting.md +++ b/doc/ru/types/casting.md @@ -1,37 +1,37 @@ ## Приведение типов -JavaScript — *слабо типизированный* язык, поэтому *преобразование типов* будет применяться **везде**, где возможно. +JavaScript — язык, в котором господствует *слабая типизация*, поэтому *преобразование типов* будет применяться **везде**, где только возможно. - // Эти равенства — истинны + // Эти равенства возвращают true new Number(10) == 10; // объект типа Number преобразуется // в числовой примитив в результате неявного вызова // метода Number.prototype.valueOf - 10 == '10'; // Strings преобразуется в Number + 10 == '10'; // Строки преобразуются в Number 10 == '+10 '; // Ещё чуток строко-безумия 10 == '010'; // и ещё isNaN(null) == false; // null преобразуется в 0, - // который конечно же не NaN + // который, конечно же, не NaN - // Эти равенства — ложь + // Эти равенства возвращают false 10 == 010; 10 == '-10'; -> **ES5 Замечание:** Числовые литералы, которые начинаются с 0, интерпретируются как восьмеричные (Base 8). В ECMAScript 5 strict mode **удалена** поддержка восьмеричной системы. +> **Замечание по ES5:** Числовые литералы, которые начинаются с 0, интерпретируются как восьмеричные (Base 8). В ECMAScript 5 strict mode **удалена** поддержка восьмеричной системы. -Для того, чтобы избежать этого, **настоятельно** рекомендуется использовать [оператор строгого равенства](#types.equality). Впрочем, хотя это и позволяет избежать многих распространенных ошибок, существует ещё много дополнительных вопросов, которые возникают из-за слабости типизации JavaScript. +Для того, чтобы избежать такого поведения, **настоятельно** рекомендуется использовать [оператор строгого равенства](#types.equality). Впрочем, хотя это и позволит избежать многих распространенных ошибок, существует ещё множество дополнительных проблем, возникающих по вине слабой типизации JavaScript. ### Конструкторы встроенных типов Конструкторы встроенных типов, например, `Number` и `String` ведут себя различным образом, в зависимости от того, вызываются они с ключевым словом `new` или без. - new Number(10) === 10; // False, Object и Number - Number(10) === 10; // True, Number и Number - new Number(10) + 0 === 10; // True, из-за неявного преобразования + new Number(10) === 10; // False: Object и Number + Number(10) === 10; // True: Number и Number + new Number(10) + 0 === 10; // True: из-за неявного преобразования -Использование встроенного типа, такого как `Number`, в качестве конструктора создаёт новый экземпляр объекта Number, но при использовании без ключевого слова `new` функция `Number` будет вести себя как конвертер. +Использование встроенных типов, таких как `Number`, с конструктором, создаёт новый экземпляр объекта `Number`, но использование их же без ключевого слова `new`, создаёт функцию `Number`, которая будет вести себя в равенствах в роли «преобразователя». -Кроме того, присутствие литералов или переменных, которые не являются объектами, приведет к еще большему насилию над типами. +Кроме того, присутствие в равенствах дополнительных литералов или переменных, которые не являются объектами, повлечёт за собой лишь ещё большее количество бессмысленных преобразований типов. Лучший вариант — это **явное** приведение к одному из трех возможных типов. @@ -39,17 +39,17 @@ JavaScript — *слабо типизированный* язык, поэтом '' + 10 === '10'; // true -Путём добавления в начале пустой строки, значение легко приводится к строке. +Путём добавления в начало пустой строки, значение легко приводится к строке. ### Приведение к числовому типу +'10' === 10; // true -Используя **унарный** оператор плюс, можно преобразовать значение в число. +Применив **унарный** оператор плюс, можно преобразовать значение в число. ### Приведение к булеву типу -Используя оператор **not** (**`!`**) дважды, значение может быть приведено к логическому (булеву) типу. +Использование оператора **not** (`!`) дважды поможет привести значение к логическому (булеву) типу. !!'foo'; // true !!''; // false diff --git a/doc/ru/types/equality.md b/doc/ru/types/equality.md index a7255ca7..624ff851 100644 --- a/doc/ru/types/equality.md +++ b/doc/ru/types/equality.md @@ -1,12 +1,12 @@ -##  Равенство и сравнение +##  Равенство и сравнения -JavaScript имеет 2 различных способа сравнения значений объектов на равенство. +JavaScript умеет сравнивать значения объектов на равенство двумя различными способами. ### Оператор сравнения Оператор сравнения состоит из **двух** символов равенства: `==` -*Слабая типизированность* языка JavaScript подразумевает **приведение** обеих переменных к **одному типу** для того, чтобы произвести сравнение. +Под *слабой типизацией* языка JavaScript подразумевается **приведение** обеих переменных к **одному типу** при сравнении двух объектов. "" == "0" // false 0 == "" // true @@ -18,15 +18,15 @@ JavaScript имеет 2 различных способа сравнения з null == undefined // true " \t\r\n" == 0 // true -В таблице выше показаны результаты приведения типов и это главная причина, почему использование `==` повсеместно считается плохой практикой: оно приводит к трудностям в отслеживании ошибок из-за сложных правил преобразования типов. +В таблице выше показаны результаты приведения типов в разных ситуациях и показана главная причина, по которой использование `==` повсеместно считается плохой практикой: «благодаря» непредсказуемости правил преобразования типов, становится очень трудно искать причины возникновения ошибок. -Кроме того, приведение типов во время сравнения также влияет на производительность; например, строка должна быть преобразована в число перед сравнением с другим числом. +Кроме того, приведение типов во время сравнения вынужденно влияет на производительность; например, строка должна быть преобразована в число перед сравнением с другим числом. ### Оператор строгого равенства Оператор строгого равенства состоит из **трёх** символов равенства: `===` -В отличие от обычного оператора равенства, оператор строгого равенства **не** выполняет приведение типов между операндами. +Он работает так же, как и обычный оператор сравнения, но оператор строгого равенства **не** выполняет приведения типов между своими операндами. "" === "0" // false 0 === "" // false @@ -38,11 +38,11 @@ JavaScript имеет 2 различных способа сравнения з null === undefined // false " \t\r\n" === 0 // false -Результаты выше более понятны и позволяют быстрее выявлять ошибки в коде. Это в определённой степени улучшает код, а также дает прирост производительности в случае, если операнды имеют различные типы. +Результаты выше немного более предсказуемы и помогают быстрее выявлять ошибки в коде. Использование этого оператора в определённой степени делает код надёжнее, а кроме того обспечивает прирост производительности в случае, если типы операндов различны. ### Сравнение объектов -Хотя оба оператора `==` и `===` заявлены как операторы равенства, они ведут себя по-разному, когда хотя бы один из операндов является `Object`. +Хотя оба оператора `==` и `===` заявлены как операторы равенства, они ведут себя по-разному, когда хотя бы одним из операндов оказывается `Object`. {} === {}; // false new String('foo') === 'foo'; // false @@ -50,9 +50,8 @@ JavaScript имеет 2 различных способа сравнения з var foo = {}; foo === foo; // true -Здесь оба операнда сравниваются на **идентичность**, а **не** на равенство; то есть будет проверяться, являются ли операнды одним **экземпляром** объекта, так же как делает `is` в Python и сравниваются указатели в С. +Здесь оба операнда сравниваются на **идентичность**, а **не** на равенство; то есть будет проверяться, являются ли операнды одним и тем же **экземпляром** объекта — так же, как делает `is` в Python или сравниваются указатели в С. ### Заключение -Крайне рекомендуется использовать только операторы **строгого равенства**. В случае, когда намечается преобразование типов, нужно сделать [явное приведение](#types.casting) и не оставлять их на совести языковых хитростей с преобразованиями. - +Крайне рекомендуется использовать только операторы **строгого равенства**. В случаях, когда необходимо использовать преобразование типов, нужно сделать [явное приведение](#types.casting), а не оставлять его на совести нарочито мудрых языковых операций. diff --git a/doc/ru/types/instanceof.md b/doc/ru/types/instanceof.md index a19f2ce9..1a6a26fc 100644 --- a/doc/ru/types/instanceof.md +++ b/doc/ru/types/instanceof.md @@ -1,6 +1,6 @@ ## Оператор `instanceof` -Оператор `instanceof` сравнивает конструкторы двух операндов. Это полезно только когда сравниваются пользовательские объекты. Использование на встроенных типах почти так же бесполезно, как и [оператор typeof](#types.typeof). +Оператор `instanceof` сравнивает конструкторы двух операндов. Он работает правильно только тогда, когда сравниваются пользовательские объекты. Использование его на встроенных типах практически так же бесполезно, как и использование [оператора typeof](#types.typeof). ### Сравнение пользовательских объектов @@ -11,8 +11,8 @@ new Bar() instanceof Bar; // true new Bar() instanceof Foo; // true - // Всего лишь присваиваем Bar.prototype объект функции Foo, - // но не экземпляра Foo + // Всего-то лишь присваиваем Bar.prototype объект функции Foo, + // а не экземпляр Foo Bar.prototype = Foo; new Bar() instanceof Foo; // false @@ -24,9 +24,9 @@ 'foo' instanceof String; // false 'foo' instanceof Object; // false -Здесь надо отметить одну важную вещь: `instanceof` не работает на объектах, которые происходят из разных контекстов JavaScript (например, из различных документов в web-браузере), так как их конструкторы и правда не будут конструкторами *тех самых* объектов. +Здесь нужно отметить одну важную вещь: `instanceof` не работает с объектами, которые происходят из разных контекстов JavaScript (например, из различных документов в web-браузере), так как их конструкторы на самом деле не будут конструкторами *тех же самых* объектов, что справедливо. ### Заключение -Оператор `instanceof` должен использоваться **только** при обращении к пользовательским объектам, происходящим из одного контекста JavaScript. Так же, как и в случае оператора `typeof`, любого другого использования необходимо **избегать**. +Оператор `instanceof` должен использоваться **только** при обращении к пользовательским объектам, происходящим из одного контекста JavaScript. Также, как и в случае оператора `typeof`, любого другого использования `instanceof` необходимо **избегать**. diff --git a/doc/ru/types/typeof.md b/doc/ru/types/typeof.md index f19b62a6..2b032e70 100644 --- a/doc/ru/types/typeof.md +++ b/doc/ru/types/typeof.md @@ -1,10 +1,10 @@ ## Оператор `typeof` -Оператор `typeof` (вместе с [`instanceof`](#types.instanceof)) — это, вероятно, самая большая недоделка в JavaScript, поскольку, похоже, он **поломан более, чем полностью**. +Оператор `typeof` (вместе с [`instanceof`](#types.instanceof)) — это, вероятно, самая большая недоделка в JavaScript, поскольку с накоплением нашего опыта выясняется, что он **поломан, разве что не полностью**. -Хотя `instanceof` еще имеет ограниченное применение, `typeof` на самом деле имеет *только один* практический случай применения, который при всём при этом **не** является проверкой типа объекта. +Учитывая, что число потенциальных поводов для применения `instanceof` довольно ограничено, важно отметить, что `typeof` вообще имеет *один-единственный* практический случай применения, который при всём при этом, неожиданно,.. **не** оказывается проверкой типа объекта. -> **Замечание:** Хотя для вызова `typeof` также можно использовать синтаксис функции, т.е. `typeof(obj)`, на самом деле это не функция. Двойные круглые скобки будут работать нормально и возвращаемое значение будет использоваться как операнд оператора `typeof`. Но функции `typeof` — **не существует**. +> **Замечание:** Хотя для вызова `typeof` также можно использовать синтаксис функции, т.е. `typeof(obj)`, на самом деле это вовсе не функция. Да — двойные круглые скобки будут работать нормально и возвращаемое значение будет использоваться как операнд оператора `typeof`, но функции `typeof` — **не существует**. ### Таблица типов JavaScript @@ -26,13 +26,13 @@ {} Object object new Object() Object object -В таблице выше *Тип* представляет собой значение, возвращаемое оператором `typeof`. Как хорошо видно, это значение может быть абсолютно любым, но не логичным результатом. +В этой таблице в колонке *Тип* приводится значение, возвращаемое оператором `typeof` для указанного объекта. Как хорошо заметно, это значение может оказаться чем угодно, но не ожидавшимся результатом. -*Класс* представляет собой значение внутреннего свойства `[[Class]]` объекта. +В колонке *Класс* приведено значение внутреннего свойства объекта `[[Class]]`. > **Из спецификации:** Значением `[[Class]]` может быть одна из следующих строк: `Arguments`, `Array`, `Boolean`, `Date`, `Error`, `Function`, `JSON`, `Math`, `Number`, `Object`, `RegExp`, `String`. -Для того, чтобы получить значение `[[Class]]`, необходимо вызвать метод `toString` у `Object.prototype`. +Чтобы получить значение `[[Class]]`, нужно применить к интересующему объекту метод `toString` из прототипа `Object.prototype`. (_прим. перев._ — то есть не вызвать метод у самого объекта, а именно применить к нему метод из прототипа, см. ниже). ### Класс объекта @@ -46,19 +46,19 @@ is('String', 'test'); // true is('String', new String('test')); // true -В примере выше `Object.prototype.toString` вызывается со значением [this](#function.this), являющимся объектом, значение `[[Class]]` которого нужно получить. +В примере выше `Object.prototype.toString` вызывается со значением [this](#function.this), ссылающимся на объект, значение `[[Class]]` которого требуется получить. -> **ES5 Замечание:** Для удобства в ECMAScript 5 возвращаемое значение `Object.prototype.toString `для `null` и `undefined` было изменено с `Object` на `Null` и `Undefined` соответственно. +> **Замечание по ES5:** Для удобства, в ECMAScript 5 возвращаемое значение `Object.prototype.toString` для `null` и `undefined` было изменено с `Object` на `Null` и `Undefined` соответственно. ### Проверка переменных на определённость typeof foo !== 'undefined' -Выше проверяется, было ли `foo` действительно объявлено или нет; просто обращение к переменной приведёт к `ReferenceError`. Это единственное, чем на самом деле полезен `typeof`. +Данное выражение позволяет удостовериться, была ли объявлена переменная `foo`; явное обращение к несуществующей переменной в коде породит `ReferenceError`. И вот это — единственное, чем на самом деле полезен `typeof`. ### Заключение -Для проверки типа объекта настоятельно рекомендуется использовать` Object.prototype.toString` — это единственный надежный способ. Как показано выше в таблице типов, некоторые возвращаемые `typeof` значения не определены в спецификации: таким образом, они могут отличаться в различных реализациях. +Для проверки типа объекта настоятельно рекомендуется использовать `Object.prototype.toString` — это единственный и надежный способ. Как показано выше в таблице типов, некоторые значения, возвращаемые `typeof`, не описаны в спецификации: следовательно, они могут различаться в разных реализациях движка. -Кроме случая проверки, была ли определена переменная, `typeof` следует избегать **во что бы то ни стало**. +За исключением случаев проверки, была ли определена переменная, использования `typeof` следует избегать.