es6
This commit is contained in:
parent
ff23414aed
commit
5bb3c5892a
7 changed files with 230 additions and 79 deletions
|
@ -231,6 +231,7 @@ set.forEach((value, valueAgain, set) => {
|
|||
Если поместить такие данные в `WeakMap`, а объект сделать ключом, то они будут автоматически удалены из памяти, когда удалится элемент.
|
||||
|
||||
Например:
|
||||
|
||||
```js
|
||||
// текущие активные пользователи
|
||||
let activeUsers = [
|
||||
|
@ -248,7 +249,7 @@ weakMap[activeUsers[0]] = 1;
|
|||
weakMap[activeUsers[1]] = 2;
|
||||
weakMap[activeUsers[2]] = 3;
|
||||
|
||||
alert( weakMap[activeUsers[0]].name ); // Вася
|
||||
alert( weakMap[activeUsers[0]] ); // 1
|
||||
|
||||
activeUsers.splice(0, 1); // Вася более не активный пользователь
|
||||
|
||||
|
@ -259,8 +260,30 @@ activeUsers.splice(0, 1); // Петя более не активный поль
|
|||
// weakMap теперь содержит только 1 элемент
|
||||
```
|
||||
|
||||
TODO WRITE MORE
|
||||
|
||||
У WeakMap есть ряд ограничений:
|
||||
<ul>
|
||||
<li>Нет свойства `size`.</li>
|
||||
<li>Нельзя перебрать элементы итератором или `forEach`.</li>
|
||||
<li>Нет метода `clear()`.</li>
|
||||
</ul>
|
||||
|
||||
Иными словами, `WeakMap` работает только на запись (`set`, `delete`) и чтение (`get`, `has`) элементов по конкретному ключу, а не как полноценная коллекция. Нельзя вывести всё содержимое `WeakMap`, нет соответствующих методов.
|
||||
|
||||
Это связано с тем, что содержимое `WeakMap` может быть модифицировано сборщиком мусора в любой момент, независимо от программиста. Сборщик мусора работает сам по себе. Он не гарантирует, что очистит объект сразу же, когда это стало возможным. Нет какого-то конкретного момента, когда такая очистка точно произойдёт -- это определяется внутренними алгоритмами сборщика и его сведениями о системе.
|
||||
|
||||
Поэтому содержимое `WeakMap` в произвольный момент, строго говоря, не определено. Может быть, сборщик мусора уже удалил какие-то записи, а может и нет. С этим, а также с требованиями к эффективной реализации `WeakMap`, и связано отсутствие методов, осуществляющих доступ ко всем записям.
|
||||
|
||||
То же самое относится и к `WeakSet`: можно добавлять элементы, проверять их наличие, но нельзя получить их список и даже узнать количество.
|
||||
|
||||
Эти ограничения могут показаться неудобными, но по сути они не мешают `WeakMap/WeakSet` выполнять свою основную задачу -- быть "вторичным" хранилищем данных для объектов, актуальный список которых (и сами они) хранятся в каком-то другом месте.
|
||||
|
||||
## Итого
|
||||
|
||||
<ul>
|
||||
<li>`Map` -- коллекция записей вида `ключ: значение`, лучше `Object` тем, что перебирает всегда в порядке вставки и допускает любые ключи.</li>
|
||||
<li>`Set` -- коллекция уникальных элементов, также допускает любые ключи.
|
||||
<li>`WeakMap` и `WeakSet` -- "урезанные" по функционалу варианты `Map/Set`, которые позволяют только "точечно" обращаться элементам (по конкретному ключу или значению). Они не препятствуют сборке мусора, то есть если ссылка на объект осталась только в `WeakSet/WeakMap` -- он будет удалён.</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -24,7 +24,9 @@ alert(lastName); // Кантор
|
|||
//+ run
|
||||
'use strict';
|
||||
|
||||
*!*
|
||||
let [firstName, lastName, ...rest] = "Юлий Цезарь Император Рима".split(" ");
|
||||
*/!*
|
||||
|
||||
alert(firstName); // Юлий
|
||||
alert(lastName); // Цезарь
|
||||
|
@ -33,6 +35,59 @@ alert(rest); // Император,Рима (массив из 2х элем
|
|||
|
||||
Значением `rest` будет массив из оставшихся элементов массива.
|
||||
|
||||
**Можно задать и значения по умолчанию, если массив почему-то оказался короче, чем ожидалось.**
|
||||
|
||||
Они задаются через знак `=`, например:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
'use strict';
|
||||
|
||||
*!*
|
||||
let [firstName="Гость", lastName="Анонимный"] = [];
|
||||
*/!*
|
||||
|
||||
alert(firstName); // Гость
|
||||
alert(lastName); // Анонимный
|
||||
```
|
||||
|
||||
В качестве значений по умолчанию можно использовать не только примитивы, но и выражения, содержащие вызовы функций:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
'use strict';
|
||||
|
||||
function defaultLastName() {
|
||||
return Date.now() + '-visitor';
|
||||
}
|
||||
|
||||
*!*
|
||||
// lastName получит значение, соответствующее текущей дате:
|
||||
let [firstName, lastName=defaultLastName()] = ["Вася"];
|
||||
*/!*
|
||||
|
||||
alert(firstName); // Вася
|
||||
alert(lastName); // 1436...-visitor
|
||||
```
|
||||
|
||||
Заметим, что вызов функции `defaultLastName` будет осуществлён только при необходимости, то есть если значения нет в массиве.
|
||||
|
||||
**Ненужные элементы массива можно отбросить, поставив лишнюю запятую:**
|
||||
|
||||
```js
|
||||
//+ run
|
||||
'use strict';
|
||||
|
||||
*!*
|
||||
// первый и второй элементы не нужны
|
||||
let [, , title] = "Юлий Цезарь Император Рима".split(" ");
|
||||
*/!*
|
||||
|
||||
alert(title); // Император
|
||||
```
|
||||
|
||||
В коде выше первый и второй элементы массива никуда не записались, они были отброшены. Как, впрочем, и все элементы после третьего.
|
||||
|
||||
## Деструктуризация объекта
|
||||
|
||||
Деструктуризация может "мгновенно" разобрать объект по переменным.
|
||||
|
@ -58,7 +113,7 @@ alert(`${title} ${width} ${height}`); // Меню 100 200
|
|||
|
||||
Как видно, свойства автоматически присваиваются соответствующим переменным.
|
||||
|
||||
Если хочется присвоить свойство объекта другой переменной, можно указать соответствие через двоеточие:
|
||||
Если хочется присвоить свойство объекта в переменную с другим именем, можно указать соответствие через двоеточие, вот так:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
|
@ -79,7 +134,7 @@ alert(`${title} ${w} ${h}`); // Меню 100 200
|
|||
|
||||
В примере выше свойство `width` отправилось в переменную `w`, свойство `height` -- в переменную `h`, а `title` -- в переменную с тем же названием.
|
||||
|
||||
Если каких-то свойств в объекте нет,можно указать значение по умолчанию через знак равенства `=`, вот так;
|
||||
Если каких-то свойств в объекте нет, можно указать значение по умолчанию через знак равенства `=`, вот так;
|
||||
|
||||
```js
|
||||
//+ run
|
||||
|
@ -116,7 +171,7 @@ alert(`${title} ${w} ${h}`); // Меню 100 200
|
|||
|
||||
А что, если в объекте больше значений, чем переменных? Можно ли куда-то присвоить "остаток"?
|
||||
|
||||
Такая возможность планируется в будущем стандарте ES-2016, и выглядеть она будет так:
|
||||
Такой возможности в текущем стандарте нет. Она планируется в будущем стандарте, и выглядеть она будет аналогично массивам:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
|
@ -136,9 +191,7 @@ let {title, ...size} = menuOptions;
|
|||
// size = { width: 100, height: 200} (остаток)
|
||||
```
|
||||
|
||||
Ещё раз заметим, что аналогичный массивам оператор `...` ("spread") для объектов пока не стандарт.
|
||||
|
||||
Этот код будет работать, например, при использовании Babel со включёнными экспериментальными возможностями.
|
||||
Этот код будет работать, например, при использовании Babel со включёнными экспериментальными возможностями, но ещё раз заметим, что в текущий стандарт такая возможность не вошла.
|
||||
|
||||
[smart header="Деструктуризация без объявления"]
|
||||
|
||||
|
@ -208,14 +261,14 @@ alert(`${title} ${width} ${height} ${item1} ${item2}`);
|
|||
<li>Синтаксис:
|
||||
```js
|
||||
let {prop : varName = default, ...} = object
|
||||
let [var1, var2, ...rest] = array
|
||||
let [var1=default, var2, ...rest] = array
|
||||
```
|
||||
|
||||
Здесь двоеточие `:` задаёт отображение свойства в переменную, а `=` задаёт значение по умолчанию (если нужно).
|
||||
Здесь двоеточие `:` задаёт отображение свойства в переменную, а `=` задаёт выражение, которое будет использовано, если значение отсутствует (не указано или `undefined`).
|
||||
|
||||
Объявление переменной вначале не обязательно, если переменные уже есть, но без него при деструктуризации объекта может потребоваться обернуть выражение в скобки.
|
||||
Объявление переменной в начале конструкции не обязательно. Можно использовать и существующие переменные. Однако при деструктуризации объекта может потребоваться обернуть выражение в скобки.
|
||||
</li>
|
||||
<li>Сложные объекты и массивы тоже работают, деструктуризации можно вкладывать.</li>
|
||||
<li>Вложенные объекты и массивы тоже работают, деструктуризации можно вкладывать друг в друга, сохраняя ту же структуру, что и исходный объект/массив.</li>
|
||||
</ul>
|
||||
|
||||
Как мы увидим далее, деструктуризации особенно пригодятся удобны при чтении объектных параметров функций.
|
||||
|
|
|
@ -76,10 +76,11 @@ showName("Юлий", "Цезарь", "Император", "Рима");
|
|||
|
||||
Оператор `…` собирает "все оставшиеся" аргументы, поэтому такое объявление не имеет смысла:
|
||||
```js
|
||||
function f(arg1, ...rest, arg2) {
|
||||
function f(arg1, ...rest, arg2) { // arg2 после ...rest ?!
|
||||
// будет ошибка
|
||||
}
|
||||
```
|
||||
Параметр `...rest` должен быть в конце функции.
|
||||
[/warn]
|
||||
|
||||
|
||||
|
@ -231,13 +232,15 @@ let inc = x => x+1;
|
|||
let inc = function(x) { return x + 1; };
|
||||
```
|
||||
|
||||
Если аргументов несколько, они оборачиваются в скобки, например:
|
||||
Если аргументов несколько, то они оборачиваются в скобки, например:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
'use strict';
|
||||
|
||||
*!*
|
||||
let sum = (a,b) => a + b;
|
||||
*/!*
|
||||
|
||||
alert( sum(1, 2) ); // 3
|
||||
```
|
||||
|
@ -248,31 +251,33 @@ alert( sum(1, 2) ); // 3
|
|||
//+ run
|
||||
'use strict';
|
||||
|
||||
let getTime = () =>
|
||||
`${new Date().getHours()} : ${new Date().getMinutes()}`;
|
||||
*!*
|
||||
let getTime = () => `${new Date().getHours()} : ${new Date().getMinutes()}`;
|
||||
*/!*
|
||||
|
||||
alert( getTime() ); // текущее время
|
||||
```
|
||||
|
||||
|
||||
Когда тело функции достаточно большое, то можно его обернуть в `{…}`:
|
||||
Когда тело функции достаточно большое, то можно его обернуть в фигурные скобки `{…}`:
|
||||
|
||||
|
||||
```js
|
||||
//+ run
|
||||
'use strict';
|
||||
|
||||
*!*
|
||||
let getTime = () => {
|
||||
let date = new Date();
|
||||
let hours = date.getHours();
|
||||
let minutes = date.getMinutes();
|
||||
return `${hours}:${minutes}`;
|
||||
};
|
||||
*/!*
|
||||
|
||||
alert( getTime() ); // текущее время
|
||||
```
|
||||
|
||||
Заметим, что как только тело функции оборачивается в `{…}`, то оно уже автоматически ничего не возвращает. Нужно делать явный `return`, как в примере выше.
|
||||
Заметим, что как только тело функции оборачивается в `{…}`, то оно уже автоматически перестаёт быть выражением. Такая функция должна делать явный `return`, как в примере выше, если конечно хочет что-либо возвратить.
|
||||
|
||||
Функции-стрелки очень удобны в качестве коллбеков, например:
|
||||
|
||||
|
@ -282,14 +287,18 @@ alert( getTime() ); // текущее время
|
|||
|
||||
let arr = [5, 8, 3];
|
||||
|
||||
*!*
|
||||
let sorted = arr.sort( (a,b) => a - b );
|
||||
*/!*
|
||||
|
||||
alert(sorted); // 3, 5, 8
|
||||
```
|
||||
|
||||
Такая запись -- коротка и понятна.
|
||||
|
||||
## Функции-стрелки не имеют своего this
|
||||
|
||||
Внутри `this` -- тот же, что и снаружи.
|
||||
Внутри функций-стрелок -- тот же `this`, что и снаружи.
|
||||
|
||||
Это очень удобно в обработчиках событий и коллбэках, например:
|
||||
|
||||
|
@ -316,9 +325,9 @@ group.showList();
|
|||
// Наш курс: Даша
|
||||
```
|
||||
|
||||
Здесь в `forEach` была использована функция-стрелка, поэтому `this.title` внутри -- это `group.title`.
|
||||
Здесь в `forEach` была использована функция-стрелка, поэтому `this.title` внутри -- тот же, что и во внешней функции `showList`. То есть, в данном случае -- `group.title`.
|
||||
|
||||
Если бы была обычная функция, то была бы ошибка:
|
||||
Если бы в `forEach` вместо функции-стрелки была обычная функция, то была бы ошибка:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
|
@ -340,9 +349,11 @@ let group = {
|
|||
group.showList();
|
||||
```
|
||||
|
||||
При запуске будет "попытка прочитать свойство `title` у `undefined`", так как `.forEach(f)` при запуске `f` не ставит `this`.
|
||||
При запуске будет "попытка прочитать свойство `title` у `undefined`", так как `.forEach(f)` при запуске `f` не ставит `this`. То есть, `this` внутри `forEach` будет `undefined`.
|
||||
|
||||
...Вместе с тем, отсутствие у функции-стрелки "своего `this`" влечёт за собой естественное ограничение: такие функции нельзя использовать в качестве конструктора.
|
||||
[warn header="Функции стрелки нельзя запускать с `new`"]
|
||||
Отсутствие у функции-стрелки "своего `this`" влечёт за собой естественное ограничение: такие функции нельзя использовать в качестве конструктора, то есть вызывать через `new`.
|
||||
[/warn]
|
||||
|
||||
## Функции-стрелки не имеют своего arguments
|
||||
|
||||
|
@ -362,7 +373,7 @@ function f() {
|
|||
f(1); // 1
|
||||
```
|
||||
|
||||
Вызов `showArg()` выведет `1`, получив его из аргументов функции `f`. Функция-стрелка здесь вызвана без параметров, но это не важно: `arguments` берутся из внешней функции.
|
||||
Вызов `showArg()` выведет `1`, получив его из аргументов функции `f`. Функция-стрелка здесь вызвана без параметров, но это не важно: `arguments` всегда берутся из внешней "обычной" функции.
|
||||
|
||||
Сохранение внешнего `this` и `arguments` удобно использовать для форвардинга вызовов и создания декораторов.
|
||||
|
||||
|
|
|
@ -146,6 +146,7 @@ function i18n(strings, ...values) {
|
|||
return translated.replace(/\{(\d)\}/g, (s, num) => values[num]);
|
||||
}
|
||||
|
||||
// Пример использования
|
||||
let name = "Вася";
|
||||
|
||||
// Перевести строку
|
||||
|
@ -158,7 +159,7 @@ alert( i18n`Hello, ${name}!` ); // Привет, Вася!
|
|||
|
||||
Внутренняя кодировка строк в JavaScript -- это UTF-16, то есть под каждый символ отводится ровно два байта.
|
||||
|
||||
Но под всевозможные символы всех языков мира 2 байт не хватает. Поэтому бывает так, что одному символу языка соответствует два юникодных символа (итого 4 байта), такое сочетание называют "суррогатной парой".
|
||||
Но под всевозможные символы всех языков мира 2 байт не хватает. Поэтому бывает так, что одному символу языка соответствует два юникодных символа (итого 4 байта). Такое сочетание называют "суррогатной парой".
|
||||
|
||||
Самый частый пример суррогатной пары, который можно встретить в литературе -- это китайские иероглифы.
|
||||
|
||||
|
@ -202,15 +203,19 @@ alert( '𝒳'.charCodeAt(0) + ' ' + '𝒳'.charCodeAt(1) ); // 55349 56499
|
|||
alert( '𝒳'.codePointAt(0) ); // 119987
|
||||
```
|
||||
|
||||
Метод `String.fromCodePoint(code)` корректно создаёт строку из "длинного кода":
|
||||
Метод `String.fromCodePoint(code)` корректно создаёт строку из "длинного кода", в отличие от старого `String.fromCharCode(code)`.
|
||||
|
||||
Например:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
// Правильно
|
||||
alert( String.fromCodePoint(119987) ); // 𝒳
|
||||
// Неверно!
|
||||
alert( String.fromCharCode(119987) ); // 풳
|
||||
```
|
||||
|
||||
Более старый метод `fromCharCode` взял первые два байта от числа `119987` и создал символ из них, а остальные отбросил, поэтому его результат неверен.
|
||||
Более старый метод `fromCharCode` в последней строке дал неверный результат, так как он берёт только первые два байта от числа `119987` и создаёт символ из них, а остальные отбрасывает.
|
||||
|
||||
|
||||
### \u{длинный код}
|
||||
|
@ -224,11 +229,13 @@ alert( String.fromCharCode(119987) ); // 풳
|
|||
alert( "\u2033" ); // ″, символ двойного штриха
|
||||
```
|
||||
|
||||
Синтаксис: `\uNNNN`, где `NNNN` -- четырёхзначный шестнадцатиричный код, причём он должен быть ровно четырёхзначным. Вот так -- уже не то:
|
||||
Синтаксис: `\uNNNN`, где `NNNN` -- четырёхзначный шестнадцатиричный код, причём он должен быть ровно четырёхзначным.
|
||||
|
||||
"Лишние" цифры уже не войдут в код, например:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( "\u20331" ); // ″1, символ двойного штриха, а затем 1
|
||||
alert( "\u20331" ); // Два символа: символ двойного штриха ″, а затем 1
|
||||
```
|
||||
|
||||
Чтобы вводить более длинные коды символов, добавили запись `\u{NNNNNNNN}`, где `NNNNNNNN` -- максимально восьмизначный (но можно и меньше цифр) код.
|
||||
|
@ -244,15 +251,15 @@ alert( "\u{20331}" ); // 𠌱, китайский иероглиф с этим
|
|||
|
||||
Во многих языках есть символы, которые получаются как сочетание основного символа и какого-то значка над ним или под ним.
|
||||
|
||||
Например, на основе обычного символа `a` существуют символы: `àáâäãåā`. Многие, но далеко не все такие сочетания имеют отдельный юникодный код.
|
||||
Например, на основе обычного символа `a` существуют символы: `àáâäãåā`. Самые часто встречающиеся подобные сочетания имеют отдельный юникодный код. Но отнюдь не все.
|
||||
|
||||
Для генерации произвольных сочетаний используются два юникодных символа: основа и значок.
|
||||
|
||||
Например, если после символа `S` идёт символ "точка сверху" (код `\u0307`), то вместе будет `Ṡ`.
|
||||
Например, если после символа `S` идёт символ "точка сверху" (код `\u0307`), то вместе будет "S с точкой сверху" `Ṡ`.
|
||||
|
||||
Если нужен ещё значок над той же буквой (или под ней) -- без проблем. Просто добавляем соответствующий символ.
|
||||
|
||||
К примеру, если добавить символ "точка снизу" (код `\u0323`), то будет `Ṩ` с двумя точками сверху и снизу.
|
||||
К примеру, если добавить символ "точка снизу" (код `\u0323`), то будет "S с двумя точками сверху и снизу" `Ṩ` .
|
||||
|
||||
В JavaScript-строке:
|
||||
|
||||
|
@ -261,7 +268,7 @@ alert( "\u{20331}" ); // 𠌱, китайский иероглиф с этим
|
|||
alert("S\u0307\u0323"); // Ṩ
|
||||
```
|
||||
|
||||
Такая возможность добавить произвольной букве нужные значки, с одной стороны, необходима, чтобы не хранить все возможные сочетания (которых громадное число), а с другой стороны -- возникает проблемка -- возможность представить одинаковый с точки зрения визуального отображения и интерпретации символ -- разными сочетаниями Unicode-кодов.
|
||||
Такая возможность добавить произвольной букве нужные значки, с одной стороны, необходима, чтобы не хранить все возможные сочетания (которых громадное число), а с другой стороны -- возникает проблемка -- можно представить одинаковый с точки зрения визуального отображения и интерпретации символ -- разными сочетаниями Unicode-кодов.
|
||||
|
||||
```js
|
||||
//+ run
|
||||
|
@ -271,7 +278,7 @@ alert("S\u0323\u0307"); // Ṩ
|
|||
alert( "S\u0307\u0323" == "S\u0323\u0307" ); // false
|
||||
```
|
||||
|
||||
В первой строке сначала верхняя точка, а потом -- нижняя, во второй -- наоборот. По кодам строки не равны друг другу.
|
||||
В первой строке сначала верхняя точка, а потом -- нижняя, во второй -- наоборот. По кодам строки не равны друг другу. Но символ задают один и тот же.
|
||||
|
||||
|
||||
С целью разрешить эту ситуацию, существует *юникодная нормализация*, при которой строки приводятся к единому, "нормальному", виду.
|
||||
|
@ -293,9 +300,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/).
|
||||
|
||||
## Полезные методы
|
||||
|
||||
|
|
|
@ -5,14 +5,6 @@
|
|||
|
||||
По классам -- чуть позже, в отдельном разделе, оно того заслуживает.
|
||||
|
||||
|
||||
Для объектов есть очень приятные нововведения, которые касаются объявления свойств и методов. Новый синтаксис используется и в классах.
|
||||
|
||||
Нововведения в объектах и классах очень тесно взаимосвязаны, поэтому обе этих темы объединены в один раздел.
|
||||
|
||||
Мы начнём с объектов, а затем перейдём к классам.
|
||||
|
||||
|
||||
## Короткое свойство
|
||||
|
||||
Зачастую у нас есть переменные, например, `name` и `isAdmin`, и мы хотим использовать их в объекте.
|
||||
|
@ -120,12 +112,39 @@ Object.assign(user, visitor, admin);
|
|||
alert( JSON.stringify(user) ); // user: Вася, visits: true, isAdmin: true
|
||||
```
|
||||
|
||||
## Object.is(value1, value2)
|
||||
|
||||
Возвращает `true`, если `value1 === value2`, иначе `false`.
|
||||
|
||||
Есть, однако, два отличия от обычного `===`, а именно:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
|
||||
// Сравнение +0 и -0
|
||||
alert( Object.is(+0, -0)); // false
|
||||
alert( +0 === -0 ); // true
|
||||
|
||||
// Сравнение с NaN
|
||||
alert( Object.is(NaN, NaN) ); // true
|
||||
alert( NaN === NaN ); // false
|
||||
```
|
||||
|
||||
При сравнении объектов через `Object.is` успользуется алгоритм [SameValue](http://www.ecma-international.org/ecma-262/6.0/index.html#sec-samevalue), который неявно применяется во многих других местах современного стандарта.
|
||||
|
||||
|
||||
## Методы объекта
|
||||
|
||||
Долгое время в JavaScript термин "метод объекта" был просто альтернативным названием для свойства-функции.
|
||||
|
||||
Теперь это уже не так, добавлены именно "методы объекта". Они отличаются от обычных свойств-функций наличием специального внутреннего свойства `[[HomeObject]]` ("домашний объект"), ссылающегося на объект, которому метод принадлежит.
|
||||
Теперь это уже не так, добавлены именно "методы объекта". Они, по сути, являются "свойствами-функциями, привязанными к объекту".
|
||||
|
||||
Их особенности:
|
||||
|
||||
<ol>
|
||||
<li>Более короткий синтаксис.</li>
|
||||
<li>Наличие в методах специального внутреннего свойства `[[HomeObject]]` ("домашний объект"), ссылающегося на объект, которому метод принадлежит. Мы посмотрим его использование чуть дальше, в разделе про `super`.</li>
|
||||
</ol>
|
||||
|
||||
Для объявления метода вместо записи `"prop: function() {…}"` нужно написать просто `"prop() { … }"`.
|
||||
|
||||
|
@ -149,6 +168,8 @@ let user = {
|
|||
user.sayHi(); // Вася
|
||||
```
|
||||
|
||||
Как видно, создание такого метода -- чуть короче, а обращение -- не отличается от обычной функции.
|
||||
|
||||
Также методами станут объявления геттеров `get prop()` и сеттеров `set prop()`:
|
||||
|
||||
```js
|
||||
|
@ -167,14 +188,30 @@ let user = {
|
|||
alert( user.fullName ); // Вася Петров
|
||||
```
|
||||
|
||||
Основное отличие "методов" от "просто функций" -- возможность обратиться к "родительскому методу" через ключевое слово `super`, о котором пойдёт речь дальше.
|
||||
Можно задать и метод с вычисляемым названием:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
'use strict';
|
||||
|
||||
let methodName = "getFirstName";
|
||||
|
||||
let user = {
|
||||
// в квадратных скобках может быть любое выражение,
|
||||
// которое должно вернуть название метода
|
||||
[methodName]() { // вместо [methodName]: function() {
|
||||
return "Вася";
|
||||
}
|
||||
};
|
||||
|
||||
alert( user.getFirstName() ); // Вася
|
||||
```
|
||||
|
||||
## super
|
||||
|
||||
Ссылка `super` позволяет из метода обратиться к прототипу объекта.
|
||||
Вызов `super.parentProperty` позволяет из метода объекта получить свойство его прототипа.
|
||||
|
||||
Например, в коде ниже `super.walk` из `rabbit` обращается к `animal.walk`:
|
||||
Например, в коде ниже `rabbit` наследует от `animal`. Вызов `super.walk` из метода объекта `rabbit` обращается к `animal.walk`:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
|
@ -201,7 +238,7 @@ rabbit.walk();
|
|||
|
||||
При обращении через `super` используется `[[HomeObject]]` текущего метода, и от него берётся `__proto__`. Поэтому `super` работает только внутри методов.
|
||||
|
||||
Например, тот же код, но со свойством-функцией `walk` вместо метода в `rabbit`:
|
||||
Например, если переписать этот код, оформив `rabbit.walk` как обычное свойство-функцию, то будет ошибка:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
|
@ -225,11 +262,9 @@ let rabbit = {
|
|||
rabbit.walk();
|
||||
```
|
||||
|
||||
Будет ошибка, так как `rabbit.walk` теперь обычная функция, и не имеет `[[HomeObject]]`.
|
||||
Ошибка возникнет, так как `rabbit.walk` теперь обычная функция, и не имеет `[[HomeObject]]`. В ней не работает `super`.
|
||||
|
||||
Исключением из этого правила являются функции-стрелки. В них используется `super` внешней функции.
|
||||
|
||||
Например, здесь функция-стрелка в `setTimeout` берёт внешний `super`:
|
||||
Исключением из этого правила являются функции-стрелки. В них используется `super` внешней функции. Например, здесь функция-стрелка в `setTimeout` берёт внешний `super`:
|
||||
|
||||
|
||||
```js
|
||||
|
@ -270,7 +305,6 @@ let rabbit = {
|
|||
__proto__: animal,
|
||||
walk() {
|
||||
super.walk();
|
||||
alert(this);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -278,12 +312,33 @@ let walk = rabbit.walk; // скопируем метод в переменную
|
|||
*!*
|
||||
walk();
|
||||
// I'm walking
|
||||
// undefined
|
||||
*/!*
|
||||
```
|
||||
|
||||
В примере выше метод `walk()` запускается отдельно от объекта, но всё равно сохраняется через `super` доступ к его прототипу, благодаря `[[HomeObject]]`.
|
||||
|
||||
Это относится именно к `super`. Правила `this` для методов те же, в примере выше будет `undefined`.
|
||||
Это относится именно к `super`. Правила `this` для методов те же, что и для обычных функций. В примере выше при вызове `walk()` без объекта `this` будет `undefined`.
|
||||
[/smart]
|
||||
|
||||
## Итого
|
||||
|
||||
Улучшения в описании свойств:
|
||||
<ul>
|
||||
<li>Запись `name: name` можно заменить на просто `name`</li>
|
||||
<li>Если имя свойства находится в переменной или задано выражением `expr`, то его можно указать в квадратных скобках `[expr]`.</li>
|
||||
<li>Свойства-функции можно оформить как методы: `"prop: function() {"` -> `"prop() {"`.</li>
|
||||
</ul>
|
||||
|
||||
В методах работает обращение к свойствам прототипа через `super.parentProperty`.
|
||||
|
||||
Для работы с прототипом:
|
||||
<ul>
|
||||
<li>`Object.setPrototypeOf(obj, proto)` -- метод для установки прототипа.</li>
|
||||
<li>`obj.__proto__` -- ссылка на прототип.</li>
|
||||
</ul>
|
||||
|
||||
Дополнительно:
|
||||
<ul>
|
||||
<li>Метод `Object.assign(target, src1, src2...)` -- копирует свойства из всех аргументов в первый объект.</li>
|
||||
<li>Метод `Object.is(value1, value2)` проверяет два значения на равенство. В отличие от `===` считает `+0` и `-0` разными числами. А также считает, что `NaN` равно самому себе.</li>
|
||||
</ul>
|
||||
|
|
|
@ -208,7 +208,7 @@ new Rabbit("Вася").walk();
|
|||
// and jump!
|
||||
```
|
||||
|
||||
[smart header="Обычные прототипы"]
|
||||
[smart header="Стандартная цепочка прототипов"]
|
||||
При наследовании формируется стандартная цепочка прототипов: методы `Rabbit` находятся в `Rabbit.prototype`, методы `Animal` -- в `Animal.prototype`, и они связаны через `__proto__`:
|
||||
|
||||
```js
|
||||
|
@ -226,9 +226,9 @@ alert( Rabbit.prototype.__proto__ == Animal.prototype ); // true
|
|||
|
||||
Немного особая история -- с конструктором.
|
||||
|
||||
Конструктор `constructor` родителя наследуется автоматически. То есть, если в потомке не указан свой `constructor`, то используется родительский.
|
||||
Конструктор `constructor` родителя наследуется автоматически. То есть, если в потомке не указан свой `constructor`, то используется родительский. В примере выше `Rabbit`, таким образом, использует `constructor` от `Animal`.
|
||||
|
||||
Если его переопределить, то родительский конструктор вызывается через `super()`, а не через `super.constructor()`.
|
||||
Если `constructor` переопределить, то чтобы в нём вызвать конструктор родителя -- используется синтаксис `super()` с аргументами для родителя.
|
||||
|
||||
Например, вызовем конструктор `Animal` в `Rabbit`:
|
||||
|
||||
|
@ -250,7 +250,7 @@ class Rabbit extends Animal {
|
|||
*!*
|
||||
constructor() {
|
||||
// вызвать конструктор Animal с аргументом "Кроль"
|
||||
super("Кроль"); // то же, что и Animal.apply(this, arguments)
|
||||
super("Кроль"); // то же, что и Animal.call(this, "Кроль")
|
||||
}
|
||||
*/!*
|
||||
}
|
||||
|
|
|
@ -82,6 +82,8 @@ alert( Symbol.keyFor(Symbol("name2")) ); // undefined, обычный симво
|
|||
|
||||
## Использование символов
|
||||
|
||||
Символы используются в качестве имён для методов и свойств объекта, которые являются системными, или которые необходимо скрыть.
|
||||
|
||||
Особенность символов -- в том, что если в объект записать свойство-символ, то оно не участвует в итерации:
|
||||
|
||||
```js
|
||||
|
@ -102,11 +104,7 @@ for(let key in user) alert(key); // name, age
|
|||
|
||||
В примере выше выведутся все свойства, кроме символьного.
|
||||
|
||||
Это нужно в первую очередь для различных системных свойств, которых в современном JavaScript очень много.
|
||||
|
||||
Их список есть в спецификации, в таблице [Well-known Symbols](http://www.ecma-international.org/ecma-262/6.0/index.html#table-1).
|
||||
|
||||
В спецификации принято для краткости обозначать их как '@@имя', например `@@iterator`, но доступны они как свойства `Symbol`.
|
||||
В современно JavaScript есть много системных символов. Их список есть в спецификации, в таблице [Well-known Symbols](http://www.ecma-international.org/ecma-262/6.0/index.html#table-1). В спецификации принято символы для краткости обозначать их как '@@имя', например `@@iterator`, но доступны они как свойства `Symbol`.
|
||||
|
||||
Например:
|
||||
<ul>
|
||||
|
@ -115,11 +113,15 @@ for(let key in user) alert(key); // name, age
|
|||
<li>...и т.п.</li>
|
||||
</ul>
|
||||
|
||||
Смысл здесь в том, что допустим надо добавить к объекту "особый" функционал, например преобразование к примитиву или функцию итерации, или ещё что-то...
|
||||
Мы легко поймём смысл введения нового типа "символ", если поставим себя на место создателей языка JavaScript.
|
||||
|
||||
Новый стандарт не мог просто сказать, что "свойство obj.toPrimitive теперь системное, оно делает то-то и то-то". Ведь свойство с таким именем, вполне возможно, используется в существующем коде. И он сломался бы.
|
||||
Допустим, надо добавить к объекту "особый" функционал, например, функцию, которая задаёт преобразование объекта к примитиву.
|
||||
|
||||
Поэтому ввели целый тип "символы", которые можно использовать для задания свойств, которые уникальны, не участвуют в итерации и заведомо не конфликтуют со старым кодом.
|
||||
Можно, конечно, сказать, что "свойство obj.toPrimitive теперь системное, оно делает то-то и то-то". Но это опасно. Мало ли, вполне возможно, что свойство с таким именем уже используется в существующем коде. И если сделать его системным, то он сломается.
|
||||
|
||||
Нельзя просто взять и зарезервировать какие-то свойства существующих объектов для нового функционала.
|
||||
|
||||
Поэтому ввели целый тип "символы". Их можно использовать для задания свойств, которые уникальны, не участвуют в итерации и заведомо не конфликтуют со старым кодом.
|
||||
|
||||
Например:
|
||||
```js
|
||||
|
@ -131,10 +133,13 @@ let obj = {
|
|||
[Symbol.iterator]: function() {}
|
||||
}
|
||||
|
||||
alert(obj.iterator); // 1, символ не конфликтует
|
||||
alert(obj.iterator); // 1
|
||||
alert(obj[Symbol.iterator]) // function, символ не конфликтует
|
||||
```
|
||||
|
||||
Чтобы получить символы, есть особый вызов [Object.getOwnPropertySymbols](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertySymbols).
|
||||
Выше мы использовали системный символ `Symbol.iterator`, поскольку он один из самых широко поддерживаемых. Мы подробно разберём его смысл в следующих главах, пока же -- это просто пример символа.
|
||||
|
||||
Чтобы получить все символы объекта, есть особый вызов [Object.getOwnPropertySymbols](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertySymbols).
|
||||
|
||||
Эта функция возвращает все символы в объекте. Заметим, что `getOwnPropertyNames` символы не возвращает.
|
||||
|
||||
|
@ -149,21 +154,20 @@ let obj = {
|
|||
|
||||
// один символ в объекте
|
||||
alert( Object.getOwnPropertySymbols(obj) ); // Symbol(Symbol.iterator)
|
||||
|
||||
// и одно обычное свойство
|
||||
alert( Object.getOwnPropertyNames(obj) ); // iterator
|
||||
```
|
||||
|
||||
|
||||
|
||||
Один из самых известных и полезных символов -- это `Symbol.iterator`. Мы будем активно его использовать позже, в главе про итераторы.
|
||||
|
||||
## Итого
|
||||
|
||||
<ul>
|
||||
<li>Символы -- новый примитивный тип, предназначенный для уникальных идентификаторов.</li>
|
||||
<li>Все символы уникальны, символы с одинаковым именем не равны друг другу.</li>
|
||||
<li>Существует глобальный реестр символов, доступных через метод `Symbol.for(name)`. Для глобального символа можно получить имя вызовом и `Symbol.keyFor(sym)`.</li>
|
||||
<li>Основная область использования символов -- это системные свойства объектов. Поддержка у них пока небольшая, но она растёт. Символы позволяют добавлять в стандарт новые "особые" свойства объектов, при этом не резервируя соответствующие названия.</li>
|
||||
</ul>
|
||||
|
||||
Основная область использования символов -- это системные свойства объектов. Поддержка у них пока небольшая, но она растёт. Системные символы позволяют добавлять в стандарт новые "особые" свойства объектов, при этом не резервируя соответствующие названия.
|
||||
|
||||
|
||||
Но, конечно, мы можем создавать и свои локальные и глобальные символы, использовать их в своих объектах.
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue