From 413e552c29f5349114870e9403962558e7cb6b21 Mon Sep 17 00:00:00 2001 From: Ilya Kantor Date: Mon, 13 Jul 2015 01:05:38 +0300 Subject: [PATCH] es6 --- .../10-es-modern/1-es-modern-usage/article.md | 14 +- 1-js/10-es-modern/2-let-const/article.md | 43 ++++-- 1-js/10-es-modern/3-destructuring/article.md | 140 +++++++++++------- 1-js/10-es-modern/4-es-function/article.md | 72 ++++++--- 1-js/10-es-modern/5-es-string/article.md | 31 ++-- 5 files changed, 188 insertions(+), 112 deletions(-) diff --git a/1-js/10-es-modern/1-es-modern-usage/article.md b/1-js/10-es-modern/1-es-modern-usage/article.md index 36d8c10c..c64b4497 100644 --- a/1-js/10-es-modern/1-es-modern-usage/article.md +++ b/1-js/10-es-modern/1-es-modern-usage/article.md @@ -1,10 +1,5 @@ # ES-2015 сейчас -[smart header="Этот раздел -- в активной разработке"] -Стандарт ES-2015 недавно был принят окончательно, и этот раздел находится в ежедневной разработке. - -[/smart] - [Стандарт ES-2015](http://www.ecma-international.org/publications/standards/Ecma-262.htm) был принят в июне 2015. Пока что большинство браузеров реализуют его частично, текущее состояние реализации различных возможностей можно посмотреть здесь: [](https://kangax.github.io/compat-table/es6/). Когда стандарт будет более-менее поддерживаться во всех браузерах, то весь учебник будет обновлён в соответствии с ним. Пока же, как центральное место для "сбора" современных фич JavaScript, создан этот раздел. @@ -68,7 +63,7 @@ Это означает, что при запуске примеров в браузере, который их не поддерживает, будет ошибка. Это не означает, что пример неправильный! Просто пока нет поддержки... -Рекомендуется [Chrome Canary](https://www.google.com/chrome/browser/canary.html), Edge или [Firefox Developer Edition](https://www.mozilla.org/en-US/firefox/channel/#developer). +Рекомендуется [Chrome Canary](https://www.google.com/chrome/browser/canary.html), Edge или [Firefox Developer Edition](https://www.mozilla.org/en-US/firefox/channel/#developer), большинство примеров в них работает. Впрочем, если пример в браузере не работает (обычно проявляется как ошибка синтаксиса) -- почти все примеры вы можете запустить его при помощи Babel, на странице [Babel: try it out](https://babeljs.io/repl/). Там же увидите и преобразованный код. @@ -78,10 +73,3 @@ Итак, поехали! - - - - - - - diff --git a/1-js/10-es-modern/2-let-const/article.md b/1-js/10-es-modern/2-let-const/article.md index 2f889f40..a3ebaf30 100644 --- a/1-js/10-es-modern/2-let-const/article.md +++ b/1-js/10-es-modern/2-let-const/article.md @@ -9,12 +9,12 @@ let a = 5; ## let -У объявлений `let` три основных отличия от `var`: +У объявлений переменной через `let` есть три основных отличия от `var`:
  1. **Область видимости переменной `let` -- блок `{...}`.** -Как мы помним, переменная, объявленная через `var`, видна везде в функции. В старом стандарте минимальная "область видимости" -- функция. +Как мы помним, переменная, объявленная через `var`, видна везде в функции. Переменная, объявленная через `let`, видна только в рамках блока `{...}`, в котором объявлена. @@ -37,7 +37,9 @@ if (true) { alert(apples); // 10 (снаружи блока то же самое) ``` -То же самое с `let`: +В примере выше `apples` -- одна переменная на весь код, которая модифицируется в `if`. + +То же самое с `let` будет работать по-другому: ```js //+ run @@ -58,7 +60,25 @@ alert(apples); // 5 (снаружи блока значение не измен Здесь, фактически, две независимые переменные `apples`, одна -- глобальная, вторая -- в блоке `if`. -Заметим, что если объявление `apples` в строке `(*)` закомментировать, то в последнем `alert` будет ошибка: переменная неопределена. Это потому что переменная `let` всегда видна именно в том блоке, где объявлена и не более. +Заметим, что если объявление `let apples` в первой строке `(*)` удалить, то в последнем `alert` будет ошибка: переменная неопределена: + +```js +//+ run +'use strict'; + +if (true) { + let apples = 10; + + alert(apples); // 10 (внутри блока) +} + +*!* +alert(apples); // ошибка! +*/!* +``` + + +Это потому что переменная `let` всегда видна именно в том блоке, где объявлена, и не более.
  2. **Переменная `let` видна только после объявления.** @@ -98,17 +118,16 @@ let x; let x; // ошибка: переменная x уже объявлена ``` -Это -- хоть и выглядит ограничением по сравнению с `var`, но на самом деле проблем не создаёт, так как область видимости ограничена блоком. - -Например, два таких цикла совсем не конфликтуют: +Это -- хоть и выглядит ограничением по сравнению с `var`, но на самом деле проблем не создаёт. Например, два таких цикла совсем не конфликтуют: ```js //+ run 'use strict'; +// каждый цикл имеет свою переменную i for(let i = 0; i<10; i++) { /* … */ } for(let i = 0; i<10; i++) { /* … */ } -alert( i ); // ошибка, переменная не определена +alert( i ); // ошибка: глобальной i нет ``` При объявлении внутри цикла переменная `i` будет видна только в блоке цикла. Она не видна снаружи, поэтому будет ошибка в последнем `alert`. @@ -117,7 +136,7 @@ alert( i ); // ошибка, переменная не определена
  3. **При использовании в цикле, для каждой итерации создаётся своя переменная.** -Переменная `var` -- одна на все итерации цикла (и видна после цикла): +Переменная `var` -- одна на все итерации цикла и видна даже после цикла: ```js //+ run @@ -126,9 +145,9 @@ for(var i=0; i<10; i++) { /* … */ } alert(i); // 10 ``` -С переменной `let` -- всё по-другому. Добавляется ещё одна область видимости: блок цикла. +С переменной `let` -- всё по-другому. -Каждому блоку цикла соответствует своя, независимая, переменная `let`. Если внутри цикла объявляются функции, то в замыкании каждой будет та переменная, которая была при итерации. +Каждому повторению цикла соответствует своя независимая переменная `let`. Если внутри цикла есть вложенные объявления функций, то в замыкании каждой будет та переменная, которая была при соответствующей итерации. Это позволяет легко решить классическую проблему с замыканиями, описанную в задаче [](/task/make-army). @@ -183,7 +202,7 @@ apple = 10; // ошибка
    • Видны только после объявления и только в текущем блоке.
    • Нельзя переобъявлять (в том же блоке).
    • -
    • В цикле каждое значение `let` принадлежит конкретной итерации цикла (и видно в замыканиях).
    • +
    • При объявлении переменной в цикле `for(let …)` -- она видна только в этом цикле. Причём каждой итерации соответствует своя переменная `let`.
    Переменная `const` -- это константа, в остальном -- как `let`. diff --git a/1-js/10-es-modern/3-destructuring/article.md b/1-js/10-es-modern/3-destructuring/article.md index d5ea49c9..f1084316 100644 --- a/1-js/10-es-modern/3-destructuring/article.md +++ b/1-js/10-es-modern/3-destructuring/article.md @@ -1,7 +1,7 @@ # Деструктуризация -*Деструктуризация* (destructuring assignment) -- способ присвоить массив или объект сразу нескольким переменным. +*Деструктуризация* (destructuring assignment) -- это особый синтаксис присваивания, при котором можно присвоить массив или объект сразу нескольким переменным, разбив его на части. ## Массив @@ -10,15 +10,33 @@ ```js 'use strict'; -let [firstName, lastName] = "Илья Кантор".split(" "); +let [firstName, lastName] = ["Илья", "Кантор"]; alert(firstName); // Илья alert(lastName); // Кантор ``` -При таком присвоении первые два значения массива будут помещены в переменные, а последующие -- будут отброшены. +При таком присвоении первое значение массива пойдёт в переменную `firstName`, второе -- в `lastName`, а последующие (если есть) -- будут отброшены. -Однако, можно добавить ещё один параметр, который получит "всё остальное", при помощи троеточия ("spread"): +Ненужные элементы массива также можно отбросить, поставив лишнюю запятую: + +```js +//+ run +'use strict'; + +*!* +// первый и второй элементы не нужны +let [, , title] = "Юлий Цезарь Император Рима".split(" "); +*/!* + +alert(title); // Император +``` + +В коде выше первый и второй элементы массива никуда не записались, они были отброшены. Как, впрочем, и все элементы после третьего. + +### Оператор "spread" + +Если мы хотим получить и последующие значения массива, но не уверены в их числе -- можно добавить ещё один параметр, который получит "всё остальное", при помощи оператора `"..."` ("spread", троеточие): ```js //+ run @@ -33,17 +51,32 @@ alert(lastName); // Цезарь alert(rest); // Император,Рима (массив из 2х элементов) ``` -Значением `rest` будет массив из оставшихся элементов массива. +Значением `rest` будет массив из оставшихся элементов массива. Вместо `rest` можно использовать и другое имя переменной, оператор здесь -- троеточие. Оно должно стоять только последним элементом в списке слева. -**Можно задать и значения по умолчанию, если массив почему-то оказался короче, чем ожидалось.** +### Значения по умолчанию + +Если значений в массиве меньше, чем переменных -- ошибки не будет, просто присвоится `undefined`: -Они задаются через знак `=`, например: ```js //+ run 'use strict'; *!* +let [firstName, lastName] = []; +*/!* + +alert(firstName); // undefined +``` + +Впрочем, как правило, в таких случаях задают значение по умолчанию. Для этого нужно после переменной использовать символ `=` со значением, например: + +```js +//+ run +'use strict'; + +*!* +// значения по умолчанию let [firstName="Гость", lastName="Анонимный"] = []; */!* @@ -51,7 +84,7 @@ alert(firstName); // Гость alert(lastName); // Анонимный ``` -В качестве значений по умолчанию можно использовать не только примитивы, но и выражения, содержащие вызовы функций: +В качестве значений по умолчанию можно использовать не только примитивы, но и выражения, даже включающие в себя вызовы функций: ```js //+ run @@ -70,27 +103,18 @@ alert(firstName); // Вася alert(lastName); // 1436...-visitor ``` -Заметим, что вызов функции `defaultLastName` будет осуществлён только при необходимости, то есть если значения нет в массиве. - -**Ненужные элементы массива можно отбросить, поставив лишнюю запятую:** - -```js -//+ run -'use strict'; - -*!* -// первый и второй элементы не нужны -let [, , title] = "Юлий Цезарь Император Рима".split(" "); -*/!* - -alert(title); // Император -``` - -В коде выше первый и второй элементы массива никуда не записались, они были отброшены. Как, впрочем, и все элементы после третьего. +Заметим, что вызов функции `defaultLastName()` для генерации значения по умолчанию будет осуществлён только при необходимости, то есть если значения нет в массиве. ## Деструктуризация объекта -Деструктуризация может "мгновенно" разобрать объект по переменным. +Деструктуризацию можно использовать и с объектами. При этом мы указываем, какие свойства в какие переменные должны "идти". + +Базовый синтаксис: +```js +let {var1, var2} = {var1:…, var2…} +``` + +Объект справа -- уже существующий, который мы хотим разбить на переменные. А слева -- список переменных, в которые нужно соответствующие свойства записать. Например: @@ -98,35 +122,35 @@ alert(title); // Император //+ run 'use strict'; -let menuOptions = { +let options = { title: "Меню", width: 100, height: 200 }; *!* -let {title, width, height} = menuOptions; +let {title, width, height} = options; */!* alert(`${title} ${width} ${height}`); // Меню 100 200 ``` -Как видно, свойства автоматически присваиваются соответствующим переменным. +Как видно, свойства `options.title`, `options.width` и `options.height` автоматически присвоились соответствующим переменным. -Если хочется присвоить свойство объекта в переменную с другим именем, можно указать соответствие через двоеточие, вот так: +Если хочется присвоить свойство объекта в переменную с другим именем, например, чтобы свойство `options.width` пошло в переменную `w`, то можно указать соответствие через двоеточие, вот так: ```js //+ run 'use strict'; -let menuOptions = { +let options = { title: "Меню", width: 100, height: 200 }; *!* -let {width: w, height: h, title} = menuOptions; +let {width: w, height: h, title} = options; */!* alert(`${title} ${w} ${h}`); // Меню 100 200 @@ -140,12 +164,12 @@ alert(`${title} ${w} ${h}`); // Меню 100 200 //+ run 'use strict'; -let menuOptions = { +let options = { title: "Меню" }; *!* -let {width=100, height=200, title} = menuOptions; +let {width=100, height=200, title} = options; */!* alert(`${title} ${width} ${height}`); // Меню 100 200 @@ -158,33 +182,33 @@ alert(`${title} ${width} ${height}`); // Меню 100 200 //+ run 'use strict'; -let menuOptions = { +let options = { title: "Меню" }; *!* -let {width:w=100, height:h=200, title} = menuOptions; +let {width:w=100, height:h=200, title} = options; */!* alert(`${title} ${w} ${h}`); // Меню 100 200 ``` -А что, если в объекте больше значений, чем переменных? Можно ли куда-то присвоить "остаток"? +А что, если в объекте больше значений, чем переменных? Можно ли куда-то присвоить "остаток", аналогично массивам? -Такой возможности в текущем стандарте нет. Она планируется в будущем стандарте, и выглядеть она будет аналогично массивам: +Такой возможности в текущем стандарте нет. Она планируется в будущем стандарте, и выглядеть она будет примерно так: ```js //+ run 'use strict'; -let menuOptions = { +let options = { title: "Меню", width: 100, height: 200 }; *!* -let {title, ...size} = menuOptions; +let {title, ...size} = options; */!* // title = "Меню" @@ -195,22 +219,25 @@ let {title, ...size} = menuOptions; [smart header="Деструктуризация без объявления"] -В примерах выше переменные объявлялись прямо перед присваиванием. Конечно, можно использовать и уже существующие переменные. +В примерах выше переменные объявлялись прямо перед присваиванием: `let {…} = {…}`. Конечно, можно и без `let`, использовать уже существующие переменные. -Однако, для объектов есть небольшой "подвох". В JavaScript, если в основном потоке кода (не внутри другого выражения) встречается `{...}`, то это воспринимается как блок. +Однако, здесь есть небольшой "подвох". В JavaScript, если в основном потоке кода (не внутри другого выражения) встречается `{...}`, то это воспринимается как блок. -Например, можно использовать для ограничения видимости переменных: +Например, можно использовать такой блок для ограничения видимости переменных: ```js //+ run 'use strict'; { + // вспомогательные переменные, локальные для блока let a = 5; + // поработали с ними alert(a); // 5 + // больше эти переменные не нужны } alert(a); // ошибка нет такой переменной ``` -...Но в данном случае это создаст проблему при деструктуризации: +Конечно, это бывает удобно, но в данном случае это создаст проблему при деструктуризации: ```js let a, b; @@ -227,15 +254,17 @@ let a, b; ## Вложенные деструктуризации +Если объект или массив содержат другие объекты или массивы, и их тоже хочется разбить на переменные -- не проблема. + Деструктуризации можно как угодно сочетать и вкладывать друг в друга. -Пример с подобъектом и подмассивом: +В коде ниже `options` содержит подобъект и подмассив. В деструктуризации ниже сохраняется та же структура: ```js //+ run 'use strict'; -let menuOptions = { +let options = { size: { width: 100, height: 200 @@ -243,16 +272,14 @@ let menuOptions = { items: ["Пончик", "Пирожное"] } -let { - title="Меню", - size: {width, height}, - items: [item1, item2] -} = menuOptions; +let { title="Меню", size: {width, height}, items: [item1, item2] } = options; // Меню 100 200 Пончик Пирожное alert(`${title} ${width} ${height} ${item1} ${item2}`); ``` +Как видно, весь объект `options` корректно разбит на переменные. + ## Итого @@ -261,14 +288,19 @@ alert(`${title} ${width} ${height} ${item1} ${item2}`);
  4. Синтаксис: ```js let {prop : varName = default, ...} = object -let [var1=default, var2, ...rest] = array ``` -Здесь двоеточие `:` задаёт отображение свойства в переменную, а `=` задаёт выражение, которое будет использовано, если значение отсутствует (не указано или `undefined`). +Здесь двоеточие `:` задаёт отображение свойства `prop` в переменную `varName`, а равенство `=default` задаёт выражение, которое будет использовано, если значение отсутствует (не указано или `undefined`). + +Для массивов имеет значение порядок, поэтому нельзя использовать `:`, но значение по умолчанию -- можно: + +```js +let [var1 = default, var2, ...rest] = array +``` Объявление переменной в начале конструкции не обязательно. Можно использовать и существующие переменные. Однако при деструктуризации объекта может потребоваться обернуть выражение в скобки.
  5. -
  6. Вложенные объекты и массивы тоже работают, деструктуризации можно вкладывать друг в друга, сохраняя ту же структуру, что и исходный объект/массив.
  7. +
  8. Вложенные объекты и массивы тоже работают, при деструктуризации нужно лишь сохранить ту же структуру, что и исходный объект/массив.
  9. Как мы увидим далее, деструктуризации особенно пригодятся удобны при чтении объектных параметров функций. diff --git a/1-js/10-es-modern/4-es-function/article.md b/1-js/10-es-modern/4-es-function/article.md index 7786262a..81e2b393 100644 --- a/1-js/10-es-modern/4-es-function/article.md +++ b/1-js/10-es-modern/4-es-function/article.md @@ -84,7 +84,7 @@ function f(arg1, ...rest, arg2) { // arg2 после ...rest ?! [/warn] -Выше мы увидели использование `...` для чтения параметров внутри функции. Но этот же оператор можно использовать и для передачи массива параметров как списка, например: +Выше мы увидели использование `...` для чтения параметров в объявлении функции. Но этот же оператор можно использовать и при вызове функции, для передачи массива параметров как списка, например: ```js //+ run @@ -92,19 +92,22 @@ function f(arg1, ...rest, arg2) { // arg2 после ...rest ?! let numbers = [2, 3, 15]; -// Передаст массив как список аргументов: Math.max(2, 3, 15) +// Оператор ... в вызове передаст массив как список аргументов +// Этот вызов аналогичен Math.max(2, 3, 15) let max = Math.max(*!*...numbers*/!*); alert( max ); // 15 ``` -Эти два вызова делают одно и то же: +Формально говоря, эти два вызова делают одно и то же: ```js Math.max(...numbers); Math.max.apply(Math, numbers); ``` +Похоже, что первый -- короче и красивее. + ## Деструктуризация в параметрах Если функция получает объект, то она может его тут же разбить в переменные: @@ -113,7 +116,7 @@ Math.max.apply(Math, numbers); //+ run 'use strict'; -let menuOptions = { +let options = { title: "Меню", width: 100, height: 200 @@ -125,7 +128,7 @@ function showMenu({title, width, height}) { alert(`${title} ${width} ${height}`); // Меню 100 200 } -showMenu(menuOptions); +showMenu(options); ``` Можно использовать и более сложную деструктуризацию, с соответствиями и значениями по умолчанию: @@ -134,21 +137,36 @@ showMenu(menuOptions); //+ run 'use strict'; -let menuOptions = { +let options = { title: "Меню" }; *!* -function showMenu({title="Заголовок", width:w=100, height:h=200} = {}) { +function showMenu({title="Заголовок", width:w=100, height:h=200}) { */!* alert(`${title} ${w} ${h}`); } -showMenu(menuOptions); // Меню 100 200 +// объект options будет разбит на переменные +showMenu(options); // Меню 100 200 +``` + +Заметим, что в примере выше какой-то аргумент у `showMenu()` обязательно должен быть, чтобы разбить его на переменные. + +Если хочется, чтобы функция могла быть вызвана вообще без аргументов -- нужно добавить ей параметр по умолчанию -- уже не внутрь деструктуризации, а в самом списке аргументов: + +```js +//+ run +'use strict'; + +function showMenu({title="Заголовок", width:w=100, height:h=200} *!*= {}*/!*) { + alert(`${title} ${w} ${h}`); +} showMenu(); // Заголовок 100 200 ``` + ## Имя "name" В свойстве `name` у функции находится её имя. @@ -166,19 +184,23 @@ let g = function g() {}; // g.name == "g" alert(`${f.name} ${g.name}`) // f g ``` -В примере выше показаны Function Declaration и Named Function Expression. +В примере выше показаны Function Declaration и Named Function Expression. В синтаксисе выше довольно очевидно, что у этих функций есть имя `name`. В конце концов, оно указано в объявлении. Но современный JavaScript идёт дальше, он старается даже анонимным функциям дать разумные имена. -Например, при создании анонимной функции с одновременной записью в переменную или свойство -- её имя равно названию переменной (или свойства): +Например, при создании анонимной функции с одновременной записью в переменную или свойство -- её имя равно названию переменной (или свойства). + +Например: ```js 'use strict'; -let g = function() {}; // g.name == "g" +// свойство g.name = "g" +let g = function() {}; let user = { - sayHi: function() { }; // user.sayHi.name == "sayHi" + // свойство user.sayHi.name == "sayHi" + sayHi: function() { }; } ``` @@ -204,7 +226,7 @@ if (true) { sayHi(); // ошибка, функции не существует ``` -То есть, иными словами, такое объявление -- ведёт себя так же как `let sayHi = function ...`, сделанное в начале блока. +То есть, иными словами, такое объявление -- ведёт себя в точности как если бы `let sayHi = function() {…}` было сделано в начале блока. ## Функции через => @@ -222,8 +244,6 @@ let inc = x => x+1; alert( inc(1) ); // 2 ``` -То есть, слева от `=>` находится аргумент, а справа -- выражение, которое нужно вернуть. - Эти две записи -- примерно аналогичны: ```js @@ -232,7 +252,9 @@ let inc = x => x+1; let inc = function(x) { return x + 1; }; ``` -Если аргументов несколько, то они оборачиваются в скобки, например: +Как видно, `"x => x+1"` -- это уже готовая функция. Слева от `=>` находится аргумент, а справа -- выражение, которое нужно вернуть. + +Если аргументов несколько, то нужно обернуть их в скобки, вот так: ```js //+ run @@ -242,16 +264,20 @@ let inc = function(x) { return x + 1; }; let sum = (a,b) => a + b; */!* +// аналог с function +// let inc = function(a, b) { return a + b; }; + alert( sum(1, 2) ); // 3 ``` -...А если совсем нет аргументов, но функцию хочется задать, то используются пустые скобки: +Если нужно задать функцию без аргументов, то также используются скобки, в этом случае -- пустые: ```js //+ run 'use strict'; *!* +// вызов getTime() будет возвращать текущее время let getTime = () => `${new Date().getHours()} : ${new Date().getMinutes()}`; */!* @@ -277,7 +303,7 @@ let getTime = () => { alert( getTime() ); // текущее время ``` -Заметим, что как только тело функции оборачивается в `{…}`, то оно уже автоматически перестаёт быть выражением. Такая функция должна делать явный `return`, как в примере выше, если конечно хочет что-либо возвратить. +Заметим, что как только тело функции оборачивается в `{…}`, то её результат уже не возвращается автоматически. Такая функция должна делать явный `return`, как в примере выше, если конечно хочет что-либо возвратить. Функции-стрелки очень удобны в качестве коллбеков, например: @@ -294,7 +320,7 @@ let sorted = arr.sort( (a,b) => a - b ); alert(sorted); // 3, 5, 8 ``` -Такая запись -- коротка и понятна. +Такая запись -- коротка и понятна. Далее мы познакомимся с дополнительными преимуществами использования функций-стрелок для этой цели. ## Функции-стрелки не имеют своего this @@ -325,7 +351,7 @@ group.showList(); // Наш курс: Даша ``` -Здесь в `forEach` была использована функция-стрелка, поэтому `this.title` внутри -- тот же, что и во внешней функции `showList`. То есть, в данном случае -- `group.title`. +Здесь в `forEach` была использована функция-стрелка, поэтому `this.title` в коллбэке -- тот же, что и во внешней функции `showList`. То есть, в данном случае -- `group.title`. Если бы в `forEach` вместо функции-стрелки была обычная функция, то была бы ошибка: @@ -352,7 +378,7 @@ group.showList(); При запуске будет "попытка прочитать свойство `title` у `undefined`", так как `.forEach(f)` при запуске `f` не ставит `this`. То есть, `this` внутри `forEach` будет `undefined`. [warn header="Функции стрелки нельзя запускать с `new`"] -Отсутствие у функции-стрелки "своего `this`" влечёт за собой естественное ограничение: такие функции нельзя использовать в качестве конструктора, то есть вызывать через `new`. +Отсутствие у функции-стрелки "своего `this`" влечёт за собой естественное ограничение: такие функции нельзя использовать в качестве конструктора, то есть нельзя вызывать через `new`. [/warn] ## Функции-стрелки не имеют своего arguments @@ -404,8 +430,10 @@ sayHiDeferred("Вася"); // Привет, Вася! через 2 секунд ```js function defer(f, ms) { return function() { +*!* let args = arguments; let ctx = this; +*/!* setTimeout(function() { return f.apply(ctx, args); }, ms); @@ -413,6 +441,8 @@ function defer(f, ms) { } ``` +В этом коде пришлось создавать дополнительные переменные `args` и `ctx` для передачи внешних аргументов и контекста через замыкание. + ## Итого diff --git a/1-js/10-es-modern/5-es-string/article.md b/1-js/10-es-modern/5-es-string/article.md index 92c18d0c..f28c4b8a 100644 --- a/1-js/10-es-modern/5-es-string/article.md +++ b/1-js/10-es-modern/5-es-string/article.md @@ -12,7 +12,7 @@ let str = `обратные кавычки`; ``` -Основные отличия от `"…"` и `'…'`: +Основные отличия от двойных `"…"` и одинарных `'…'` кавычек: @@ -88,7 +88,9 @@ let str = f`Sum of ${apples} + ${oranges} =\n ${apples + oranges}!`; Это нужно в тех случаях, когда функция шаблонизации хочет произвести обработку полностью самостоятельно (свои спец. символы?). Или же когда обработка спец. символов не нужна -- например, строка содержит "обычный текст", набранный непрограммистом без учёта спец. символов. [/smart] -Функция может как-то преобразовать строку и вернуть новый результат. +Как видно, функция имеет доступ ко всему: к выражениям, к участкам текста и даже, через `strings.raw` -- к оригинально введённому тексту без учёта стандартных спец. символов. + +Функция шаблонизации может как-то преобразовать строку и вернуть новый результат. В простейшем случае можно просто "склеить" полученные фрагменты в строку: @@ -116,11 +118,11 @@ let oranges = 5; alert( str`Sum of ${apples} + ${oranges} = ${apples + oranges}!`); ``` -Функция `str` в примере выше делает то же самое, что обычные обратные кавычки. Но, конечно, можно пойти намного дальше. +Функция `str` в примере выше делает то же самое, что обычные обратные кавычки. Но, конечно, можно пойти намного дальше. Например, генерировать из HTML-строки DOM-узлы (функции шаблонизации не обязательно возвращать именно строку). -Например, генерировать из HTML-строки DOM-узлы (функции шаблонизации не обязательно возвращать именно строку). +Или можно реализовать интернационализацию. В примере ниже функция `i18n` осуществляет перевод строки. -Или можно реализовать интернационализацию. В примере ниже функция `i18n` подбирает по строке вида `"Hello, ${name}!"` шаблон перевода `"Привет, {0}!"` (где `{0}` -- место для вставки параметра) и возвращает переведённый результат. +Она подбирает по строке вида `"Hello, ${name}!"` шаблон перевода `"Привет, {0}!"` (где `{0}` -- место для вставки параметра) и возвращает переведённый результат со вставленным именем `name`: ```js //+ run @@ -147,12 +149,16 @@ function i18n(strings, ...values) { } // Пример использования +*!* let name = "Вася"; // Перевести строку alert( i18n`Hello, ${name}!` ); // Привет, Вася! +*/!* ``` +Итоговое использование выглядит довольно красиво, не правда ли? + Разумеется, эту функцию можно улучшить и расширить. Функция шаблонизации -- это своего рода "стандартный синтаксический сахар" для упрощения форматирования и парсинга строк. ## Улучшена поддержка юникода @@ -253,23 +259,24 @@ alert( "\u{20331}" ); // 𠌱, китайский иероглиф с этим Например, на основе обычного символа `a` существуют символы: `àáâäãåā`. Самые часто встречающиеся подобные сочетания имеют отдельный юникодный код. Но отнюдь не все. -Для генерации произвольных сочетаний используются два юникодных символа: основа и значок. +Для генерации произвольных сочетаний используются несколько юникодных символов: основа и один или несколько значков. -Например, если после символа `S` идёт символ "точка сверху" (код `\u0307`), то вместе будет "S с точкой сверху" `Ṡ`. +Например, если после символа `S` идёт символ "точка сверху" (код `\u0307`), то показано это будет как "S с точкой сверху" `Ṡ`. Если нужен ещё значок над той же буквой (или под ней) -- без проблем. Просто добавляем соответствующий символ. К примеру, если добавить символ "точка снизу" (код `\u0323`), то будет "S с двумя точками сверху и снизу" `Ṩ` . -В JavaScript-строке: +Пример этого символа в JavaScript-строке: ```js //+ run alert("S\u0307\u0323"); // Ṩ ``` -Такая возможность добавить произвольной букве нужные значки, с одной стороны, необходима, чтобы не хранить все возможные сочетания (которых громадное число), а с другой стороны -- возникает проблемка -- можно представить одинаковый с точки зрения визуального отображения и интерпретации символ -- разными сочетаниями Unicode-кодов. +Такая возможность добавить произвольной букве нужные значки, с одной стороны, необходима, а с другой стороны -- возникает проблемка: можно представить одинаковый с точки зрения визуального отображения и интерпретации символ -- разными сочетаниями Unicode-кодов. +Вот пример: ```js //+ run alert("S\u0307\u0323"); // Ṩ @@ -278,7 +285,7 @@ alert("S\u0323\u0307"); // Ṩ alert( "S\u0307\u0323" == "S\u0323\u0307" ); // false ``` -В первой строке сначала верхняя точка, а потом -- нижняя, во второй -- наоборот. По кодам строки не равны друг другу. Но символ задают один и тот же. +В первой строке после основы `S` идёт сначала значок "верхняя точка", а потом -- нижняя, во второй -- наоборот. По кодам строки не равны друг другу. Но символ задают один и тот же. С целью разрешить эту ситуацию, существует *юникодная нормализация*, при которой строки приводятся к единому, "нормальному", виду. @@ -300,7 +307,7 @@ alert( "S\u0307\u0323".normalize() == "\u1e68" ); // true Это, конечно, не всегда так, просто в данном случае оказалось, что именно такой символ в юникоде уже есть. Если добавить значков, то нормализация уже даст несколько символов. -Впрочем, для большинства практических задач информации, данной выше, должно быть вполне достаточно, но если хочется более подробно ознакомиться с вариантами и правилами нормализации -- они описаны в приложении к стандарту юникод [Unicode Normalization Forms](http://www.unicode.org/reports/tr15/). +Для большинства практических задач информации, данной выше, должно быть вполне достаточно, но если хочется более подробно ознакомиться с вариантами и правилами нормализации -- они описаны в приложении к стандарту юникод [Unicode Normalization Forms](http://www.unicode.org/reports/tr15/). ## Полезные методы