diff --git a/1-js/2-first-steps/15-switch/article.md b/1-js/2-first-steps/15-switch/article.md
index b94a6f9e..023f542c 100644
--- a/1-js/2-first-steps/15-switch/article.md
+++ b/1-js/2-first-steps/15-switch/article.md
@@ -2,7 +2,7 @@
Конструкция `switch` заменяет собой сразу несколько `if`.
-Это -- более наглядный способ сравнить выражение сразу с несколькими вариантами.
+Она представляет собой более наглядный способ сравнить выражение сразу с несколькими вариантами.
[cut]
## Синтаксис
@@ -44,7 +44,7 @@ switch(x) {
```js
//+ run
-var a = 2+2;
+var a = 2 + 2;
switch (a) {
case 3:
@@ -63,11 +63,13 @@ switch (a) {
}
```
-Будет выведено только одно значение, соответствующее `4`. После чего `break` прервёт выполнение.
+Здесь оператор `switch` последовательно сравнит `a` со всеми вариантами из `case`.
-**Если его не прервать -- оно пойдёт далее, при этом остальные проверки игнорируются.**
+Сначала `3`, затем -- так как нет совпадения -- `4`. Совпадение найдено, будет выполнен этот вариант, со строки `alert('В точку!')` и далее, до ближайшего `break`, который прервёт выполнение.
-Например:
+**Если `break` нет, то выполнение пойдёт ниже по следующим `case`, при этом остальные проверки игнорируются.**
+
+Пример без `break`:
```js
//+ run
@@ -87,7 +89,7 @@ switch (a) {
}
```
-В примере выше последовательно выполнятся три `alert`.
+В примере выше последовательно выполнятся три `alert`:
```js
alert('В точку!');
@@ -95,7 +97,7 @@ alert('Перебор');
alert('Я таких значений не знаю');
```
-**В `case` могут быть любые выражения**, в том числе включающие в себя переменные и функции.
+В `case` могут быть любые выражения, в том числе включающие в себя переменные и функции.
Например:
@@ -165,21 +167,19 @@ switch(arg) {
case 3:
alert('Никогда не выполнится');
- case null:
- alert('Отмена');
- break;
-
default:
alert('Неизвестное значение: ' + arg)
}
```
-Что оно выведет при вводе чисел 0, 1, 2, 3? Подумайте и *потом* читайте дальше...
+Что оно выведет при вводе числа 0? Числа 1? 2? 3?
+
+Подумайте, выпишите свои ответы, исходя из текущего понимания работы `switch` и *потом* читайте дальше...
-
При вводе `0` или `1` выполнится первый `alert`, далее выполнение продолжится вниз до первого `break` и выведет второй `alert('Два')`.
-
При вводе `2`, `switch` перейдет к `case '2'` и выведет `Два`.
-
**При вводе `3`, `switch` перейдет на `default`.** Это потому, что `prompt` возвращает строку `'3'`, а не число. Типы разные. `Switch` использует строгое равенство `===`, так что совпадения не будет.
-
При отмене сработает `case null`.
+
При вводе `0` выполнится первый `alert`, далее выполнение продолжится вниз до первого `break` и выведет второй `alert('Два')`. Итого, два вывода `alert`.
+
При вводе `1` произойдёт то же самое.
+
При вводе `2`, `switch` перейдет к `case '2'`, и сработает единственный `alert('Два')`.
+
**При вводе `3`, `switch` перейдет на `default`.** Это потому, что `prompt` возвращает строку `'3'`, а не число. Типы разные. Оператор `switch` предполагает строгое равенство `===`, так что совпадения не будет.
diff --git a/1-js/2-first-steps/16-function-basics/article.md b/1-js/2-first-steps/16-function-basics/article.md
index c1322276..6b85b993 100644
--- a/1-js/2-first-steps/16-function-basics/article.md
+++ b/1-js/2-first-steps/16-function-basics/article.md
@@ -65,13 +65,14 @@ alert(message); // <-- будет ошибка, т.к. переменная ви
```js
function count() {
+ // переменные i,j не будут уничтожены по окончании цикла
for (*!*var*/!* i=0; i<3; i++) {
*!*var*/!* j = i * 2;
}
*!*
- alert(i); // i=3, на этом значении цикл остановился
- alert(j); // j=4, последнее значение, на котором цикл сработал, было i=2
+ alert(i); // i=3, последнее значение i, при нём цикл перестал работать
+ alert(j); // j=4, последнее значение j, которое вычислил цикл
*/!*
}
```
@@ -114,13 +115,12 @@ showMessage(); // Привет, я Вася
```js
//+ run
-var *!*userName*/!* = 'Вася';
+var userName = 'Вася';
function showMessage() {
-*!*
userName = 'Петя'; // (1) присвоение во внешнюю переменную
-*/!*
- var message = 'Привет, я ' + *!*userName*/!*;
+
+ var message = 'Привет, я ' + userName;
alert(message);
}
@@ -133,13 +133,11 @@ alert(userName); // Петя, значение внешней переменно
Конечно, если бы внутри функции, в строке `(1)`, была бы объявлена своя локальная переменная `var userName`, то все обращения использовали бы её, и внешняя переменная осталась бы неизменной.
-[summary]
**Переменные, объявленные на уровне всего скрипта, называют *"глобальными переменными"*.**
-Делайте глобальными только те переменные, которые действительно имеют общее значение для вашего проекта.
+В примере выше переменная `userName` -- глобальная.
-Пусть каждая функция работает "в своей песочнице".
-[/summary]
+Делайте глобальными только те переменные, которые действительно имеют общее значение для вашего проекта, а нужные для решения конкретной задачи -- пусть будут локальными в соответствующей функции.
[warn header="Внимание: неявное объявление глобальных переменных!"]
@@ -167,7 +165,7 @@ alert(message); // Привет
Здесь опасность даже не в автоматическом создании переменной, а в том, что глобальные переменные должны использоваться тогда, когда действительно нужны "общескриптовые" параметры.
-Забыли `var` в одном месте, потом в другом -- в результате одна функция неожиданно поменяла глобальную переменную, которую использует другая. Возможна ошибка и потеря времени на поиск проблемы.
+Забыли `var` в одном месте, потом в другом -- в результате одна функция неожиданно поменяла глобальную переменную, которую использует другая. И поди разберись, кто и когда её поменял, не самая приятная ошибка для отладки.
[/warn]
В будущем, когда мы лучше познакомимся с основами JavaScript, в главе [](/closures), мы более детально рассмотрим внутренние механизмы работы переменных и функций.
@@ -201,7 +199,7 @@ showMessage('Маша', 'Как дела?');
//+ run
function showMessage(from, text) {
*!*
- from = '**' + from + '**'; // меняем локальную переменную (1)
+ from = '**' + from + '**'; // меняем локальную переменную from
*/!*
alert(from + ': ' + text);
}
@@ -210,32 +208,9 @@ var from = "Маша";
showMessage(from, "Привет");
-alert(from); // "Маша", без изменений, так как в строке (1) была изменена копия значения
+alert(from); // старое значение from без изменений, в функции была изменена копия
```
-Здесь есть небольшая тонкость при работе с объектами. Как мы помним, в переменной хранится ссылка на объект. Поэтому функция, получив параметр-объект, работает с самим этим объектом:
-
-Например, в коде ниже функция по ссылке меняет содержимое объекта `user`:
-
-```js
-//+ run
-function makeAdmin(user) {
- user.isAdmin = true;
-}
-
-var user = { name: "Вася" };
-
-makeAdmin(user);
-alert(user.isAdmin); // true
-```
-
-## Стиль объявления функций
-
-В объявлении функции есть правила для расстановки пробелов. Они отмечены стрелочками:
-
-
-
-Конечно, вы можете ставить пробелы и по-другому, но эти правила используются в большинстве JavaScript-фреймворков.
## Аргументы по умолчанию
Функцию можно вызвать с любым количеством аргументов.
diff --git a/1-js/2-first-steps/18-function-declaration-expression/article.md b/1-js/2-first-steps/17-function-declaration-expression/article.md
similarity index 67%
rename from 1-js/2-first-steps/18-function-declaration-expression/article.md
rename to 1-js/2-first-steps/17-function-declaration-expression/article.md
index 564b90aa..18db5aeb 100644
--- a/1-js/2-first-steps/18-function-declaration-expression/article.md
+++ b/1-js/2-first-steps/17-function-declaration-expression/article.md
@@ -2,8 +2,6 @@
В JavaScript функция является значением, таким же как строка или число.
-## Функция -- это значение
-
Как и любое значение, объявленную функцию можно вывести, вот так:
```js
@@ -41,14 +39,16 @@ sayHi(); // ошибка (4)
...Однако, в любой момент значение переменной можно поменять. При этом, если оно не функция, то вызов `(4)` выдаст ошибку.
-Обычные значения, такие как числа или строки, представляют собой *данные*. А функцию можно воспринимать как *действие*. Это действие, как правило, хранится в переменной, но его можно скопировать или переместить из неё.
+Обычные значения, такие как числа или строки, представляют собой *данные*. А функцию можно воспринимать как *действие*.
+
+Это действие можно запустить через скобки `()`, а можно и скопировать в другую переменную, как было продемонстрировано выше.
## Объявление Function Expression [#function-expression]
-Функцию можно создать и присвоить переменной в любом месте кода.
+Существует альтернативный синтаксис для объявления функции, который ещё более наглядно показывает, что функция -- это всего лишь разновидность значения переменной.
-Для этого используется объявление "Function Expression" (функциональное выражение), которое выглядит так:
+Он называется "Function Expression" (функциональное выражение) и выглядит так:
```js
//+ run
@@ -73,8 +73,8 @@ sayHi('Вася');
"Классическое" объявление функции, о котором мы говорили до этого, вида `function имя(параметры) {...}`, называется в спецификации языка "Function Declaration".
-
**Function Declaration** -- функция, объявленная в основном потоке кода.
-
**Function Expression** -- объявление функции в контексте какого-либо выражения, например присваивания.
+
*Function Declaration* -- функция, объявленная в основном потоке кода.
+
*Function Expression* -- объявление функции в контексте какого-либо выражения, например присваивания.
Несмотря на немного разный вид, по сути две эти записи делают одно и то же:
@@ -93,13 +93,7 @@ var sum = function(a, b) {
Оба этих объявления говорят интерпретатору: "объяви переменную `sum`, создай функцию с указанными параметрами и кодом и сохрани её в `sum`".
-**При этом название переменной, в которую записана функция, обычно называют "именем функции". Говорят: "функция sum". Но при этом имя к функции никак не привязано.**
-
-Это всего лишь имя переменной, в которой *в данный момент* находится функция.
-
-Функция может быть в процессе работы скрипта скопирована в другую переменную, а из этой удалена, передана в другое место кода, и так далее, как мы видели выше.
-
-**Основное отличие между ними: функции, объявленные как Function Declaration, создаются интерпретатором *до выполнения кода*.**
+**Основное отличие между ними: функции, объявленные как Function Declaration, создаются интерпретатором до выполнения кода.**
Поэтому их можно вызвать *до* объявления, например:
@@ -127,12 +121,13 @@ var sayHi = function(name) {
}
```
-Это из-за того, что JavaScript перед запуском кода ищет в нём Function Declaration (они начинаются в основном потоке с `function`) и обрабатывает их.
+Это из-за того, что JavaScript перед запуском кода ищет в нём Function Declaration (их легко найти: они не являются частью выражений и начинаются со слова `function`) и обрабатывает их.
-А Function Expression создаются при выполнении выражения, в котором созданы, в данном случае -- функция будет создана при операции присваивания `sayHi = ...`.
+А Function Expression создаются в процессе выполнении выражения, в котором созданы, в данном случае -- функция будет создана при операции присваивания `sayHi = function...`
-**Как правило, возможность Function Declaration вызвать функцию до объявления -- это удобно, так как даёт больше свободы в том, как организовать свой код.**
+Как правило, возможность Function Declaration вызвать функцию до объявления -- это удобно, так как даёт больше свободы в том, как организовать свой код.
+Можно расположить функции внизу, а их вызов -- сверху или наоборот.
### Условное объявление функции [#bad-conditional-declaration]
@@ -142,7 +137,7 @@ var sayHi = function(name) {
```js
//+ run
-var age = 20;
+var age = +prompt("Сколько вам лет?", 20);
if (age >= 18) {
function sayHi() { alert('Прошу вас!'); }
@@ -153,15 +148,7 @@ if (age >= 18) {
sayHi();
```
-[smart header="Зачем условное объявление?"]
-Конечно, можно произвести проверку условия внутри функции.
-
-Но вынос проверки вовне даёт очевидный выигрыш в производительности в том случае, когда проверку достаточно произвести только один раз, и её результат никогда не изменится.
-
-Например, мы можем проверить, поддерживает ли браузер определённые современные возможности, и если да -- функция будет использовать их, а если нет -- реализовать её другим способом. При этом проверка будет осуществляться один раз, на этапе объявления функции, а не при каждом запуске функции.
-[/smart]
-
-При запуске примера выше в любом браузере, кроме Firefox, мы увидим, что условное объявление не работает. Срабатывает `"До 18 нельзя"`, несмотря на то, что `age = 20`.
+При вводе `20` в примере выше в любом браузере, кроме Firefox, мы увидим, что условное объявление не работает. Срабатывает `"До 18 нельзя"`, несмотря на то, что `age = 20`.
В чём дело? Чтобы ответить на этот вопрос -- вспомним, как работают функции.
@@ -231,55 +218,54 @@ sayHi();
Взглянем ещё на один пример.
-Функция `test(f, yes, no)` получает три функции, вызывает первую и, в зависимости от её результата, вызывает вторую или третью:
+Функция `ask(question, yes, no)` предназначена для выбора действия в зависимости от результата `f`.
+
+Она выводит вопрос на подтверждение `question` и, в зависимости от согласия пользователя, вызывает `yes` или `no`:
```js
//+ run
*!*
-function test(f, yes, no) {
- if (f()) yes()
+function ask(question, yes, no) {
+ if (confirm(question)) yes()
else no();
}
*/!*
-// вспомогательные функции
-function f1() {
- return confirm("OK?");
-}
-
-function f2() {
+function showOk() {
alert("Вы согласились.");
}
-function f3() {
+function showCancel() {
alert("Вы отменили выполнение.");
}
// использование
-test(f1, f2, f3);
+ask("Вы согласны?", showOk, showCancel);
```
-В этом примере для нас, наверно, нет ничего нового. Подумаешь, объявили функции `f1`, `f2`, `f3`, передали их в качестве параметров другой функции (ведь функция -- обычное значение), вызвали те, которые нужны...
+Какой-то очень простой код, не правда ли? Зачем, вообще, может понадобиться такая `ask`?
-А вот то же самое, но более коротко:
+...Но при работе со страницей такие функции как раз очень востребованы, только вот спрашивают они не простым `confirm`, а выводят более красивое окно с вопросом и могут интеллектуально обработать ввод посетителя. Но это всё в своё время.
+
+Здесь обратим внимание на то, что то же самое можно написать более коротко:
```js
//+ run
-function test(f, yes, no) {
- if (f()) yes()
+function ask(question, yes, no) {
+ if (confirm(question)) yes()
else no();
}
*!*
-test(
- function() { return confirm("OK?"); },
+ask(
+ "Вы согласны?",
function() { alert("Вы согласились."); },
function() { alert("Вы отменили выполнение."); }
);
*/!*
```
-Здесь функции объявлены прямо внутри вызова `test(...)`, даже без присвоения им имени.
+Здесь функции объявлены прямо внутри вызова `ask(...)`, даже без присвоения им имени.
**Функциональное выражение, которое не записывается в переменную, называют [анонимной функцией](http://ru.wikipedia.org/wiki/%D0%90%D0%BD%D0%BE%D0%BD%D0%B8%D0%BC%D0%BD%D0%B0%D1%8F_%D1%84%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D1%8F).**
@@ -289,9 +275,9 @@ test(
## new Function
-Существует ещё один способ создания функции, который используется очень редко.
+Существует ещё один способ создания функции, который используется очень редко, но упомянем и его для полноты картины.
-Он выглядит так:
+Он позволяет создавать функцию полностью "на лету" из строки, вот так:
```js
//+ run
@@ -309,7 +295,9 @@ alert(result); // 3
Код функции в виде строки.
-Этот способ позволяет конструировать строку с кодом функции динамически, во время выполнения программы. Это, скорее, исключение, чем правило, но также бывает востребовано. Пример использования -- динамическая компиляция шаблонов на JavaScript, мы встретимся с ней позже, при работе с интерфейсами.
+Таким образом можно конструировать функцию, код которой неизвестен на момент написания программы, но строка с ним генерируется или подгружается динамически во время её выполнения.
+
+Пример использования -- динамическая компиляция шаблонов на JavaScript, мы встретимся с ней позже, при работе с интерфейсами.
## Итого
diff --git a/1-js/2-first-steps/17-recursion/1-sum-to/solution.md b/1-js/2-first-steps/18-recursion/1-sum-to/solution.md
similarity index 100%
rename from 1-js/2-first-steps/17-recursion/1-sum-to/solution.md
rename to 1-js/2-first-steps/18-recursion/1-sum-to/solution.md
diff --git a/1-js/2-first-steps/17-recursion/1-sum-to/task.md b/1-js/2-first-steps/18-recursion/1-sum-to/task.md
similarity index 100%
rename from 1-js/2-first-steps/17-recursion/1-sum-to/task.md
rename to 1-js/2-first-steps/18-recursion/1-sum-to/task.md
diff --git a/1-js/2-first-steps/17-recursion/2-factorial/solution.md b/1-js/2-first-steps/18-recursion/2-factorial/solution.md
similarity index 100%
rename from 1-js/2-first-steps/17-recursion/2-factorial/solution.md
rename to 1-js/2-first-steps/18-recursion/2-factorial/solution.md
diff --git a/1-js/2-first-steps/17-recursion/2-factorial/task.md b/1-js/2-first-steps/18-recursion/2-factorial/task.md
similarity index 100%
rename from 1-js/2-first-steps/17-recursion/2-factorial/task.md
rename to 1-js/2-first-steps/18-recursion/2-factorial/task.md
diff --git a/1-js/2-first-steps/17-recursion/3-fibonacci-numbers/solution.md b/1-js/2-first-steps/18-recursion/3-fibonacci-numbers/solution.md
similarity index 100%
rename from 1-js/2-first-steps/17-recursion/3-fibonacci-numbers/solution.md
rename to 1-js/2-first-steps/18-recursion/3-fibonacci-numbers/solution.md
diff --git a/1-js/2-first-steps/17-recursion/3-fibonacci-numbers/task.md b/1-js/2-first-steps/18-recursion/3-fibonacci-numbers/task.md
similarity index 100%
rename from 1-js/2-first-steps/17-recursion/3-fibonacci-numbers/task.md
rename to 1-js/2-first-steps/18-recursion/3-fibonacci-numbers/task.md
diff --git a/1-js/2-first-steps/17-recursion/article.md b/1-js/2-first-steps/18-recursion/article.md
similarity index 51%
rename from 1-js/2-first-steps/17-recursion/article.md
rename to 1-js/2-first-steps/18-recursion/article.md
index 7225e526..89ec2e62 100644
--- a/1-js/2-first-steps/17-recursion/article.md
+++ b/1-js/2-first-steps/18-recursion/article.md
@@ -1,44 +1,51 @@
# Рекурсия, стек
-В коде функции могут вызывать другие функции для выполнения подзадач. Частный случай подвызова -- когда функция вызывает сама себя. Это называется *рекурсией*.
+В коде функции могут вызывать другие функции для выполнения подзадач.
+
+Частный случай подвызова -- когда функция вызывает сама себя. Это называется *рекурсией*.
+
+Рекурсия используется для ситуаций, когда выполнение одной сложной задачи можно представить как некое действие в совокупности с решением той же задачи в более простом варианте.
+
+Сейчас мы посмотрим примеры.
+
+Рекурсия -- общая тема программирования, не относящаяся напрямую к JavaScript. Если вы разрабатывали на других языках или изучали программирование раньше в ВУЗе, то наверняка уже знаете, что это такое.
+
+Эта глава предназначена для читателей, которые пока с этой темой незнакомы и хотят лучше разобраться в том, как работают функции.
-В этой главе мы рассмотрим, как рекурсия устроена изнутри, и как её можно использовать.
[cut]
-## Реализация pow(x, n) через рекурсию
+## Степень pow(x, n) через рекурсию
-Чтобы возвести `x` в натуральную степень `n` -- можно умножить его на себя `n` раз в цикле:
+В качестве первого примера использования рекурсивных вызовов -- рассмотрим задачу возведения числа `x` в натуральную степень `n`.
+
+Её можно представить как совокупность более простого действия и более простой задачи того же типа вот так:
```js
-function pow(x, n) {
- var result = x;
- for(var i=1; ixn = x * xn-1.
-Ведь xn = x * xn-1, т.е. можно вынести один `x` из-под степени. Иначе говоря, значение функции `pow(x,n)` получается из `pow(x, n-1)` умножением на `x`.
+Например, вычислим `pow(2, 4)`, последовательно переходя к более простой задаче:
-Этот процесс можно продолжить. Например, вычислим `pow(2, 4)`:
+
+
`pow(2, 4) = 2 * pow(2, 3)`
+
`pow(2, 3) = 2 * pow(2, 2)`
+
`pow(2, 2) = 2 * pow(2, 1)`
+
`pow(2, 1) = 2`
+
-```js
-pow(2, 4) = 2 * pow(2, 3) = 2 * 2 * pow(2, 2) = 2 * 2 * 2 * pow(2, 1) = 2 * 2 * 2 * 2;
-```
+На шаге 1 нам нужно вычислить `pow(2,3)`, поэтому мы делаем шаг 2, дальше нам нужно `pow(2,2)`, мы делаем шаг 3, затем шаг 4, и на нём уже можно остановиться, ведь очевидно, что результат возведения числа в степень 1 -- равен самому числу.
-Процесс перехода от `n` к `n-1` останавливается на `n==1`, так как очевидно, что `pow(x,1) == x`.
+Далее, имея результат на шаге 4, он подставляется обратно в шаг 3, затем имеем `pow(2,2)` -- подставляем в шаг 2 и на шаге 1 уже получаем результат.
-Код для такого вычисления:
+Этот алгоритм на JavaScript:
```js
//+ run
function pow(x, n) {
- if (n != 1) { // пока n!=1 сводить вычисление pow(..n) к pow(..n-1)
+ if (n != 1) { // пока n != 1, сводить вычисление pow(x,n) к pow(x,n-1)
return x * pow(x, n-1);
} else {
return x;
@@ -48,13 +55,15 @@ function pow(x, n) {
alert( pow(2, 3) ); // 8
```
-Говорят, что "функция `pow` *рекурсивно вызывает сама себя*" при `n != 1`.
+Говорят, что "функция `pow` *рекурсивно вызывает сама себя*" до `n == 1`.
Значение, на котором рекурсия заканчивается называют *базисом рекурсии*. В примере выше базисом является `1`.
-Общее количество вложенных вызовов называют *глубиной рекурсии*. В случае со степенью, всего будет `n` вызовов. Максимальная глубина рекурсии ограничена и составляет около `10000`, но это число зависит от конкретного интерпретатора JavaScript и может быть в 10 раз меньше.
+Общее количество вложенных вызовов называют *глубиной рекурсии*. В случае со степенью, всего будет `n` вызовов.
-**Рекурсию используют, когда вычисление функции можно свести к её более простому вызову, а его -- еще к более простому, и так далее, пока значение не станет очевидно.**
+Максимальная глубина рекурсии в браузерах ограничена, точно можно рассчитывать на `10000` вложенных вызовов, но некоторые интерпретаторы допускают и больше.
+
+Итак, рекурсию используют, когда вычисление функции можно свести к её более простому вызову, а его -- еще к более простому, и так далее, пока значение не станет очевидно.
## Контекст выполнения, стек
@@ -62,60 +71,39 @@ alert( pow(2, 3) ); // 8
**У каждого вызова функции есть свой "контекст выполнения" (execution context).**
-Контекст выполнения -- это служебная информация, которая соответствует текущему запуску функции. Она включает в себя локальные переменные функции.
+Контекст выполнения -- это служебная информация, которая соответствует текущему запуску функции. Она включает в себя локальные переменные функции и конкретное место в коде, на котором находится интерпретатор.
-Например, для вызова:
-
-```js
-//+ run
-function pow(x, n) {
- if (n != 1) { // пока n!=1 сводить вычисление pow(..n) к pow(..n-1)
- return x * pow(x, n-1);
- } else {
- return x;
- }
-}
-
-*!*
-alert( pow(2, 3) ); // (*)
-*/!*
-```
-
-При запуске функции `pow` в строке `(*)` будет создан контекст выполнения, который будет хранить переменные `x = 2, n = 3`. Мы схематично обозначим его так:
+Например, для вызова `pow(2, 3)` из примера выше будет создан контекст выполнения, который будет хранить переменные `x = 2, n = 3`. Мы схематично обозначим его так:
-
Контекст: { x: 2, n: 3 }
+
Контекст: { x: 2, n: 3, строка 1 }
Далее функция `pow` начинает выполняться. Вычисляется выражение `n != 1` -- оно равно `true`, ведь в текущем контексте `n=3`. Поэтому задействуется первая ветвь `if` :
```js
- if (n != 1) { // пока n!=1 сводить вычисление pow(..n) к pow(..n-1)
+function pow(x, n) {
+ if (n != 1) { // пока n != 1 сводить вычисление pow(x,n) к pow(x,n-1)
*!*
return x * pow(x, n-1);
*/!*
} else {
return x;
}
+}
```
Чтобы вычислить выражение `x * pow(x, n-1)`, требуется произвести запуск `pow` с новыми аргументами.
-**При любом вложенном вызове JavaScript запоминает место, где он остановился в текущей функции в специальной внутренней структуре данных -- "стеке контекстов".**
+**При любом вложенном вызове JavaScript запоминает текущий контекст выполнения в специальной внутренней структуре данных -- "стеке контекстов".**
-Это как если бы мы куда-то ехали, но очень захотелось поесть. Можно остановиться у кафе, оставить машину, отойти, а потом, через некоторое время, вернуться к ней и продолжить дорогу.
+Затем интерпретатор приступает к выполнению вложенного вызова.
-Так и здесь -- мы запомним, где остановились в этой функции, пойдём выполним вложенный вызов, затем вернёмся и продолжим дорогу.
+В данном случае вызывается та же `pow`, однако это абсолютно неважно. Для любых функций процесс одинаков.
-**После того, как текущий контекст выполнения сохранён в стеке контекстов, JavaScript приступает к выполнению вложенного вызова.**
+Для нового вызова создаётся свой контекст выполнения, и управление переходит в него, а когда он завершён -- старый контекст достаётся из стека и выполнение внешней функции возобновляется.
-В данном случае вызывается та же `pow`, однако, это абсолютно неважно. Для любых функций процесс одинаков.
-
-**Создаётся новый контекст выполнения, и управление переходит в подвызов, а когда он завершён -- старый контекст достаётся из стека и выполнение внешней функции возобновляется.**
-
-## Разбор примера
-
-Разберём происходящее более подробно, начиная с вызова `(*)`:
+Разберём происходящее с контекстами более подробно, начиная с вызова `(*)`:
```js
//+ run
@@ -137,26 +125,29 @@ alert( pow(2, 3) ); // (*)
Запускается функция `pow`, с аргументами `x=2`, `n=3`. Эти переменные хранятся в контексте выполнения, схематично изображённом ниже:
-
Контекст: { x: 2, n: 3 }
+
Контекст: { x: 2, n: 3, строка 1 }
Выполнение в этом контексте продолжается, пока не встретит вложенный вызов в строке 3.
`pow(2, 2)`
-
В строке `3` происходит вложенный вызов `pow` с аргументами `x=2`, `n=2`. Для этой функции создаётся новый текущий контекст (выделен красным), а предыдущий сохраняется в "стеке":
+
В строке `3` происходит вложенный вызов `pow` с аргументами `x=2`, `n=2`. Текущий контекст сохраняется в стеке, а для вложеннного вызова создаётся новый контекст (выделен жирным ниже):
-
Контекст: { x: 2, n: 3 }
-
Контекст: { x: 2, n: 2 }
+
Контекст: { x: 2, n: 3, строка 3 }
+
Контекст: { x: 2, n: 2, строка 1 }
+Обратим внимание, что контекст включает в себя не только переменные, но и место в коде, так что когда вложенный вызов завершится -- можно будет легко вернуться назад.
+Слово "строка" здесь условно, на самом деле, конечно, запомнено более точное место в цепочке команд.
`pow(2, 1)`
Опять вложенный вызов в строке `3`, на этот раз -- с аргументами `x=2`, `n=1`. Создаётся новый текущий контекст, предыдущий добавляется в стек:
-
Контекст: { x: 2, n: 3 }
-
Контекст: { x: 2, n: 2 }
-
Контекст: { x: 2, n: 1 }
+
Контекст: { x: 2, n: 3, строка 3 }
+
Контекст: { x: 2, n: 2, строка 3 }
+
Контекст: { x: 2, n: 1, строка 1 }
+На текущий момент в стеке уже два старых контекста.
Выход из `pow(2, 1)`.
При выполнении `pow(2, 1)`, в отличие от предыдущих запусков, выражение `n != 1` будет равно `false`, поэтому сработает вторая ветка `if..else`:
@@ -176,36 +167,29 @@ function pow(x, n) {
Здесь вложенных вызовов нет, так что функция заканчивает свою работу, возвращая `2`. Текущий контекст больше не нужен и удаляется из памяти, из стека восстанавливается предыдущий:
Самый внешний вызов заканчивает свою работу, его результат: `pow(2, 3) = 8`.
-Глубина рекурсии в данном случае составила: **3**.
+Глубина рекурсии в данном случае составила: **3**.
Как видно из иллюстраций выше, глубина рекурсии равна максимальному числу контекстов, одновременно хранимых в стеке.
-[smart]
-В самом конце, как и в самом начале, выполнение попадает во внешний код, который находится вне любых функций.
-
-Контекст, который соответствует самому внешнему коду, называют *"глобальный контекст"*. Естественно, он является начальной и конечной точкой любых вложенных подвызовов.
-[/smart]
-
Обратим внимание на требования к памяти. Рекурсия приводит к хранению всех данных для неоконченных внешних вызовов в стеке, в данном случае это приводит к тому, что возведение в степень `n` хранит в памяти `n` различных контекстов.
-Реализация степени через цикл гораздо более экономна:
+Реализация возведения в степень через цикл гораздо более экономна:
```js
function pow(x, n) {
@@ -221,31 +205,17 @@ function pow(x, n) {
**Любая рекурсия может быть переделана в цикл. Как правило, вариант с циклом будет эффективнее.**
-...Но зачем тогда нужна рекурсия? Да просто затем, что рекурсивный код может быть гораздо проще и понятнее!
-
-Переделка в цикл может быть нетривиальной, особенно когда в функции, в зависимости от условий, используются разные рекурсивные подвызовы.
-
-В программировании мы в первую очередь стремимся сделать сложное простым, а повышенная производительность нужна... Лишь там, где она действительно нужна. Поэтому красивое рекурсивное решение во многих случаях лучше.
-
-Недостатки и преимущества рекурсии:
-
-[compare]
--Требования к памяти.
--Ограничена максимальная глубина стека.
-+Краткость и простота кода.
-[/compare]
-
+Но переделка рекурсии в цикл может быть нетривиальной, особенно когда в функции, в зависимости от условий, используются различные рекурсивные подвызовы, когда ветвление более сложное.
## Итого
-Рекурсия -- это когда функция вызывает сама себя, с другими аргументами.
+Рекурсия -- это когда функция вызывает сама себя, как правило, с другими аргументами.
Существуют много областей применения рекурсивных вызовов. Здесь мы посмотрели на один из них -- решение задачи путём сведения её к более простой (с меньшими аргументами), но также рекурсия используется для работы с "естественно рекурсивными" структурами данных, такими как HTML-документы, для "глубокого" копирования сложных объектов.
Есть и другие применения, с которыми мы встретимся по мере изучения JavaScript.
-Здесь мы постарались рассмотреть происходящее достаточно подробно, однако, если пожелаете, допустимо временно забежать вперёд и открыть главу [](/debugging-chrome), с тем чтобы при помощи отладчика построчно пробежаться по коду и посмотреть стек.
-
+Здесь мы постарались рассмотреть происходящее достаточно подробно, однако, если пожелаете, допустимо временно забежать вперёд и открыть главу [](/debugging-chrome), с тем чтобы при помощи отладчика построчно пробежаться по коду и посмотреть стек на каждом шаге. Отладчик даёт к нему доступ.
@@ -261,13 +231,13 @@ function pow(x, n) {
float: left;
clear: both;
border: 1px solid black;
- font-family: "PT Mono", monospace;
+ font-family: "Consolas", monospace;
padding: 3px 5px;
}
.function-execution-context li:last-child {
- color: red;
+ font-weight: bold;
}
[/head]
\ No newline at end of file
diff --git a/1-js/2-first-steps/19-named-function-expression/article.md b/1-js/2-first-steps/19-named-function-expression/article.md
index 241c5bef..9d4de04e 100644
--- a/1-js/2-first-steps/19-named-function-expression/article.md
+++ b/1-js/2-first-steps/19-named-function-expression/article.md
@@ -1,26 +1,28 @@
# Именованные функциональные выражения
-Обычно то, что называют "именем функции", на самом деле -- всего лишь имя переменной, в которую присвоена функция. К самой функции это "имя" никак не привязано.
-
-Однако, есть в JavaScript способ указать имя, действительно привязанное к функции. Оно называется "Named Function Expression" (сокращённо NFE) или, по-русски, *"именованное функциональное выражение"*.
+Специально для работы с рекурсией в JavaScript существует особое расширение функциональных выражений, которое называется "Named Function Expression" (сокращённо NFE) или, по-русски, *"именованное функциональное выражение"*.
+
[cut]
## Named Function Expression [#functions-nfe]
-Простейший пример NFE выглядит так:
+Обычное функциональное выражение:
+```js
+var f = function (...) { /* тело функции */ };
+```
+
+Именованное с именем `sayHi`:
```js
var f = function *!*sayHi*/!*(...) { /* тело функции */ };
```
-Проще говоря, NFE -- это `Function Expression` с дополнительным именем (в примере выше `sayHi`).
+Что же это за имя, которое идёт в дополнение к `f`, и зачем оно?
-Что же это за имя, которое идёт в дополнение к переменной `f`, и зачем оно?
+Имя функционального выражения (`sayHi`) имеет особый смысл. Оно доступно только изнутри самой функции.
-**Имя функционального выражения (`sayHi`) имеет особый смысл. Оно доступно только изнутри самой функции.**
-
-**Это ограничение видимости входит в стандарт JavaScript и поддерживается всеми браузерами, кроме IE8-.**
+Это ограничение видимости входит в стандарт JavaScript и поддерживается всеми браузерами, кроме IE8-.
Например:
@@ -33,13 +35,13 @@ var f = function sayHi(name) {
alert(sayHi); // снаружи - не видно (ошибка: undefined variable 'sayHi')
```
-**Кроме того, имя NFE нельзя перезаписать:**
+Кроме того, имя NFE нельзя перезаписать:
```js
//+ run
var test = function sayHi(name) {
*!*
- sayHi = "тест"; // перезапись
+ sayHi = "тест"; // попытка перезаписи
*/!*
alert(sayHi); // function... (перезапись не удалась)
};
@@ -49,15 +51,7 @@ test();
В режиме `use strict` код выше выдал бы ошибку.
-**Как правило, имя NFE используется для единственной цели -- позволить изнутри функции вызвать саму себя.**
-
-[smart header="Устаревшее специальное значение `arguments.callee`"]
-Если вы работали с JavaScript, то, возможно, знаете, что для этой цели также служило специальное значение `arguments.callee`.
-
-Если это название вам ни о чём не говорит -- всё в порядке, читайте дальше, мы обязательно обсудим его [в отдельной главе](#arguments-callee).
-
-Если же вы в курсе, то стоит иметь в виду, что оно официально исключено из современного стандарта. А NFE -- это наше настоящее.
-[/smart]
+Как правило, имя NFE используется для единственной цели -- позволить изнутри функции вызвать саму себя.
## Пример использования
@@ -130,7 +124,23 @@ alert(f === factorial);
Все остальные браузеры полностью поддерживают именованные функциональные выражения.
[/warn]
+
+[smart header="Устаревшее специальное значение `arguments.callee`"]
+Если вы давно работаете с JavaScript, то, возможно, знаете, что раньше для этой цели также служило специальное значение `arguments.callee`.
+
+Если это название вам ни о чём не говорит -- всё в порядке, читайте дальше, мы обязательно обсудим его [в отдельной главе](#arguments-callee).
+
+Если же вы в курсе, то стоит иметь в виду, что оно официально исключено из современного стандарта. А NFE -- это наше настоящее.
+[/smart]
+
+
## Итого
-Если функция задана как Function Expression, её можно дать имя. Оно будет доступно только внутри функции (кроме IE8-) и предназначено для надёжного рекурсивного вызова функции, даже если она записана в другую переменную.
+Если функция задана как Function Expression, ей можно дать имя.
+
+Оно будет доступно только внутри функции (кроме IE8-).
+
+Это имя предназначено для надёжного рекурсивного вызова функции, даже если она записана в другую переменную.
+
+Обратим внимание, что с Function Declaration так поступить нельзя. Такое "специальное" внутреннее имя функции задаётся только в синтаксисе Function Expression.
diff --git a/1-js/2-first-steps/2-structure/article.md b/1-js/2-first-steps/2-structure/article.md
index 2998d281..c0376ffc 100644
--- a/1-js/2-first-steps/2-structure/article.md
+++ b/1-js/2-first-steps/2-structure/article.md
@@ -23,6 +23,8 @@ alert('Привет');
alert('Мир');
```
+## Точка с запятой [#semicolon]
+
Точку с запятой *во многих случаях* можно не ставить, если есть переход на новую строку.
Так тоже будет работать:
diff --git a/1-js/2-first-steps/20-javascript-specials/article.md b/1-js/2-first-steps/20-javascript-specials/article.md
index 859e24da..3b875424 100644
--- a/1-js/2-first-steps/20-javascript-specials/article.md
+++ b/1-js/2-first-steps/20-javascript-specials/article.md
@@ -25,7 +25,7 @@ alert('Привет')
alert('Мир')
```
-..Однако, иногда JavaScript не вставляет точку с запятой. Например:
+...Однако, иногда JavaScript не вставляет точку с запятой. Например:
```js
//+ run
@@ -35,7 +35,16 @@ var a = 2
alert(a); // 5
```
-Бывают случаи, когда это ведёт к ошибкам, которые достаточно трудно найти и исправить. Правила, когда точка с запятой ставится, а когда нет -- конечно, есть в спецификации языка, но запомнить их поначалу сложно, так как они неочевидны, придуманы "не для людей".
+Бывают случаи, когда это ведёт к ошибкам, которые достаточно трудно найти и исправить, например:
+
+```js
+//+ run
+alert("После этого сообщения будет ошибка")
+
+[1, 2].forEach(alert)
+```
+
+Детали того, как работает код выше (массивы `[...]` и `forEach`) мы скоро изучим, здесь важно то, что при установке точки с запятой после `alert` он будет работать корректно.
**Поэтому в JavaScript рекомендуется точки с запятой ставить. Сейчас это, фактически, общепринятый стандарт.**
@@ -97,34 +106,6 @@ alert( x ); // undefined
Подробнее: [](/variables), [](/types-intro).
-
-## Методы и свойства
-
-Все значения в JavaScript, за исключением `null` и `undefined`, содержат набор вспомогательных функций и значений, доступных "через точку".
-
-Такие функции называют "методами", а значения -- "свойствами".
-
-Например:
-
-```js
-//+ run
-alert( "Привет, мир!".length ); // 12
-```
-
-Еще у строк есть *метод* `toUpperCase()`, который возвращает строку в верхнем регистре:
-
-```js
-//+ run
-var hello = "Привет, мир!";
-
-*!*
-alert( hello.toUpperCase() ); // "ПРИВЕТ, МИР!"
-*/!*
-```
-
-Подробнее: [](/properties-and-methods).
-
-
## Строгий режим
Для того, чтобы интерпретатор работал в режиме максимального соответствия современному стандарту, нужно начинать скрипт директивой `'use strict';`
@@ -300,7 +281,7 @@ for(;;) {
-Подробнее: [](/break-continue).
+Подробнее: [](/while-for).
## Конструкция switch
@@ -347,7 +328,7 @@ alert( sum(1, 2) ); // 3
`sum` -- имя функции, ограничения на имя функции -- те же, что и на имя переменной.
Переменные, объявленные через `var` внутри функции, видны везде внутри этой функции, блоки `if`, `for` и т.п. на видимость не влияют.
-
Параметры передаются "по значению", т.е. копируются в локальные переменные `a`, `b`, за исключением объектов, которые передаются "по ссылке", их мы подробно обсудим в главе [](/object).
+
Параметры передаются копируются в локальные переменные `a`, `b`.
Функция без `return` считается возвращающей `undefined`. Вызов `return` без значения также возвращает `undefined`:
@@ -391,7 +372,7 @@ alert( sum(1, 2) ); // 3
Если объявление функции является частью какого-либо выражения, например `var = function...` или любого другого, то это Function Expression.
-В этом случае имя, которое можно (но не обязательно) указать после `function`, будет видно только внутри этой функции и позволяет обратиться к функции изнутри себя. Обычно оно используется для рекурсивных вызовов.
+В этом случае функции можно присвоить "внутреннее" имя, указав его после `function`. Оно будет видно только внутри этой функции и позволяет обратиться к функции изнутри себя. Обычно это используется для рекурсивных вызовов.
Например, создадим функцию для вычисления факториала как Function Expression и дадим ей имя `me`:
diff --git a/1-js/3-writing-js/1-debugging-chrome/article.md b/1-js/3-writing-js/1-debugging-chrome/article.md
index 0d497bc7..f2b01a5a 100644
--- a/1-js/3-writing-js/1-debugging-chrome/article.md
+++ b/1-js/3-writing-js/1-debugging-chrome/article.md
@@ -12,11 +12,11 @@
В вашей версии Chrome панель может выглядеть несколько по-иному, но что где находится, должно быть понятно.
-Зайдите на страницу [debugging/pow/index.html](/debugging/pow/index.html) браузером Chrome.
+Зайдите на [страницу с примером](debugging/index.html) браузером Chrome.
Откройте инструменты разработчика: [key F12] или в меню `Инструменты > Инструменты Разработчика`.
-Выберите сверху `Sources` (вместо иконок у вас могут быть просто надписи "Elements", "Resources", "Network", "Sources"...)
+Выберите сверху `Sources`.
@@ -34,7 +34,7 @@
-Три полезные кнопки управления:
+Три наиболее часто используемые кнопки управления:
Формат
Нажатие форматирует текст текущего файла, расставляет отступы. Нужна, если вы хотите разобраться в чужом коде, плохо отформатированном или сжатом.
@@ -46,10 +46,12 @@
## Точки остановки
-Открыли `pow.js` в зоне текста? Кликните на 6й строке файла `pow.js`, прямо на цифре 6.
+Открыли файл `pow.js` во вкладке Sources? Кликните на 6й строке файла `pow.js`, прямо на цифре 6.
Поздравляю! Вы поставили "точку остановки" или, как чаще говорят, "брейкпойнт".
+Выглядет это должно примерно так:
+
Слово *Брейкпойнт* (breakpoint) -- часто используемый английский жаргонизм. Это то место в коде, где отладчик будет *автоматически* останавливать выполнение JavaScript, как только оно до него дойдёт.
@@ -62,9 +64,9 @@
Вкладка Breakpoints очень удобна, когда код большой, она позволяет:
-
Быстро перейти на место кода, где стоит брейкпойнт -- кликом на текст.
-
Временно выключить брейкпойнт -- кликом на чекбокс.
-
Быстро удалить брейкпойнт -- правым кликом на текст и выбором Remove...
+
Быстро перейти на место кода, где стоит брейкпойнт кликом на текст.
+
Временно выключить брейкпойнт кликом на чекбокс.
+
Быстро удалить брейкпойнт правым кликом на текст и выбором Remove, и так далее.
[smart header="Дополнительные возможности"]
@@ -89,7 +91,7 @@ function pow(x, n) {
## Остановиться и осмотреться
-Наша функция выполняется сразу при загрузке страницы, так что самый простой способ активировать JavaScript -- перезагрузить её. Итак, нажимаем [key F5] (Windows, Linux) или [key Cmd+R] (Mac).
+Наша функция выполняется сразу при загрузке страницы, так что самый простой способ активировать отладчик JavaScript -- перезагрузить её. Итак, нажимаем [key F5] (Windows, Linux) или [key Cmd+R] (Mac).
Если вы сделали всё, как описано выше, то выполнение прервётся как раз на 6й строке.
@@ -117,17 +119,19 @@ function pow(x, n) {
## Управление выполнением
-Пришло время "погонять" скрипт и "оттрейсить" (от англ. trace, отслеживать) его работу.
+Пришло время, как говорят, "погонять" скрипт и "оттрейсить" (от англ. trace -- отслеживать) его работу.
Обратим внимание на панель управления справа-сверху, в ней есть 6 кнопок:
Если скрипт не встретит новых точек остановки, то на этом работа в отладчике закончится.
+
Продолжает выполнения скрипта с текущего момента в обычном режиме. Если скрипт не встретит новых точек остановки, то в отладчик управление больше не вернётся.
Нажмите на эту кнопку.
-Вы увидите, что отладчик остался на той же строке, но в `Call Stack` появился новый вызов. Это произошло потому, что в 6й строке находится рекурсивный вызов функции `pow`, т.е. управление перешло в неё опять, но с другими аргументами.
+Скрипт продолжится, далее, в 6й строке находится рекурсивный вызов функции `pow`, т.е. управление перейдёт в неё опять (с другими аргументами) и сработает точка остановки, вновь включая отладчик.
+
+При этом вы увидите, что выполнение стоит на той же строке, но в `Call Stack` появился новый вызов.
Походите по стеку вверх-вниз -- вы увидите, что действительно аргументы разные.
@@ -162,17 +166,15 @@ function pow(x, n) {
**Процесс отладки заключается в том, что мы останавливаем скрипт, смотрим, что с переменными, переходим дальше и ищем, где поведение отклоняется от правильного.**
-[smart header="Дополнительные возможности"]
-Правый клик на номер строки открывает контекстное меню, в котором можно запустить выполнение кода до неё (Continue to here).
-
-Это очень удобно, если промежуточные строки нас не интересуют.
+[smart header="Continue to here"]
+Правый клик на номер строки открывает контекстное меню, в котором можно запустить выполнение кода до неё (Continue to here). Это удобно, когда хочется сразу прыгнуть вперёд и breakpoint неохота ставить.
[/smart]
## Консоль
-При отладке, кроме просмотра переменных, бывает полезно запускать команды JavaScript. Для этого нужна консоль.
+При отладке, кроме просмотра переменных и передвижения по скрипту, бывает полезно запускать команды JavaScript. Для этого нужна консоль.
В неё можно перейти, нажав кнопку "Console" вверху-справа, а можно и открыть в дополнение к отладчику, нажав на кнопку или клавишей [key ESC].
@@ -190,13 +192,13 @@ for(var i=0; i<5; i++) {
Полную информацию по специальным командам консоли вы можете получить на странице [](https://developers.google.com/chrome-developer-tools/docs/commandline-api?hl=ru). Эти команды также действуют в Firebug (отладчик для браузера Firefox).
-Консоль поддерживают все браузеры, и, хотя IE10- поддерживает далеко не все функции, `console.log` работает везде, пользуйтесь им вместо `alert`.
+Консоль поддерживают все браузеры, и, хотя IE10- поддерживает далеко не все функции, но `console.log` работает везде. Используйте его для вывода отладочной информации по ходу работы скрипта.
## Ошибки
Ошибки JavaScript выводятся в консоли.
-Например, прервите отладку -- для этого достаточно закрыть инструменты разрабтчика -- и откройте страницу [debugging/pow-error/index.html](/debugging/pow-error/index.html).
+Например, прервите отладку -- для этого достаточно закрыть инструменты разрабтчика -- и откройте [страницу с ошибкой](error/index.html).
Перейдите во вкладку Console инструментов разработчика ([key Ctrl+Shift+J] / [key Cmd+Shift+J]).
diff --git a/1-js/3-writing-js/1-debugging-chrome/chrome_break_error.png b/1-js/3-writing-js/1-debugging-chrome/chrome_break_error.png
deleted file mode 100755
index e55e7e02..00000000
Binary files a/1-js/3-writing-js/1-debugging-chrome/chrome_break_error.png and /dev/null differ
diff --git a/1-js/3-writing-js/1-debugging-chrome/chrome_sources.png b/1-js/3-writing-js/1-debugging-chrome/chrome_sources.png
deleted file mode 100755
index be7df489..00000000
Binary files a/1-js/3-writing-js/1-debugging-chrome/chrome_sources.png and /dev/null differ
diff --git a/1-js/3-writing-js/1-debugging-chrome/chrome_sources_break.png b/1-js/3-writing-js/1-debugging-chrome/chrome_sources_break.png
deleted file mode 100755
index a6b806ac..00000000
Binary files a/1-js/3-writing-js/1-debugging-chrome/chrome_sources_break.png and /dev/null differ
diff --git a/1-js/3-writing-js/1-debugging-chrome/chrome_sources_breakpoint.png b/1-js/3-writing-js/1-debugging-chrome/chrome_sources_breakpoint.png
deleted file mode 100755
index 17ddbab4..00000000
Binary files a/1-js/3-writing-js/1-debugging-chrome/chrome_sources_breakpoint.png and /dev/null differ
diff --git a/1-js/3-writing-js/1-debugging-chrome/chrome_sources_buttons.png b/1-js/3-writing-js/1-debugging-chrome/chrome_sources_buttons.png
deleted file mode 100755
index ecfe6d8f..00000000
Binary files a/1-js/3-writing-js/1-debugging-chrome/chrome_sources_buttons.png and /dev/null differ
diff --git a/1-js/3-writing-js/1-debugging-chrome/console_error.png b/1-js/3-writing-js/1-debugging-chrome/console_error.png
deleted file mode 100755
index 18346ea6..00000000
Binary files a/1-js/3-writing-js/1-debugging-chrome/console_error.png and /dev/null differ
diff --git a/1-js/3-writing-js/1-debugging-chrome/debugging.view/index.html b/1-js/3-writing-js/1-debugging-chrome/debugging.view/index.html
new file mode 100755
index 00000000..eb0eb6e4
--- /dev/null
+++ b/1-js/3-writing-js/1-debugging-chrome/debugging.view/index.html
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+ Пример для отладчика.
+
+
+
+
+
diff --git a/1-js/3-writing-js/1-debugging-chrome/debugging.view/pow.js b/1-js/3-writing-js/1-debugging-chrome/debugging.view/pow.js
new file mode 100755
index 00000000..687cf273
--- /dev/null
+++ b/1-js/3-writing-js/1-debugging-chrome/debugging.view/pow.js
@@ -0,0 +1,8 @@
+function pow(x, n) {
+ if (n == 1) {
+ return x;
+ }
+
+ var result = x * pow(x, n-1);
+ return result;
+}
diff --git a/1-js/3-writing-js/1-debugging-chrome/error.view/index.html b/1-js/3-writing-js/1-debugging-chrome/error.view/index.html
new file mode 100755
index 00000000..eb0eb6e4
--- /dev/null
+++ b/1-js/3-writing-js/1-debugging-chrome/error.view/index.html
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+ Пример для отладчика.
+
+
+
+
+
diff --git a/1-js/3-writing-js/1-debugging-chrome/error.view/pow.js b/1-js/3-writing-js/1-debugging-chrome/error.view/pow.js
new file mode 100755
index 00000000..9ef7a1ed
--- /dev/null
+++ b/1-js/3-writing-js/1-debugging-chrome/error.view/pow.js
@@ -0,0 +1,8 @@
+function pow(x, n) {
+ if (n == 1) {
+ return y;
+ }
+
+ var result = x * pow(x, n-1);
+ return result;
+}
diff --git a/1-js/3-writing-js/1-debugging-chrome/manage1.png b/1-js/3-writing-js/1-debugging-chrome/manage1.png
deleted file mode 100755
index bbc2a6c7..00000000
Binary files a/1-js/3-writing-js/1-debugging-chrome/manage1.png and /dev/null differ
diff --git a/1-js/3-writing-js/1-debugging-chrome/manage2.png b/1-js/3-writing-js/1-debugging-chrome/manage2.png
deleted file mode 100755
index 40abcd45..00000000
Binary files a/1-js/3-writing-js/1-debugging-chrome/manage2.png and /dev/null differ
diff --git a/1-js/3-writing-js/1-debugging-chrome/manage3.png b/1-js/3-writing-js/1-debugging-chrome/manage3.png
deleted file mode 100755
index 5f82f393..00000000
Binary files a/1-js/3-writing-js/1-debugging-chrome/manage3.png and /dev/null differ
diff --git a/1-js/3-writing-js/1-debugging-chrome/manage4.png b/1-js/3-writing-js/1-debugging-chrome/manage4.png
deleted file mode 100755
index 6c851d68..00000000
Binary files a/1-js/3-writing-js/1-debugging-chrome/manage4.png and /dev/null differ
diff --git a/1-js/3-writing-js/1-debugging-chrome/manage5.png b/1-js/3-writing-js/1-debugging-chrome/manage5.png
deleted file mode 100755
index 9f30eb2d..00000000
Binary files a/1-js/3-writing-js/1-debugging-chrome/manage5.png and /dev/null differ
diff --git a/1-js/3-writing-js/1-debugging-chrome/manage6.png b/1-js/3-writing-js/1-debugging-chrome/manage6.png
deleted file mode 100755
index 30409411..00000000
Binary files a/1-js/3-writing-js/1-debugging-chrome/manage6.png and /dev/null differ
diff --git a/1-js/3-writing-js/2-coding-style/1-style-errors/solution.md b/1-js/3-writing-js/2-coding-style/1-style-errors/solution.md
index f23811eb..bcb64956 100644
--- a/1-js/3-writing-js/2-coding-style/1-style-errors/solution.md
+++ b/1-js/3-writing-js/2-coding-style/1-style-errors/solution.md
@@ -27,11 +27,13 @@ else // <- можно на одной строке } else {
Исправленный вариант:
```js
-function pow(x,n) {
- var result = 1;
+function pow(x, n) {
+ var result = 1;
+
for(var i = 0; i < n; i++) {
result *=x;
- }
+ }
+
return result;
}
diff --git a/1-js/3-writing-js/2-coding-style/article.md b/1-js/3-writing-js/2-coding-style/article.md
index 72dbda94..2b165ecb 100644
--- a/1-js/3-writing-js/2-coding-style/article.md
+++ b/1-js/3-writing-js/2-coding-style/article.md
@@ -5,27 +5,70 @@
[cut]
## Синтаксис
-Шпаргалка с правилами синтаксиса:
+Шпаргалка с правилами синтаксиса (детально они их варианты разобраны далее):
-
-Разберём основные моменты.
+
+
+
+
+
+Не всё здесь однозначно, так что разберём эти правила подробнее.
### Фигурные скобки
Пишутся на той же строке, так называемый "египетский" стиль. Перед скобкой -- пробел.
-
+
+
+
+
+Если у вас уже есть опыт в разработке и вы привыкли делать скобку на отдельной строке -- это тоже вариант. В конце концов, решать вам. Но в большинстве JavaScript-фреймворков стиль именно такой.
+
+Если условие и код достаточно короткие, например `if (cond) return null`, то запись в одну строку вполне читаема... Но, как правило, отдельная строка всё равно воспринимается лучше.
### Длина строки
Максимальную длину строки согласовывают в команде. Как правило, это либо `80`, либо `120` символов, в зависимости от того, какие мониторы у разработчиков.
-Более длинные строки необходимо разбивать. Если этого не сделать, то перевод очень длинной строки сделает редактор, и это может быть менее красиво и читаемо.
+Более длинные строки необходимо разбивать для улучшения читаемости.
### Отступы
@@ -36,42 +79,29 @@
Как правило, используются именно пробелы, т.к. они позволяют сделать более гибкие "конфигурации отступов", чем символ "Tab".
-Например:
-
+Например, выровнять аргументы относительно открывающей скобки:
```js
-function fib(n) {
-*!*
- var a = 1;
- var b = 1;
-*/!*
- for (var i = 3; i <= n; i++) {
- var c = a + b;
- a = b;
- b = c;
- }
- return b;
-}
+show("Строки" +
+ " выровнены" +
+ " строго" +
+ " одна под другой");
```
-
-Кстати, обратите внимание, переменные в выделенном фрагменте объявлены по вертикали, а не в строку `var a=1, b=1`. Так более наглядно, человеческий глаз лучше воспринимает ("сканирует") вертикально выравненную информацию, нежели по горизонтали. Это известный факт среди дизайнеров и нам, программистам, он тоже будет полезен для лучшей организации кода.
-
**Вертикальный отступ, для лучшей разбивки кода -- перевод строки.**
-Используется, чтобы разделить логические блоки внутри одной функции. В примере ниже разделены функция `pow`, получение данных `x,n` и их обработка `if`.
+Используется, чтобы разделить логические блоки внутри одной функции. В примере разделены инициализация переменных, главный цикл и возвращение результата:
```js
function pow(x, n) {
- return (n != 1) ? pow(x, n-1) : x;
-}
- // <--
-x = prompt(...);
-n = prompt(...);
- // <--
-if (n >= 1) {
- var result = pow(x, n);
- alert(result);
+ var result = 1;
+ // <--
+ for (var i = 0; i < n; i++) {
+ result *=x;
+ }
+ // <--
+ return result;
}
+
```
Вставляйте дополнительный перевод строки туда, где это сделает код более читаемым. Не должно быть более 9 строк кода подряд без вертикального отступа.
@@ -82,9 +112,7 @@ if (n >= 1) {
Точки с запятой нужно ставить, даже если их, казалось бы, можно пропустить.
-Есть языки, в которых точка с запятой не обязательна, и её там никто не ставит. В JavaScript она тоже не обязательна, но ставить нужно. В чём же разница?
-
-Она в том, что **в JavaScript без точки с запятой возможны трудноуловимые ошибки.** С некоторыми примерами вы встретитесь дальше в учебнике. Такая вот особенность синтаксиса. И поэтому рекомендуется её всегда ставить.
+Есть языки, в которых точка с запятой не обязательна, и её там никто не ставит. В JavaScript перевод строки её заменяет, но лишь частично, поэтому лучше её ставить, как обсуждалось [ранее](#semicolon).
## Именование
@@ -102,7 +130,7 @@ if (n >= 1) {
Уровней вложенности должно быть немного.
-Например, [проверки в циклах лучше делать через "continue"](#continue), чтобы не было дополнительного уровня `if(..) { ... }`:
+Например, [проверки в циклах можно делать через "continue"](#continue), чтобы не было дополнительного уровня `if(..) { ... }`:
Вместо:
@@ -159,21 +187,15 @@ function isEven(n) { // проверка чётности
В случае с функцией `isEven` можно было бы поступить и проще:
-```js
-function isEven(n) { // проверка чётности
- return n % 2 == 0;
-}
-```
-
-..Казалось бы, можно пойти дальше, есть ещё более короткий вариант:
-
```js
function isEven(n) { // проверка чётности
return !(n % 2);
}
```
-...Однако, код `!(n % 2)` менее очевиден чем `n % 2 == 0`. Поэтому, на самом деле, последний вариант хуже. **Главное для нас -- не краткость кода, а его простота и читаемость.**
+...Однако, если код `!(n % 2)` для вас менее очевиден чем предыдущий вариант, то стоит использовать предыдущий.
+
+Главное для нас -- не краткость кода, а его простота и читаемость. Совсем не всегда более короткий код проще для понимания, чем более развёрнутый.
## Функции = Комментарии
@@ -185,7 +207,7 @@ function isEven(n) { // проверка чётности
Сравните, например, две функции `showPrimes(n)` для вывода простых чисел до `n`.
-Первый вариант:
+Первый вариант использует метку:
```js
function showPrimes(n) {
@@ -201,7 +223,7 @@ function showPrimes(n) {
}
```
-Второй вариант, вынесена подфункция `isPrime(n)` для проверки на простоту:
+Второй вариант -- дополнительную функцию `isPrime(n)` для проверки на простоту:
```js
function showPrimes(n) {
@@ -277,28 +299,41 @@ function walkAround() {
-...На самом деле существует еще третий "стиль", при котором функции хаотично разбросаны по коду ;), но это ведь не наш метод, да?
+...На самом деле существует еще третий "стиль", при котором функции хаотично разбросаны по коду, но это ведь не наш метод, да?
-**Как правило, лучше располагать функции под кодом, который их использует.** То есть, это 2й способ.
+**Как правило, лучше располагать функции под кодом, который их использует.**
-Дело в том, что при чтении такого кода мы хотим знать в первую очередь, *что он делает*, а уже затем *какие функции ему помогают.* Если первым идёт код, то это как раз дает необходимую информацию. Что же касается функций, то вполне возможно нам и не понадобится их читать, особенно если они названы адекватно и то, что они делают, понятно.
+То есть, предпочтителен 2й способ.
-У первого способа, впрочем, есть то преимущество, что на момент чтения мы уже знаем, какие функции существуют.
+Дело в том, что при чтении такого кода мы хотим знать в первую очередь, *что он делает*, а уже затем *какие функции ему помогают.* Если первым идёт код, то это как раз дает необходимую информацию. Что же касается функций, то вполне возможно нам и не понадобится их читать, особенно если они названы адекватно и то, что они делают, понятно из названия.
-Таким образом, если над названиями функций никто не думает -- может быть, это будет лучшим выбором :). Попробуйте оба варианта, но по моей практике предпочтителен всё же второй.
-
-
-## Комментарии
+## Плохие комментарии
В коде нужны комментарии.
-**Как правило, комментарии отвечают на вопрос "что происходит в коде?"**
+Сразу начну с того, каких комментариев быть почти не должно.
+
+**Должен быть минимум комментариев, которые отвечают на вопрос "что происходит в коде?"**
+
+Что интересно, в коде начинающих разработчиков обычно комментариев либо нет, либо они как раз такого типа: "что делается в этих строках".
+
+Серьёзно, хороший код и так понятен.
+
+Об этом замечательно выразился Р.Мартин в книге ["Чистый код"](http://www.ozon.ru/context/detail/id/21916535/): "Если вам кажется, что нужно добавить комментарий для улучшения понимания, это значит, что ваш код недостаточно прост, и, может, стоит переписать его".
+
+Если у вас образовалась длинная "простыня", то, возможно, стоит разбить её на отдельные функции, и тогда из их названий будет понятно, что делает тот или иной фрагмент.
+
+Да, конечно, бывают сложные алгоритмы, хитрые решения для оптимизации, поэтому нельзя такие комментарии просто запретить. Но перед тем, как писать подобное -- подумайте: "Нельзя ли сделать код понятным и без них?"
+
+## Хорошие комментарии
+
+
+А какие комментарии полезны и приветствуются?
-Например:
**Архитектурный комментарий -- "как оно, вообще, устроено".**
-Какие компоненты есть, какие технологии использованы, поток взаимодействия. О чём и зачем этот скрипт. Эти комментарии особенно нужны, если вы не один.
+Какие компоненты есть, какие технологии использованы, поток взаимодействия. О чём и зачем этот скрипт. Взгляд с высоты птичьего полёта. Эти комментарии особенно нужны, если вы не один, а проект большой.
Для описания архитектуры, кстати, создан специальный язык [UML](http://ru.wikipedia.org/wiki/Unified_Modeling_Language), красивые диаграммы, но можно и без этого. Главное -- чтобы понятно.
@@ -319,18 +354,11 @@ function pow(x, n) {
}
```
-Такие комментарии позволяют сразу понять, что принимает и что делает функция, не вникая в код.
+Такие комментарии позволяют сразу понять, что принимает и что делает функция, не вникая в код.
-Кстати, они автоматически обрабатываются многими редакторами, например [Aptana](http://aptana.com) и редакторами от [JetBrains](http://www.jetbrains.com/), которые учитывают их при автодополнении.
-
-
**Краткий комментарий, что именно происходит в данном блоке кода.**
-
-Что интересно, в коде начинающих разработчиков обычно комментариев либо нет, либо они как раз такого типа: "что делается в этих строках кода".
-
-На самом деле именно эти комментарии, как правило, являются самыми ненужными. Хороший код и так самоочевиден, если не используются особо сложные алгоритмы.
-
-Об этом замечательно выразился Р. Мартин в книге ["Чистый код"](http://www.ozon.ru/context/detail/id/21916535/): "Если вам кажется, что нужно добавить комментарий для улучшения понимания, это значит, что ваш код не достаточно прост, и, может, стоит переписать его".
+Кстати, они автоматически обрабатываются многими редакторами, например [Aptana](http://aptana.com) и редакторами от [JetBrains](http://www.jetbrains.com/), которые учитывают их при автодополнении, а также выводят их в автоподсказках при наборе кода.
+Кроме того, есть инструменты, например [JSDoc 3](https://github.com/jsdoc3/jsdoc), которые умеют генерировать по таким комментариям документацию в формате HTML. Более подробную информацию об этом можно также найти на сайте [](http://usejsdoc.org/).
@@ -342,9 +370,9 @@ function pow(x, n) {
Например:
-
-
**Есть несколько способов решения задачи. Почему выбран именно этот?**
-
+
+
Есть несколько способов решения задачи. Почему выбран именно этот?
+
Например, пробовали решить задачу по-другому, но не получилось -- напишите об этом. Почему вы выбрали именно этот способ решения? Особенно это важно в тех случаях, когда используется не первый приходящий в голову способ, а какой-то другой.
Без этого возможна, например, такая ситуация:
@@ -354,18 +382,18 @@ function pow(x, n) {
...Порыв, конечно, хороший, да только этот вариант вы уже обдумали раньше. И отказались, а почему -- забыли. В процессе переписывания вспомнили, конечно (к счастью), но результат - потеря времени на повторное обдумывание.
-Комментарии, которые объясняют поведение кода, очень важны. Они помогают понять происходящее и принять правильное решение о развитии кода.
-
-
-
**Какие неочевидные возможности обеспечивает этот код?** Где в другом месте кода они используются?
-
+Комментарии, которые объясняют выбор решения, очень важны. Они помогают понять происходящее и предпринять правильные шаги при развитии кода.
+
+
Какие неочевидные возможности обеспечивает этот код? Где ещё они используются?
+
В хорошем коде должно быть минимум неочевидного. Но там, где это есть -- пожалуйста, комментируйте.
-
+
+
-
+[smart header="Комментарии -- это важно"]
Один из показателей хорошего разработчика -- качество комментариев, которые позволяют эффективно поддерживать код, возвращаться к нему после любой паузы и легко вносить изменения.
-
+[/smart]
## Руководства по стилю
@@ -382,21 +410,23 @@ function pow(x, n) {
-Для того, чтобы начать разработку, вполне хватит элементов стилей, обозначенных в этой главе. В дальнейшем, посмотрите на эти руководства, найдите "свой" стиль ;)
+Для того, чтобы начать разработку, вполне хватит элементов стилей, обозначенных в этой главе. В дальнейшем, посмотрев эти руководства, вы можете выработать и свой стиль, но лучше не делать его особенно "уникальным и неповторимым", себе дороже потом будет с людьми сотрудничать.
### Автоматизированные средства проверки
-Существуют онлайн-сервисы, проверяющие стиль кода.
+Существуют средства, проверяющие стиль кода.
Самые известные -- это:
[JSLint](http://www.jslint.com/) -- проверяет код на соответствие [стилю JSLint](http://www.jslint.com/lint.html), в онлайн-интерфейсе вверху можно ввести код, а внизу различные настройки проверки, чтобы сделать её более мягкой.
-
[JSHint](http://www.jshint.com/) -- ещё один вариант JSLint, ослабляющий требования в ряде мест.
+
[JSHint](http://www.jshint.com/) -- вариант JSLint с большим количеством настроек.
[Closure Linter](https://developers.google.com/closure/utilities/) -- проверка на соответствие [Google JavaScript Style Guide](http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml).
-Все они также доступны в виде программ, которые можно скачать.
+В частности, JSLint и JSHint интегрированы с большинством редакторов, они гибко настраиваются под нужный стиль и совершенно незаметно улучшают разработку, подсказывая, где и что поправить.
+
+Побочный эффект -- они видят некоторые ошибки, например необъявленные переменные. У меня это обычно результат опечатки, которые таким образом сразу отлавливаются. Очень рекомендую поставить что-то из этого. Я использую [JSHint](http://www.jshint.com/).
## Итого
diff --git a/1-js/3-writing-js/2-coding-style/cheatsheet.png b/1-js/3-writing-js/2-coding-style/cheatsheet.png
deleted file mode 100755
index ea150473..00000000
Binary files a/1-js/3-writing-js/2-coding-style/cheatsheet.png and /dev/null differ
diff --git a/1-js/3-writing-js/2-coding-style/code-style.svg b/1-js/3-writing-js/2-coding-style/code-style.svg
new file mode 100644
index 00000000..d59db930
--- /dev/null
+++ b/1-js/3-writing-js/2-coding-style/code-style.svg
@@ -0,0 +1,94 @@
+
+
\ No newline at end of file
diff --git a/1-js/3-writing-js/2-coding-style/figure-bracket-style.svg b/1-js/3-writing-js/2-coding-style/figure-bracket-style.svg
new file mode 100644
index 00000000..770d697d
--- /dev/null
+++ b/1-js/3-writing-js/2-coding-style/figure-bracket-style.svg
@@ -0,0 +1,32 @@
+
+
\ No newline at end of file
diff --git a/1-js/3-writing-js/2-coding-style/figure.png b/1-js/3-writing-js/2-coding-style/figure.png
deleted file mode 100755
index 5d2f1167..00000000
Binary files a/1-js/3-writing-js/2-coding-style/figure.png and /dev/null differ
diff --git a/1-js/3-writing-js/3-write-unmain-code/article.md b/1-js/3-writing-js/3-write-unmain-code/article.md
index 9bee2b50..06fa507c 100644
--- a/1-js/3-writing-js/3-write-unmain-code/article.md
+++ b/1-js/3-writing-js/3-write-unmain-code/article.md
@@ -1,18 +1,26 @@
# Как писать неподдерживаемый код?
+[warn header="Познай свой код"]
+Эта статья представляет собой мой вольный перевод [How To Write Unmaintainable Code](http://mindprod.com/jgloss/unmain.html) ("как писать неподдерживаемый код") с дополнениями, актуальными для JavaScript.
+
+Возможно, в каких-то из этих советов вам даже удастся узнать "этого парня в зеркале".
+[/warn]
+
+
Предлагаю вашему вниманию советы мастеров древности, следование которым создаст дополнительные рабочие места для JavaScript-разработчиков.
Если вы будете им следовать, то ваш код будет так сложен в поддержке, что у JavaScript'еров, которые придут после вас, даже простейшее изменение займет годы *оплачиваемого* труда! А сложные задачи оплачиваются хорошо, так что они, определённо, скажут вам "Спасибо".
Более того, *внимательно* следуя этим правилам, вы сохраните и своё рабочее место, так как все будут бояться вашего кода и бежать от него...
-...Впрочем, всему своя мера. При написании такого кода он не должен *выглядеть* сложным в поддержке, код должен *быть* таковым. Явно кривой код может написать любой дурак. Это заметят, и вас уволят, а код будет переписан с нуля. Вы не можете такого допустить. Эти советы учитывают такую возможность. Да здравствует дзен.
+...Впрочем, всему своя мера. При написании такого кода он не должен *выглядеть* сложным в поддержке, код должен *быть* таковым.
+
+Явно кривой код может написать любой дурак. Это заметят, и вас уволят, а код будет переписан с нуля. Вы не можете такого допустить. Эти советы учитывают такую возможность. Да здравствует дзен.
-Статья представляет собой мой вольный перевод [How To Write Unmaintainable Code](http://mindprod.com/jgloss/unmain.html) с дополнениями, актуальными для JavaScript.
[cut]
-## Соглашения
+## Соглашения -- по настроению
[quote author="Сериал \"Симпсоны\", серия Helter Shelter"]
Рабочий-чистильщик осматривает дом:
@@ -31,22 +39,26 @@
Как затруднить задачу? Можно везде нарушать соглашения -- это помешает ему, но такое могут заметить, и код будет переписан. Как поступил бы ниндзя на вашем месте?
-**...Правильно! Следуйте соглашениям "в общем", но иногда -- нарушайте их.** Тщательно разбросанные по коду нарушения соглашений с одной стороны не делают код явно плохим при первом взгляде, а с другой -- имеют в точности тот же, и даже лучший эффект, чем явное неследование им!
+**...Правильно! Следуйте соглашениям "в общем", но иногда -- нарушайте их.**
-Если пример, который я приведу ниже, пока сложноват -- пропустите его, но обязательно вернитесь к нему позже. Поверьте, это стоит того.
+Тщательно разбросанные по коду нарушения соглашений с одной стороны не делают код явно плохим при первом взгляде, а с другой -- имеют в точности тот же, и даже лучший эффект, чем явное неследование им!
+### Пример из jQuery
+
+[warn header="jQuery / DOM"]
+Этот пример требует знаний jQuery/DOM, если пока их у вас нет -- пропустите его, ничего страшного, но обязательно вернитесь к нему позже. Подобное стоит многих часов отладки.
+[/warn]
Во фреймворке jQuery есть метод [wrap](http://api.jquery.com/wrap/), который обёртывает один элемент вокруг другого:
```js
var img = $(''); // создали новые элементы (jQuery-синтаксис)
var div = $(''); // и поместили в переменную
-*!*
img.wrap(div); // обернуть img в div
-*/!*
+div.append('');
```
-Результат кода выше -- два элемента, один вложен в другой:
+Результат кода после операции `wrap` -- два элемента, один вложен в другой:
```html
@@ -54,66 +66,24 @@ img.wrap(div); // обернуть img в div
```
-(`div` обернулся вокруг `img`)
+А что же после `append`?
-А теперь, когда все расслабились и насладились этим замечательным методом...
+Можно предположить, что `` добавится в конец `div`, сразу после `img`... Но ничего подобного!
-...Самое время ниндзя нанести свой удар!
+Искусный ниндзя уже нанёс свой удар и поведение кода стало неправильным, хотя разработчик об этом даже не подозревает.
-**Как вы думаете, что будет, если добавить к коду выше строку:**
+Как правило, методы jQuery работают с теми элементами, которые им переданы. Но не здесь!
-```js
-//+ lines first-line=5
-div.append('');
-```
+Внутри вызова `img.wrap(div)` происходит клонирование `div` и вокруг `img` оборачивается не сам `div`, а его клон. При этом исходная переменная `div` не меняется, в ней как был пустой `div`, так и остался.
-[smart header="jQuery-справка"]
-Вызов `elemA.append(elemB)` добавляет `elemB` в конец содержимого элемента `elemA`.
-[/smart]
+В итоге, после вызова получается два независимых `div'а`: первый содержит `img` (этот неявный клон никуда не присвоен), а второй -- наш `span`.
-**Возможно, вы полагаете, что `` добавится в конец `div`, сразу после `img`?**
+Злая магия? Плохой феншуй?
-А вот и нет! А вот и нет!..
+Ничего подобного, просто избирательное следование соглашениям. Вызов `wrap` -- неявно клонирует элемент.
-Оказывается, внутри вызова `img.wrap(div)` происходит *клонирование* `div`. И вокруг `img` оборачивается не сам `div`, а его злой клон.
+Такой сюрприз, бесспорно, стоит многих часов отладки.
-При этом исходная переменная `div` не меняется, в ней как был пустой `div`, так и остался. В итоге, после применения к нему `append` получается два `div'а`: один обёрнут вокруг `span`, а в другом -- только `img`.
-
-
-
-
-
Переменная `div`
-
Клон `div`, созданный `wrap`
- (не присвоен никакой переменной)
-
-
-
-
-
-```html
-
-
-
-```
-
-
-
-
-```html
-
-
-
-```
-
-
-
-
-
-Странно? Неочевидно? Да, и не только вам :)
-
-Соглашение в данном случае -- в том, что большинство методов jQuery не клонируют элементы. А вызов `wrap` -- клонирует.
-
-Код его истинный ниндзя писал!
## Краткость -- сестра таланта!
@@ -148,7 +118,7 @@ i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0;
Остановите свой взыскательный взгляд на чём-нибудь более экзотическом. Например, `x` или `y`.
-Эффективность этого подхода особенно заметна, если тело цикла занимает одну-две страницы.
+Эффективность этого подхода особенно заметна, если тело цикла занимает одну-две страницы (чем длиннее -- тем лучше).
В этом случае заметить, что переменная -- счетчик цикла, без пролистывания вверх, невозможно.
@@ -281,7 +251,7 @@ function ninjaFunction(elem) {
var *!*user*/!* = authenticateUser();
function render() {
- var *!*user*/!* = ...
+ var *!*user*/!* = anotherValue();
...
...многобукв...
...
@@ -290,7 +260,7 @@ function render() {
}
```
-Зашедший в середину метода `render` программист, скорее всего, не заметит, что переменная `user` "уже не та" и использует её... Ловушка захлопнулась! Здравствуй, отладчик.
+Зашедший в середину метода `render` программист, скорее всего, не заметит, что переменная `user` локально перекрыта и попытается работать с ней, полагая, что это результат `authenticateUser()`... Ловушка захлопнулась! Здравствуй, отладчик.
## Мощные функции!
@@ -298,7 +268,9 @@ function render() {
Например, функция `validateEmail(email)` может, кроме проверки e-mail на правильность, выводить сообщение об ошибке и просить заново ввести e-mail.
-**Выберите хотя бы пару дополнительных действий, кроме основного назначения функции.** Главное -- они должны быть неочевидны из названия функции. Истинный ниндзя-девелопер сделает так, что они будут неочевидны и из кода тоже.
+**Выберите хотя бы пару дополнительных действий, кроме основного назначения функции.**
+
+Главное -- они должны быть неочевидны из названия функции. Истинный ниндзя-девелопер сделает так, что они будут неочевидны и из кода тоже.
**Объединение нескольких смежных действий в одну функцию защитит ваш код от повторного использования.**
@@ -315,9 +287,9 @@ function render() {
**Ещё одна вариация такого подхода -- возвращать нестандартное значение.**
-Ведь общеизвестно, что `is..` и `check..` обычно возвращают `true/false`. Продемонстрируйте оригинальное мышление. Пусть вызов `checkPermission` возвращает не результат `true/false`, а объект -- с результатами проверки! А что, полезно.
+Ведь общеизвестно, что `is..` и `check..` обычно возвращают `true/false`. Продемонстрируйте оригинальное мышление. Пусть вызов `checkPermission` возвращает не результат `true/false`, а объект с результатами проверки! А чего, полезно.
-Те разработчики, кто попытается написать проверку `if (checkPermission(..))`, будут весьма удивлены результатом. Ответьте им: "надо читать документацию!". И перешлите эту статью.
+Те же разработчики, кто попытается написать проверку `if (checkPermission(..))`, будут весьма удивлены результатом. Ответьте им: "надо читать документацию!". И перешлите эту статью.
## Заключение
diff --git a/1-js/3-writing-js/4-testing/article.md b/1-js/3-writing-js/4-testing/article.md
index 164607ab..a5721a71 100644
--- a/1-js/3-writing-js/4-testing/article.md
+++ b/1-js/3-writing-js/4-testing/article.md
@@ -8,13 +8,11 @@
При написании функции мы обычно представляем, что она должна делать, какое значение -- на каких аргументах выдавать.
-В процессе разработки мы, время от времени, проверяем функцию. Самый простой способ проверки -- это запустить функцию и посмотреть результат.
+В процессе разработки мы, время от времени, проверяем, правильно ли работает функция. Самый простой способ проверить -- это запустить её, например, в консоли, и посмотреть результат.
-Потом написать ещё код, попробовать запустить -- опять посмотреть результат.
+Если что-то не так -- поправить, опять запустить -- посмотреть результат... И так -- "до победного конца".
-И так -- "до победного конца".
-
-К сожалению, такие ручные запуски -- очень несовершенное средство проверки.
+Но такие ручные запуски -- очень несовершенное средство проверки.
**Когда проверяешь работу кода вручную -- легко его "недотестировать".**
@@ -28,15 +26,14 @@
BDD -- это не просто тесты. Это гораздо больше.
-**Тесты BDD -- это три в одном: это И тесты И документация И примеры использования одновременно.**
+**Тесты BDD -- это три в одном: И тесты И документация И примеры использования одновременно.**
Впрочем, хватит слов. Рассмотрим примеры.
-## Разработка pow
+## Разработка pow: спецификация
Допустим, мы хотим разработать функцию `pow(x, n)`, которая возводит `x` в целую степень `n`, для простоты `n≥0`.
-### Спецификация
Ещё до разработки мы можем представить себе, что эта функция будет делать и описать это по методике BDD.
@@ -61,9 +58,15 @@ describe("pow", function() {
`assert.equal(value1, value2)`
Код внутри `it`, если реализация верна, должен выполняться без ошибок.
-Для того, чтобы проверить, делает ли `pow` то, что задумано, используются функции вида `assert.*`. Пока что нас интересует только одна из них -- `assert.equal`, она сравнивает свой первый аргумент со вторым и выдаёт ошибку в случае, когда они не равны. Есть и другие виды сравнений и проверок, которые мы увидим далее.
+Различные функции вида `assert.*` используются, чтобы проверить, делает ли `pow` то, что задумано. Пока что нас интересует только одна из них -- `assert.equal`, она сравнивает свой первый аргумент со вторым и выдаёт ошибку в случае, когда они не равны. В данном случае она проверяет, что результат `pow(2, 3)` равен `8`.
+
+
+Есть и другие виды сравнений и проверок, которые мы увидим далее.
+
+## Поток разработки
+
Как правило, поток разработки таков:
Пишется спецификация, которая описывает самый базовый функционал.
@@ -77,7 +80,7 @@ describe("pow", function() {
В нашем случае первый шаг уже завершён, начальная спецификация готова, хорошо бы приступить к реализации. Но перед этим проведём "нулевой" запуск спецификации, просто чтобы увидеть, что уже в таком виде, даже без реализации -- тесты работают.
-### Проверка спецификации
+## Пример в действии
Для запуска тестов нужны соответствующие JavaScript-библиотеки.
@@ -96,22 +99,23 @@ describe("pow", function() {
```
-Эту страницу можно условно разделить на три части:
+Эту страницу можно условно разделить на четыре части:
-