This commit is contained in:
Ilya Kantor 2015-07-13 01:05:38 +03:00
parent 43dfa400c1
commit 413e552c29
5 changed files with 188 additions and 112 deletions

View file

@ -1,10 +1,5 @@
# ES-2015 сейчас # 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/). [Стандарт ES-2015](http://www.ecma-international.org/publications/standards/Ecma-262.htm) был принят в июне 2015. Пока что большинство браузеров реализуют его частично, текущее состояние реализации различных возможностей можно посмотреть здесь: [](https://kangax.github.io/compat-table/es6/).
Когда стандарт будет более-менее поддерживаться во всех браузерах, то весь учебник будет обновлён в соответствии с ним. Пока же, как центральное место для "сбора" современных фич JavaScript, создан этот раздел. Когда стандарт будет более-менее поддерживаться во всех браузерах, то весь учебник будет обновлён в соответствии с ним. Пока же, как центральное место для "сбора" современных фич 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/). Там же увидите и преобразованный код. Впрочем, если пример в браузере не работает (обычно проявляется как ошибка синтаксиса) -- почти все примеры вы можете запустить его при помощи Babel, на странице [Babel: try it out](https://babeljs.io/repl/). Там же увидите и преобразованный код.
@ -78,10 +73,3 @@
Итак, поехали! Итак, поехали!

View file

@ -9,12 +9,12 @@ let a = 5;
## let ## let
У объявлений `let` три основных отличия от `var`: У объявлений переменной через `let` есть три основных отличия от `var`:
<ol> <ol>
<li>**Область видимости переменной `let` -- блок `{...}`.** <li>**Область видимости переменной `let` -- блок `{...}`.**
Как мы помним, переменная, объявленная через `var`, видна везде в функции. В старом стандарте минимальная "область видимости" -- функция. Как мы помним, переменная, объявленная через `var`, видна везде в функции.
Переменная, объявленная через `let`, видна только в рамках блока `{...}`, в котором объявлена. Переменная, объявленная через `let`, видна только в рамках блока `{...}`, в котором объявлена.
@ -37,7 +37,9 @@ if (true) {
alert(apples); // 10 (снаружи блока то же самое) alert(apples); // 10 (снаружи блока то же самое)
``` ```
То же самое с `let`: В примере выше `apples` -- одна переменная на весь код, которая модифицируется в `if`.
То же самое с `let` будет работать по-другому:
```js ```js
//+ run //+ run
@ -58,7 +60,25 @@ alert(apples); // 5 (снаружи блока значение не измен
Здесь, фактически, две независимые переменные `apples`, одна -- глобальная, вторая -- в блоке `if`. Здесь, фактически, две независимые переменные `apples`, одна -- глобальная, вторая -- в блоке `if`.
Заметим, что если объявление `apples` в строке `(*)` закомментировать, то в последнем `alert` будет ошибка: переменная неопределена. Это потому что переменная `let` всегда видна именно в том блоке, где объявлена и не более. Заметим, что если объявление `let apples` в первой строке `(*)` удалить, то в последнем `alert` будет ошибка: переменная неопределена:
```js
//+ run
'use strict';
if (true) {
let apples = 10;
alert(apples); // 10 (внутри блока)
}
*!*
alert(apples); // ошибка!
*/!*
```
Это потому что переменная `let` всегда видна именно в том блоке, где объявлена, и не более.
</li> </li>
<li>**Переменная `let` видна только после объявления.** <li>**Переменная `let` видна только после объявления.**
@ -98,17 +118,16 @@ let x;
let x; // ошибка: переменная x уже объявлена let x; // ошибка: переменная x уже объявлена
``` ```
Это -- хоть и выглядит ограничением по сравнению с `var`, но на самом деле проблем не создаёт, так как область видимости ограничена блоком. Это -- хоть и выглядит ограничением по сравнению с `var`, но на самом деле проблем не создаёт. Например, два таких цикла совсем не конфликтуют:
Например, два таких цикла совсем не конфликтуют:
```js ```js
//+ run //+ run
'use strict'; 'use strict';
// каждый цикл имеет свою переменную i
for(let i = 0; i<10; i++) { /* */ } for(let i = 0; i<10; i++) { /* */ }
for(let i = 0; i<10; i++) { /* */ } for(let i = 0; i<10; i++) { /* */ }
alert( i ); // ошибка, переменная не определена alert( i ); // ошибка: глобальной i нет
``` ```
При объявлении внутри цикла переменная `i` будет видна только в блоке цикла. Она не видна снаружи, поэтому будет ошибка в последнем `alert`. При объявлении внутри цикла переменная `i` будет видна только в блоке цикла. Она не видна снаружи, поэтому будет ошибка в последнем `alert`.
@ -117,7 +136,7 @@ alert( i ); // ошибка, переменная не определена
</li> </li>
<li>**При использовании в цикле, для каждой итерации создаётся своя переменная.** <li>**При использовании в цикле, для каждой итерации создаётся своя переменная.**
Переменная `var` -- одна на все итерации цикла (и видна после цикла): Переменная `var` -- одна на все итерации цикла и видна даже после цикла:
```js ```js
//+ run //+ run
@ -126,9 +145,9 @@ for(var i=0; i<10; i++) { /* … */ }
alert(i); // 10 alert(i); // 10
``` ```
С переменной `let` -- всё по-другому. Добавляется ещё одна область видимости: блок цикла. С переменной `let` -- всё по-другому.
Каждому блоку цикла соответствует своя, независимая, переменная `let`. Если внутри цикла объявляются функции, то в замыкании каждой будет та переменная, которая была при итерации. Каждому повторению цикла соответствует своя независимая переменная `let`. Если внутри цикла есть вложенные объявления функций, то в замыкании каждой будет та переменная, которая была при соответствующей итерации.
Это позволяет легко решить классическую проблему с замыканиями, описанную в задаче [](/task/make-army). Это позволяет легко решить классическую проблему с замыканиями, описанную в задаче [](/task/make-army).
@ -183,7 +202,7 @@ apple = 10; // ошибка
<ul> <ul>
<li>Видны только после объявления и только в текущем блоке.</li> <li>Видны только после объявления и только в текущем блоке.</li>
<li>Нельзя переобъявлять (в том же блоке).</li> <li>Нельзя переобъявлять (в том же блоке).</li>
<li>В цикле каждое значение `let` принадлежит конкретной итерации цикла (и видно в замыканиях).</li> <li>При объявлении переменной в цикле `for(let …)` -- она видна только в этом цикле. Причём каждой итерации соответствует своя переменная `let`.</li>
</ul> </ul>
Переменная `const` -- это константа, в остальном -- как `let`. Переменная `const` -- это константа, в остальном -- как `let`.

View file

@ -1,7 +1,7 @@
# Деструктуризация # Деструктуризация
*Деструктуризация* (destructuring assignment) -- способ присвоить массив или объект сразу нескольким переменным. *Деструктуризация* (destructuring assignment) -- это особый синтаксис присваивания, при котором можно присвоить массив или объект сразу нескольким переменным, разбив его на части.
## Массив ## Массив
@ -10,15 +10,33 @@
```js ```js
'use strict'; 'use strict';
let [firstName, lastName] = "Илья Кантор".split(" "); let [firstName, lastName] = ["Илья", "Кантор"];
alert(firstName); // Илья alert(firstName); // Илья
alert(lastName); // Кантор alert(lastName); // Кантор
``` ```
При таком присвоении первые два значения массива будут помещены в переменные, а последующие -- будут отброшены. При таком присвоении первое значение массива пойдёт в переменную `firstName`, второе -- в `lastName`, а последующие (если есть) -- будут отброшены.
Однако, можно добавить ещё один параметр, который получит "всё остальное", при помощи троеточия ("spread"): Ненужные элементы массива также можно отбросить, поставив лишнюю запятую:
```js
//+ run
'use strict';
*!*
// первый и второй элементы не нужны
let [, , title] = "Юлий Цезарь Император Рима".split(" ");
*/!*
alert(title); // Император
```
В коде выше первый и второй элементы массива никуда не записались, они были отброшены. Как, впрочем, и все элементы после третьего.
### Оператор "spread"
Если мы хотим получить и последующие значения массива, но не уверены в их числе -- можно добавить ещё один параметр, который получит "всё остальное", при помощи оператора `"..."` ("spread", троеточие):
```js ```js
//+ run //+ run
@ -33,17 +51,32 @@ alert(lastName); // Цезарь
alert(rest); // Император,Рима (массив из 2х элементов) alert(rest); // Император,Рима (массив из 2х элементов)
``` ```
Значением `rest` будет массив из оставшихся элементов массива. Значением `rest` будет массив из оставшихся элементов массива. Вместо `rest` можно использовать и другое имя переменной, оператор здесь -- троеточие. Оно должно стоять только последним элементом в списке слева.
**Можно задать и значения по умолчанию, если массив почему-то оказался короче, чем ожидалось.** ### Значения по умолчанию
Если значений в массиве меньше, чем переменных -- ошибки не будет, просто присвоится `undefined`:
Они задаются через знак `=`, например:
```js ```js
//+ run //+ run
'use strict'; 'use strict';
*!* *!*
let [firstName, lastName] = [];
*/!*
alert(firstName); // undefined
```
Впрочем, как правило, в таких случаях задают значение по умолчанию. Для этого нужно после переменной использовать символ `=` со значением, например:
```js
//+ run
'use strict';
*!*
// значения по умолчанию
let [firstName="Гость", lastName="Анонимный"] = []; let [firstName="Гость", lastName="Анонимный"] = [];
*/!* */!*
@ -51,7 +84,7 @@ alert(firstName); // Гость
alert(lastName); // Анонимный alert(lastName); // Анонимный
``` ```
В качестве значений по умолчанию можно использовать не только примитивы, но и выражения, содержащие вызовы функций: В качестве значений по умолчанию можно использовать не только примитивы, но и выражения, даже включающие в себя вызовы функций:
```js ```js
//+ run //+ run
@ -70,27 +103,18 @@ alert(firstName); // Вася
alert(lastName); // 1436...-visitor alert(lastName); // 1436...-visitor
``` ```
Заметим, что вызов функции `defaultLastName` будет осуществлён только при необходимости, то есть если значения нет в массиве. Заметим, что вызов функции `defaultLastName()` для генерации значения по умолчанию будет осуществлён только при необходимости, то есть если значения нет в массиве.
**Ненужные элементы массива можно отбросить, поставив лишнюю запятую:**
```js
//+ run
'use strict';
*!*
// первый и второй элементы не нужны
let [, , title] = "Юлий Цезарь Император Рима".split(" ");
*/!*
alert(title); // Император
```
В коде выше первый и второй элементы массива никуда не записались, они были отброшены. Как, впрочем, и все элементы после третьего.
## Деструктуризация объекта ## Деструктуризация объекта
Деструктуризация может "мгновенно" разобрать объект по переменным. Деструктуризацию можно использовать и с объектами. При этом мы указываем, какие свойства в какие переменные должны "идти".
Базовый синтаксис:
```js
let {var1, var2} = {var1:…, var2…}
```
Объект справа -- уже существующий, который мы хотим разбить на переменные. А слева -- список переменных, в которые нужно соответствующие свойства записать.
Например: Например:
@ -98,35 +122,35 @@ alert(title); // Император
//+ run //+ run
'use strict'; 'use strict';
let menuOptions = { let options = {
title: "Меню", title: "Меню",
width: 100, width: 100,
height: 200 height: 200
}; };
*!* *!*
let {title, width, height} = menuOptions; let {title, width, height} = options;
*/!* */!*
alert(`${title} ${width} ${height}`); // Меню 100 200 alert(`${title} ${width} ${height}`); // Меню 100 200
``` ```
Как видно, свойства автоматически присваиваются соответствующим переменным. Как видно, свойства `options.title`, `options.width` и `options.height` автоматически присвоились соответствующим переменным.
Если хочется присвоить свойство объекта в переменную с другим именем, можно указать соответствие через двоеточие, вот так: Если хочется присвоить свойство объекта в переменную с другим именем, например, чтобы свойство `options.width` пошло в переменную `w`, то можно указать соответствие через двоеточие, вот так:
```js ```js
//+ run //+ run
'use strict'; 'use strict';
let menuOptions = { let options = {
title: "Меню", title: "Меню",
width: 100, width: 100,
height: 200 height: 200
}; };
*!* *!*
let {width: w, height: h, title} = menuOptions; let {width: w, height: h, title} = options;
*/!* */!*
alert(`${title} ${w} ${h}`); // Меню 100 200 alert(`${title} ${w} ${h}`); // Меню 100 200
@ -140,12 +164,12 @@ alert(`${title} ${w} ${h}`); // Меню 100 200
//+ run //+ run
'use strict'; 'use strict';
let menuOptions = { let options = {
title: "Меню" title: "Меню"
}; };
*!* *!*
let {width=100, height=200, title} = menuOptions; let {width=100, height=200, title} = options;
*/!* */!*
alert(`${title} ${width} ${height}`); // Меню 100 200 alert(`${title} ${width} ${height}`); // Меню 100 200
@ -158,33 +182,33 @@ alert(`${title} ${width} ${height}`); // Меню 100 200
//+ run //+ run
'use strict'; 'use strict';
let menuOptions = { let options = {
title: "Меню" 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 alert(`${title} ${w} ${h}`); // Меню 100 200
``` ```
А что, если в объекте больше значений, чем переменных? Можно ли куда-то присвоить "остаток"? А что, если в объекте больше значений, чем переменных? Можно ли куда-то присвоить "остаток", аналогично массивам?
Такой возможности в текущем стандарте нет. Она планируется в будущем стандарте, и выглядеть она будет аналогично массивам: Такой возможности в текущем стандарте нет. Она планируется в будущем стандарте, и выглядеть она будет примерно так:
```js ```js
//+ run //+ run
'use strict'; 'use strict';
let menuOptions = { let options = {
title: "Меню", title: "Меню",
width: 100, width: 100,
height: 200 height: 200
}; };
*!* *!*
let {title, ...size} = menuOptions; let {title, ...size} = options;
*/!* */!*
// title = "Меню" // title = "Меню"
@ -195,22 +219,25 @@ let {title, ...size} = menuOptions;
[smart header="Деструктуризация без объявления"] [smart header="Деструктуризация без объявления"]
В примерах выше переменные объявлялись прямо перед присваиванием. Конечно, можно использовать и уже существующие переменные. В примерах выше переменные объявлялись прямо перед присваиванием: `let {…} = {…}`. Конечно, можно и без `let`, использовать уже существующие переменные.
Однако, для объектов есть небольшой "подвох". В JavaScript, если в основном потоке кода (не внутри другого выражения) встречается `{...}`, то это воспринимается как блок. Однако, здесь есть небольшой "подвох". В JavaScript, если в основном потоке кода (не внутри другого выражения) встречается `{...}`, то это воспринимается как блок.
Например, можно использовать для ограничения видимости переменных: Например, можно использовать такой блок для ограничения видимости переменных:
```js ```js
//+ run //+ run
'use strict'; 'use strict';
{ {
// вспомогательные переменные, локальные для блока
let a = 5; let a = 5;
// поработали с ними
alert(a); // 5 alert(a); // 5
// больше эти переменные не нужны
} }
alert(a); // ошибка нет такой переменной alert(a); // ошибка нет такой переменной
``` ```
...Но в данном случае это создаст проблему при деструктуризации: Конечно, это бывает удобно, но в данном случае это создаст проблему при деструктуризации:
```js ```js
let a, b; let a, b;
@ -227,15 +254,17 @@ let a, b;
## Вложенные деструктуризации ## Вложенные деструктуризации
Если объект или массив содержат другие объекты или массивы, и их тоже хочется разбить на переменные -- не проблема.
Деструктуризации можно как угодно сочетать и вкладывать друг в друга. Деструктуризации можно как угодно сочетать и вкладывать друг в друга.
Пример с подобъектом и подмассивом: В коде ниже `options` содержит подобъект и подмассив. В деструктуризации ниже сохраняется та же структура:
```js ```js
//+ run //+ run
'use strict'; 'use strict';
let menuOptions = { let options = {
size: { size: {
width: 100, width: 100,
height: 200 height: 200
@ -243,16 +272,14 @@ let menuOptions = {
items: ["Пончик", "Пирожное"] items: ["Пончик", "Пирожное"]
} }
let { let { title="Меню", size: {width, height}, items: [item1, item2] } = options;
title="Меню",
size: {width, height},
items: [item1, item2]
} = menuOptions;
// Меню 100 200 Пончик Пирожное // Меню 100 200 Пончик Пирожное
alert(`${title} ${width} ${height} ${item1} ${item2}`); alert(`${title} ${width} ${height} ${item1} ${item2}`);
``` ```
Как видно, весь объект `options` корректно разбит на переменные.
## Итого ## Итого
@ -261,14 +288,19 @@ alert(`${title} ${width} ${height} ${item1} ${item2}`);
<li>Синтаксис: <li>Синтаксис:
```js ```js
let {prop : varName = default, ...} = object let {prop : varName = default, ...} = object
let [var1=default, var2, ...rest] = array
``` ```
Здесь двоеточие `:` задаёт отображение свойства в переменную, а `=` задаёт выражение, которое будет использовано, если значение отсутствует (не указано или `undefined`). Здесь двоеточие `:` задаёт отображение свойства `prop` в переменную `varName`, а равенство `=default` задаёт выражение, которое будет использовано, если значение отсутствует (не указано или `undefined`).
Для массивов имеет значение порядок, поэтому нельзя использовать `:`, но значение по умолчанию -- можно:
```js
let [var1 = default, var2, ...rest] = array
```
Объявление переменной в начале конструкции не обязательно. Можно использовать и существующие переменные. Однако при деструктуризации объекта может потребоваться обернуть выражение в скобки. Объявление переменной в начале конструкции не обязательно. Можно использовать и существующие переменные. Однако при деструктуризации объекта может потребоваться обернуть выражение в скобки.
</li> </li>
<li>Вложенные объекты и массивы тоже работают, деструктуризации можно вкладывать друг в друга, сохраняя ту же структуру, что и исходный объект/массив.</li> <li>Вложенные объекты и массивы тоже работают, при деструктуризации нужно лишь сохранить ту же структуру, что и исходный объект/массив.</li>
</ul> </ul>
Как мы увидим далее, деструктуризации особенно пригодятся удобны при чтении объектных параметров функций. Как мы увидим далее, деструктуризации особенно пригодятся удобны при чтении объектных параметров функций.

View file

@ -84,7 +84,7 @@ function f(arg1, ...rest, arg2) { // arg2 после ...rest ?!
[/warn] [/warn]
Выше мы увидели использование `...` для чтения параметров внутри функции. Но этот же оператор можно использовать и для передачи массива параметров как списка, например: Выше мы увидели использование `...` для чтения параметров в объявлении функции. Но этот же оператор можно использовать и при вызове функции, для передачи массива параметров как списка, например:
```js ```js
//+ run //+ run
@ -92,19 +92,22 @@ function f(arg1, ...rest, arg2) { // arg2 после ...rest ?!
let numbers = [2, 3, 15]; let numbers = [2, 3, 15];
// Передаст массив как список аргументов: Math.max(2, 3, 15) // Оператор ... в вызове передаст массив как список аргументов
// Этот вызов аналогичен Math.max(2, 3, 15)
let max = Math.max(*!*...numbers*/!*); let max = Math.max(*!*...numbers*/!*);
alert( max ); // 15 alert( max ); // 15
``` ```
Эти два вызова делают одно и то же: Формально говоря, эти два вызова делают одно и то же:
```js ```js
Math.max(...numbers); Math.max(...numbers);
Math.max.apply(Math, numbers); Math.max.apply(Math, numbers);
``` ```
Похоже, что первый -- короче и красивее.
## Деструктуризация в параметрах ## Деструктуризация в параметрах
Если функция получает объект, то она может его тут же разбить в переменные: Если функция получает объект, то она может его тут же разбить в переменные:
@ -113,7 +116,7 @@ Math.max.apply(Math, numbers);
//+ run //+ run
'use strict'; 'use strict';
let menuOptions = { let options = {
title: "Меню", title: "Меню",
width: 100, width: 100,
height: 200 height: 200
@ -125,7 +128,7 @@ function showMenu({title, width, height}) {
alert(`${title} ${width} ${height}`); // Меню 100 200 alert(`${title} ${width} ${height}`); // Меню 100 200
} }
showMenu(menuOptions); showMenu(options);
``` ```
Можно использовать и более сложную деструктуризацию, с соответствиями и значениями по умолчанию: Можно использовать и более сложную деструктуризацию, с соответствиями и значениями по умолчанию:
@ -134,21 +137,36 @@ showMenu(menuOptions);
//+ run //+ run
'use strict'; 'use strict';
let menuOptions = { let options = {
title: "Меню" title: "Меню"
}; };
*!* *!*
function showMenu({title="Заголовок", width:w=100, height:h=200} = {}) { function showMenu({title="Заголовок", width:w=100, height:h=200}) {
*/!* */!*
alert(`${title} ${w} ${h}`); 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 showMenu(); // Заголовок 100 200
``` ```
## Имя "name" ## Имя "name"
В свойстве `name` у функции находится её имя. В свойстве `name` у функции находится её имя.
@ -166,19 +184,23 @@ let g = function g() {}; // g.name == "g"
alert(`${f.name} ${g.name}`) // f g alert(`${f.name} ${g.name}`) // f g
``` ```
В примере выше показаны Function Declaration и Named Function Expression. В примере выше показаны Function Declaration и Named Function Expression. В синтаксисе выше довольно очевидно, что у этих функций есть имя `name`. В конце концов, оно указано в объявлении.
Но современный JavaScript идёт дальше, он старается даже анонимным функциям дать разумные имена. Но современный JavaScript идёт дальше, он старается даже анонимным функциям дать разумные имена.
Например, при создании анонимной функции с одновременной записью в переменную или свойство -- её имя равно названию переменной (или свойства): Например, при создании анонимной функции с одновременной записью в переменную или свойство -- её имя равно названию переменной (или свойства).
Например:
```js ```js
'use strict'; 'use strict';
let g = function() {}; // g.name == "g" // свойство g.name = "g"
let g = function() {};
let user = { let user = {
sayHi: function() { }; // user.sayHi.name == "sayHi" // свойство user.sayHi.name == "sayHi"
sayHi: function() { };
} }
``` ```
@ -204,7 +226,7 @@ if (true) {
sayHi(); // ошибка, функции не существует sayHi(); // ошибка, функции не существует
``` ```
То есть, иными словами, такое объявление -- ведёт себя так же как `let sayHi = function ...`, сделанное в начале блока. То есть, иными словами, такое объявление -- ведёт себя в точности как если бы `let sayHi = function() {…}` было сделано в начале блока.
## Функции через => ## Функции через =>
@ -222,8 +244,6 @@ let inc = x => x+1;
alert( inc(1) ); // 2 alert( inc(1) ); // 2
``` ```
То есть, слева от `=>` находится аргумент, а справа -- выражение, которое нужно вернуть.
Эти две записи -- примерно аналогичны: Эти две записи -- примерно аналогичны:
```js ```js
@ -232,7 +252,9 @@ let inc = x => x+1;
let inc = function(x) { return x + 1; }; let inc = function(x) { return x + 1; };
``` ```
Если аргументов несколько, то они оборачиваются в скобки, например: Как видно, `"x => x+1"` -- это уже готовая функция. Слева от `=>` находится аргумент, а справа -- выражение, которое нужно вернуть.
Если аргументов несколько, то нужно обернуть их в скобки, вот так:
```js ```js
//+ run //+ run
@ -242,16 +264,20 @@ let inc = function(x) { return x + 1; };
let sum = (a,b) => a + b; let sum = (a,b) => a + b;
*/!* */!*
// аналог с function
// let inc = function(a, b) { return a + b; };
alert( sum(1, 2) ); // 3 alert( sum(1, 2) ); // 3
``` ```
...А если совсем нет аргументов, но функцию хочется задать, то используются пустые скобки: Если нужно задать функцию без аргументов, то также используются скобки, в этом случае -- пустые:
```js ```js
//+ run //+ run
'use strict'; 'use strict';
*!* *!*
// вызов getTime() будет возвращать текущее время
let getTime = () => `${new Date().getHours()} : ${new Date().getMinutes()}`; let getTime = () => `${new Date().getHours()} : ${new Date().getMinutes()}`;
*/!* */!*
@ -277,7 +303,7 @@ let getTime = () => {
alert( getTime() ); // текущее время alert( getTime() ); // текущее время
``` ```
Заметим, что как только тело функции оборачивается в `{…}`, то оно уже автоматически перестаёт быть выражением. Такая функция должна делать явный `return`, как в примере выше, если конечно хочет что-либо возвратить. Заметим, что как только тело функции оборачивается в `{…}`, то её результат уже не возвращается автоматически. Такая функция должна делать явный `return`, как в примере выше, если конечно хочет что-либо возвратить.
Функции-стрелки очень удобны в качестве коллбеков, например: Функции-стрелки очень удобны в качестве коллбеков, например:
@ -294,7 +320,7 @@ let sorted = arr.sort( (a,b) => a - b );
alert(sorted); // 3, 5, 8 alert(sorted); // 3, 5, 8
``` ```
Такая запись -- коротка и понятна. Такая запись -- коротка и понятна. Далее мы познакомимся с дополнительными преимуществами использования функций-стрелок для этой цели.
## Функции-стрелки не имеют своего this ## Функции-стрелки не имеют своего this
@ -325,7 +351,7 @@ group.showList();
// Наш курс: Даша // Наш курс: Даша
``` ```
Здесь в `forEach` была использована функция-стрелка, поэтому `this.title` внутри -- тот же, что и во внешней функции `showList`. То есть, в данном случае -- `group.title`. Здесь в `forEach` была использована функция-стрелка, поэтому `this.title` в коллбэке -- тот же, что и во внешней функции `showList`. То есть, в данном случае -- `group.title`.
Если бы в `forEach` вместо функции-стрелки была обычная функция, то была бы ошибка: Если бы в `forEach` вместо функции-стрелки была обычная функция, то была бы ошибка:
@ -352,7 +378,7 @@ group.showList();
При запуске будет "попытка прочитать свойство `title` у `undefined`", так как `.forEach(f)` при запуске `f` не ставит `this`. То есть, `this` внутри `forEach` будет `undefined`. При запуске будет "попытка прочитать свойство `title` у `undefined`", так как `.forEach(f)` при запуске `f` не ставит `this`. То есть, `this` внутри `forEach` будет `undefined`.
[warn header="Функции стрелки нельзя запускать с `new`"] [warn header="Функции стрелки нельзя запускать с `new`"]
Отсутствие у функции-стрелки "своего `this`" влечёт за собой естественное ограничение: такие функции нельзя использовать в качестве конструктора, то есть вызывать через `new`. Отсутствие у функции-стрелки "своего `this`" влечёт за собой естественное ограничение: такие функции нельзя использовать в качестве конструктора, то есть нельзя вызывать через `new`.
[/warn] [/warn]
## Функции-стрелки не имеют своего arguments ## Функции-стрелки не имеют своего arguments
@ -404,8 +430,10 @@ sayHiDeferred("Вася"); // Привет, Вася! через 2 секунд
```js ```js
function defer(f, ms) { function defer(f, ms) {
return function() { return function() {
*!*
let args = arguments; let args = arguments;
let ctx = this; let ctx = this;
*/!*
setTimeout(function() { setTimeout(function() {
return f.apply(ctx, args); return f.apply(ctx, args);
}, ms); }, ms);
@ -413,6 +441,8 @@ function defer(f, ms) {
} }
``` ```
В этом коде пришлось создавать дополнительные переменные `args` и `ctx` для передачи внешних аргументов и контекста через замыкание.
## Итого ## Итого

View file

@ -12,7 +12,7 @@
let str = `обратные кавычки`; let str = `обратные кавычки`;
``` ```
Основные отличия от `"…"` и `'…'`: Основные отличия от двойных `"…"` и одинарных `'…'` кавычек:
<ul> <ul>
<li>**В них разрешён перевод строки.** <li>**В них разрешён перевод строки.**
@ -38,7 +38,7 @@ let oranges = 3;
alert(`${apples} + ${oranges} = ${apples + oranges}`); // 2 + 3 = 5 alert(`${apples} + ${oranges} = ${apples + oranges}`); // 2 + 3 = 5
``` ```
Как видно, можно вставлять как и значение переменной, так и более сложные выражения, вызовы функций и т.п. Это называют "интерполяцией". Как видно, при помощи `${…}` можно вставлять как и значение переменной `${apples}`, так и более сложные выражения, которые могут включать в себя операторы, вызовы функций и т.п. Такую вставку называют "интерполяцией".
</li> </li>
</ul> </ul>
@ -88,7 +88,9 @@ let str = f`Sum of ${apples} + ${oranges} =\n ${apples + oranges}!`;
Это нужно в тех случаях, когда функция шаблонизации хочет произвести обработку полностью самостоятельно (свои спец. символы?). Или же когда обработка спец. символов не нужна -- например, строка содержит "обычный текст", набранный непрограммистом без учёта спец. символов. Это нужно в тех случаях, когда функция шаблонизации хочет произвести обработку полностью самостоятельно (свои спец. символы?). Или же когда обработка спец. символов не нужна -- например, строка содержит "обычный текст", набранный непрограммистом без учёта спец. символов.
[/smart] [/smart]
Функция может как-то преобразовать строку и вернуть новый результат. Как видно, функция имеет доступ ко всему: к выражениям, к участкам текста и даже, через `strings.raw` -- к оригинально введённому тексту без учёта стандартных спец. символов.
Функция шаблонизации может как-то преобразовать строку и вернуть новый результат.
В простейшем случае можно просто "склеить" полученные фрагменты в строку: В простейшем случае можно просто "склеить" полученные фрагменты в строку:
@ -116,11 +118,11 @@ let oranges = 5;
alert( str`Sum of ${apples} + ${oranges} = ${apples + oranges}!`); 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 ```js
//+ run //+ run
@ -147,12 +149,16 @@ function i18n(strings, ...values) {
} }
// Пример использования // Пример использования
*!*
let name = "Вася"; let name = "Вася";
// Перевести строку // Перевести строку
alert( i18n`Hello, ${name}!` ); // Привет, Вася! alert( i18n`Hello, ${name}!` ); // Привет, Вася!
*/!*
``` ```
Итоговое использование выглядит довольно красиво, не правда ли?
Разумеется, эту функцию можно улучшить и расширить. Функция шаблонизации -- это своего рода "стандартный синтаксический сахар" для упрощения форматирования и парсинга строк. Разумеется, эту функцию можно улучшить и расширить. Функция шаблонизации -- это своего рода "стандартный синтаксический сахар" для упрощения форматирования и парсинга строк.
## Улучшена поддержка юникода ## Улучшена поддержка юникода
@ -253,23 +259,24 @@ alert( "\u{20331}" ); // 𠌱, китайский иероглиф с этим
Например, на основе обычного символа `a` существуют символы: `àáâäãåā`. Самые часто встречающиеся подобные сочетания имеют отдельный юникодный код. Но отнюдь не все. Например, на основе обычного символа `a` существуют символы: `àáâäãåā`. Самые часто встречающиеся подобные сочетания имеют отдельный юникодный код. Но отнюдь не все.
Для генерации произвольных сочетаний используются два юникодных символа: основа и значок. Для генерации произвольных сочетаний используются несколько юникодных символов: основа и один или несколько значков.
Например, если после символа `S` идёт символ "точка сверху" (код `\u0307`), то вместе будет "S с точкой сверху" `Ṡ`. Например, если после символа `S` идёт символ "точка сверху" (код `\u0307`), то показано это будет как "S с точкой сверху" `Ṡ`.
Если нужен ещё значок над той же буквой (или под ней) -- без проблем. Просто добавляем соответствующий символ. Если нужен ещё значок над той же буквой (или под ней) -- без проблем. Просто добавляем соответствующий символ.
К примеру, если добавить символ "точка снизу" (код `\u0323`), то будет "S с двумя точками сверху и снизу" `Ṩ` . К примеру, если добавить символ "точка снизу" (код `\u0323`), то будет "S с двумя точками сверху и снизу" `Ṩ` .
В JavaScript-строке: Пример этого символа в JavaScript-строке:
```js ```js
//+ run //+ run
alert("S\u0307\u0323"); // Ṩ alert("S\u0307\u0323"); // Ṩ
``` ```
Такая возможность добавить произвольной букве нужные значки, с одной стороны, необходима, чтобы не хранить все возможные сочетания (которых громадное число), а с другой стороны -- возникает проблемка -- можно представить одинаковый с точки зрения визуального отображения и интерпретации символ -- разными сочетаниями Unicode-кодов. Такая возможность добавить произвольной букве нужные значки, с одной стороны, необходима, а с другой стороны -- возникает проблемка: можно представить одинаковый с точки зрения визуального отображения и интерпретации символ -- разными сочетаниями Unicode-кодов.
Вот пример:
```js ```js
//+ run //+ run
alert("S\u0307\u0323"); // Ṩ alert("S\u0307\u0323"); // Ṩ
@ -278,7 +285,7 @@ alert("S\u0323\u0307"); // Ṩ
alert( "S\u0307\u0323" == "S\u0323\u0307" ); // false 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/).
## Полезные методы ## Полезные методы