This commit is contained in:
Ilya Kantor 2015-07-08 09:53:06 +03:00
parent ff23414aed
commit 5bb3c5892a
7 changed files with 230 additions and 79 deletions

View file

@ -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>

View file

@ -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>
Как мы увидим далее, деструктуризации особенно пригодятся удобны при чтении объектных параметров функций.

View file

@ -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` удобно использовать для форвардинга вызовов и создания декораторов.

View file

@ -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/).
## Полезные методы

View file

@ -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>

View file

@ -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, "Кроль")
}
*/!*
}

View file

@ -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>
Основная область использования символов -- это системные свойства объектов. Поддержка у них пока небольшая, но она растёт. Системные символы позволяют добавлять в стандарт новые "особые" свойства объектов, при этом не резервируя соответствующие названия.
Но, конечно, мы можем создавать и свои локальные и глобальные символы, использовать их в своих объектах.