From 4b8b168fd248b1da393e805571e967ec1f3b2a1c Mon Sep 17 00:00:00 2001 From: Ilya Kantor Date: Sat, 10 Jan 2015 00:54:38 +0300 Subject: [PATCH] renovations --- 1-js/2-first-steps/15-switch/article.md | 32 +-- .../16-function-basics/article.md | 47 +--- .../article.md | 84 ++++---- .../1-sum-to/solution.md | 0 .../1-sum-to/task.md | 0 .../2-factorial/solution.md | 0 .../2-factorial/task.md | 0 .../3-fibonacci-numbers/solution.md | 0 .../3-fibonacci-numbers/task.md | 0 .../{17-recursion => 18-recursion}/article.md | 158 ++++++-------- .../19-named-function-expression/article.md | 52 +++-- 1-js/2-first-steps/2-structure/article.md | 2 + .../20-javascript-specials/article.md | 47 ++-- .../1-debugging-chrome/article.md | 38 ++-- .../1-debugging-chrome/chrome_break_error.png | Bin 86080 -> 0 bytes .../1-debugging-chrome/chrome_sources.png | Bin 82959 -> 0 bytes .../chrome_sources_break.png | Bin 84508 -> 0 bytes .../chrome_sources_breakpoint.png | Bin 69773 -> 0 bytes .../chrome_sources_buttons.png | Bin 72177 -> 0 bytes .../1-debugging-chrome/console_error.png | Bin 25184 -> 0 bytes .../debugging.view/index.html | 19 ++ .../1-debugging-chrome/debugging.view/pow.js | 8 + .../1-debugging-chrome/error.view/index.html | 19 ++ .../1-debugging-chrome/error.view/pow.js | 8 + .../1-debugging-chrome/manage1.png | Bin 717 -> 0 bytes .../1-debugging-chrome/manage2.png | Bin 504 -> 0 bytes .../1-debugging-chrome/manage3.png | Bin 431 -> 0 bytes .../1-debugging-chrome/manage4.png | Bin 418 -> 0 bytes .../1-debugging-chrome/manage5.png | Bin 554 -> 0 bytes .../1-debugging-chrome/manage6.png | Bin 463 -> 0 bytes .../2-coding-style/1-style-errors/solution.md | 8 +- 1-js/3-writing-js/2-coding-style/article.md | 200 ++++++++++-------- .../2-coding-style/cheatsheet.png | Bin 38371 -> 0 bytes .../2-coding-style/code-style.svg | 94 ++++++++ .../2-coding-style/figure-bracket-style.svg | 32 +++ 1-js/3-writing-js/2-coding-style/figure.png | Bin 17529 -> 0 bytes .../3-write-unmain-code/article.md | 102 ++++----- 1-js/3-writing-js/4-testing/article.md | 54 ++--- .../4-testing/beforeafter.view/test.js | 4 +- .../pow-full.view/index.html | 0 .../pow-full.view/test.js | 0 1-js/3-writing-js/4-testingpow-full/test.js | 44 ---- 42 files changed, 562 insertions(+), 490 deletions(-) rename 1-js/2-first-steps/{18-function-declaration-expression => 17-function-declaration-expression}/article.md (67%) rename 1-js/2-first-steps/{17-recursion => 18-recursion}/1-sum-to/solution.md (100%) rename 1-js/2-first-steps/{17-recursion => 18-recursion}/1-sum-to/task.md (100%) rename 1-js/2-first-steps/{17-recursion => 18-recursion}/2-factorial/solution.md (100%) rename 1-js/2-first-steps/{17-recursion => 18-recursion}/2-factorial/task.md (100%) rename 1-js/2-first-steps/{17-recursion => 18-recursion}/3-fibonacci-numbers/solution.md (100%) rename 1-js/2-first-steps/{17-recursion => 18-recursion}/3-fibonacci-numbers/task.md (100%) rename 1-js/2-first-steps/{17-recursion => 18-recursion}/article.md (51%) delete mode 100755 1-js/3-writing-js/1-debugging-chrome/chrome_break_error.png delete mode 100755 1-js/3-writing-js/1-debugging-chrome/chrome_sources.png delete mode 100755 1-js/3-writing-js/1-debugging-chrome/chrome_sources_break.png delete mode 100755 1-js/3-writing-js/1-debugging-chrome/chrome_sources_breakpoint.png delete mode 100755 1-js/3-writing-js/1-debugging-chrome/chrome_sources_buttons.png delete mode 100755 1-js/3-writing-js/1-debugging-chrome/console_error.png create mode 100755 1-js/3-writing-js/1-debugging-chrome/debugging.view/index.html create mode 100755 1-js/3-writing-js/1-debugging-chrome/debugging.view/pow.js create mode 100755 1-js/3-writing-js/1-debugging-chrome/error.view/index.html create mode 100755 1-js/3-writing-js/1-debugging-chrome/error.view/pow.js delete mode 100755 1-js/3-writing-js/1-debugging-chrome/manage1.png delete mode 100755 1-js/3-writing-js/1-debugging-chrome/manage2.png delete mode 100755 1-js/3-writing-js/1-debugging-chrome/manage3.png delete mode 100755 1-js/3-writing-js/1-debugging-chrome/manage4.png delete mode 100755 1-js/3-writing-js/1-debugging-chrome/manage5.png delete mode 100755 1-js/3-writing-js/1-debugging-chrome/manage6.png delete mode 100755 1-js/3-writing-js/2-coding-style/cheatsheet.png create mode 100644 1-js/3-writing-js/2-coding-style/code-style.svg create mode 100644 1-js/3-writing-js/2-coding-style/figure-bracket-style.svg delete mode 100755 1-js/3-writing-js/2-coding-style/figure.png rename 1-js/3-writing-js/{4-testingpow-full => 4-testing}/pow-full.view/index.html (100%) rename 1-js/3-writing-js/{4-testingpow-full => 4-testing}/pow-full.view/test.js (100%) delete mode 100755 1-js/3-writing-js/4-testingpow-full/test.js 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` и *потом* читайте дальше... 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". Несмотря на немного разный вид, по сути две эти записи делают одно и то же: @@ -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)`: +
      +
    1. `pow(2, 4) = 2 * pow(2, 3)`
    2. +
    3. `pow(2, 3) = 2 * pow(2, 2)`
    4. +
    5. `pow(2, 2) = 2 * pow(2, 1)`
    6. +
    7. `pow(2, 1) = 2`
    8. +
    -```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`. Мы схематично обозначим его так: Далее функция `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`. Текущий контекст больше не нужен и удаляется из памяти, из стека восстанавливается предыдущий:
      -
    • Контекст: { x: 2, n: 3 }
    • -
    • Контекст: { x: 2, n: 2 }
    • +
    • Контекст: { x: 2, n: 3, строка 3 }
    • +
    • Контекст: { x: 2, n: 2, строка 3 }
    Возобновляется обработка внешнего вызова `pow(2, 2)`.
    Выход из `pow(2, 2)`.
    ...И теперь уже `pow(2, 2)` может закончить свою работу, вернув `4`. Восстанавливается контекст предыдущего вызова:
      -
    • Контекст: { x: 2, n: 3 }
    • +
    • Контекст: { x: 2, n: 3, строка 3 }
    Возобновляется обработка внешнего вызова `pow(2, 3)`.
    -
    Выход из `pow(2, 3)`.
    Самый внешний вызов заканчивает свою работу, его результат: `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 +[smart header="Комментарии -- это важно"] Один из показателей хорошего разработчика -- качество комментариев, которые позволяют эффективно поддерживать код, возвращаться к нему после любой паузы и легко вносить изменения. - +[/smart] ## Руководства по стилю @@ -382,21 +410,23 @@ function pow(x, n) {
  • [Dojo Style Guide](http://dojotoolkit.org/community/styleGuide)
  • -Для того, чтобы начать разработку, вполне хватит элементов стилей, обозначенных в этой главе. В дальнейшем, посмотрите на эти руководства, найдите "свой" стиль ;) +Для того, чтобы начать разработку, вполне хватит элементов стилей, обозначенных в этой главе. В дальнейшем, посмотрев эти руководства, вы можете выработать и свой стиль, но лучше не делать его особенно "уникальным и неповторимым", себе дороже потом будет с людьми сотрудничать. ### Автоматизированные средства проверки -Существуют онлайн-сервисы, проверяющие стиль кода. +Существуют средства, проверяющие стиль кода. Самые известные -- это:
    • [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 ea150473810958cfc2b68f8a5ea42a5a67e0563f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 38371 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 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 @@ + + + + Slice 1 + Created with Sketch. + + + + + + + + + + 2 + + + + + + Между именем функции + и скобкой + ( + нет пробела + + + + + Отступ + 2 пробела + + + + + Пробел после + for + + + + + } else { + без перевода строки + + + + + Пробелы вокруг + вложенного вызова + + + + + + + пустая строка + между + логическими блоками + + + + + длина строки + не более 80 символов + + + + + точка с запятой ; + обязательна + + + + + Фигурная скобка + { + + на той же строке, через пробел + + + + + Пробел между + параметрами + + + + + Пробел между + параметрами + + + + + \ 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 @@ + + + + Slice 1 + Created with Sketch. + + + + + + Плохо! + Фигурные скобки не имеют смысла + + + + + + + + + В одну строку без скобок - приемлемо, + если эта строка короткая + + + Самый лучший вариант + + + + + + + \ 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 5d2f116722e53141ba3bec9f6abdef881a069641..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17529 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 @@ -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`. + + +Есть и другие виды сравнений и проверок, которые мы увидим далее. + +## Поток разработки + Как правило, поток разработки таков:
    1. Пишется спецификация, которая описывает самый базовый функционал.
    2. @@ -77,7 +80,7 @@ describe("pow", function() { В нашем случае первый шаг уже завершён, начальная спецификация готова, хорошо бы приступить к реализации. Но перед этим проведём "нулевой" запуск спецификации, просто чтобы увидеть, что уже в таком виде, даже без реализации -- тесты работают. -### Проверка спецификации +## Пример в действии Для запуска тестов нужны соответствующие JavaScript-библиотеки. @@ -96,22 +99,23 @@ describe("pow", function() { ``` -Эту страницу можно условно разделить на три части: +Эту страницу можно условно разделить на четыре части:
        -
      1. В `` подключаем библиотеки и стили.
      2. -
      3. Подключаем `