diff --git a/1-js/10-es-modern/10-set-map/article.md b/1-js/10-es-modern/10-set-map/article.md
index f1973f2a..ae6ed07e 100644
--- a/1-js/10-es-modern/10-set-map/article.md
+++ b/1-js/10-es-modern/10-set-map/article.md
@@ -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 есть ряд ограничений:
+
+- Нет свойства `size`.
+- Нельзя перебрать элементы итератором или `forEach`.
+- Нет метода `clear()`.
+
+
+Иными словами, `WeakMap` работает только на запись (`set`, `delete`) и чтение (`get`, `has`) элементов по конкретному ключу, а не как полноценная коллекция. Нельзя вывести всё содержимое `WeakMap`, нет соответствующих методов.
+
+Это связано с тем, что содержимое `WeakMap` может быть модифицировано сборщиком мусора в любой момент, независимо от программиста. Сборщик мусора работает сам по себе. Он не гарантирует, что очистит объект сразу же, когда это стало возможным. Нет какого-то конкретного момента, когда такая очистка точно произойдёт -- это определяется внутренними алгоритмами сборщика и его сведениями о системе.
+
+Поэтому содержимое `WeakMap` в произвольный момент, строго говоря, не определено. Может быть, сборщик мусора уже удалил какие-то записи, а может и нет. С этим, а также с требованиями к эффективной реализации `WeakMap`, и связано отсутствие методов, осуществляющих доступ ко всем записям.
+
+То же самое относится и к `WeakSet`: можно добавлять элементы, проверять их наличие, но нельзя получить их список и даже узнать количество.
+
+Эти ограничения могут показаться неудобными, но по сути они не мешают `WeakMap/WeakSet` выполнять свою основную задачу -- быть "вторичным" хранилищем данных для объектов, актуальный список которых (и сами они) хранятся в каком-то другом месте.
+
+## Итого
+
+
+- `Map` -- коллекция записей вида `ключ: значение`, лучше `Object` тем, что перебирает всегда в порядке вставки и допускает любые ключи.
+- `Set` -- коллекция уникальных элементов, также допускает любые ключи.
+
- `WeakMap` и `WeakSet` -- "урезанные" по функционалу варианты `Map/Set`, которые позволяют только "точечно" обращаться элементам (по конкретному ключу или значению). Они не препятствуют сборке мусора, то есть если ссылка на объект осталась только в `WeakSet/WeakMap` -- он будет удалён.
+
diff --git a/1-js/10-es-modern/3-destructuring/article.md b/1-js/10-es-modern/3-destructuring/article.md
index a4d64cef..d5ea49c9 100644
--- a/1-js/10-es-modern/3-destructuring/article.md
+++ b/1-js/10-es-modern/3-destructuring/article.md
@@ -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}`);
Синтаксис:
```js
let {prop : varName = default, ...} = object
-let [var1, var2, ...rest] = array
+let [var1=default, var2, ...rest] = array
```
-Здесь двоеточие `:` задаёт отображение свойства в переменную, а `=` задаёт значение по умолчанию (если нужно).
+Здесь двоеточие `:` задаёт отображение свойства в переменную, а `=` задаёт выражение, которое будет использовано, если значение отсутствует (не указано или `undefined`).
-Объявление переменной вначале не обязательно, если переменные уже есть, но без него при деструктуризации объекта может потребоваться обернуть выражение в скобки.
+Объявление переменной в начале конструкции не обязательно. Можно использовать и существующие переменные. Однако при деструктуризации объекта может потребоваться обернуть выражение в скобки.
-Сложные объекты и массивы тоже работают, деструктуризации можно вкладывать.
+Вложенные объекты и массивы тоже работают, деструктуризации можно вкладывать друг в друга, сохраняя ту же структуру, что и исходный объект/массив.
Как мы увидим далее, деструктуризации особенно пригодятся удобны при чтении объектных параметров функций.
diff --git a/1-js/10-es-modern/4-es-function/article.md b/1-js/10-es-modern/4-es-function/article.md
index 4d05bb2e..7786262a 100644
--- a/1-js/10-es-modern/4-es-function/article.md
+++ b/1-js/10-es-modern/4-es-function/article.md
@@ -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` удобно использовать для форвардинга вызовов и создания декораторов.
diff --git a/1-js/10-es-modern/5-es-string/article.md b/1-js/10-es-modern/5-es-string/article.md
index fcf8430c..92c18d0c 100644
--- a/1-js/10-es-modern/5-es-string/article.md
+++ b/1-js/10-es-modern/5-es-string/article.md
@@ -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/).
## Полезные методы
diff --git a/1-js/10-es-modern/6-es-objects/article.md b/1-js/10-es-modern/6-es-objects/article.md
index 1b4c4a71..40772e76 100644
--- a/1-js/10-es-modern/6-es-objects/article.md
+++ b/1-js/10-es-modern/6-es-objects/article.md
@@ -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]]` ("домашний объект"), ссылающегося на объект, которому метод принадлежит.
+Теперь это уже не так, добавлены именно "методы объекта". Они, по сути, являются "свойствами-функциями, привязанными к объекту".
+
+Их особенности:
+
+
+- Более короткий синтаксис.
+- Наличие в методах специального внутреннего свойства `[[HomeObject]]` ("домашний объект"), ссылающегося на объект, которому метод принадлежит. Мы посмотрим его использование чуть дальше, в разделе про `super`.
+
Для объявления метода вместо записи `"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]
+## Итого
+
+Улучшения в описании свойств:
+
+- Запись `name: name` можно заменить на просто `name`
+- Если имя свойства находится в переменной или задано выражением `expr`, то его можно указать в квадратных скобках `[expr]`.
+- Свойства-функции можно оформить как методы: `"prop: function() {"` -> `"prop() {"`.
+
+
+В методах работает обращение к свойствам прототипа через `super.parentProperty`.
+
+Для работы с прототипом:
+
+- `Object.setPrototypeOf(obj, proto)` -- метод для установки прототипа.
+- `obj.__proto__` -- ссылка на прототип.
+
+
+Дополнительно:
+
+- Метод `Object.assign(target, src1, src2...)` -- копирует свойства из всех аргументов в первый объект.
+- Метод `Object.is(value1, value2)` проверяет два значения на равенство. В отличие от `===` считает `+0` и `-0` разными числами. А также считает, что `NaN` равно самому себе.
+
diff --git a/1-js/10-es-modern/7-es-classes/article.md b/1-js/10-es-modern/7-es-classes/article.md
index 2d4fe3ce..1ba08f25 100644
--- a/1-js/10-es-modern/7-es-classes/article.md
+++ b/1-js/10-es-modern/7-es-classes/article.md
@@ -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, "Кроль")
}
*/!*
}
diff --git a/1-js/10-es-modern/8-symbol/article.md b/1-js/10-es-modern/8-symbol/article.md
index 4fb61419..86260c05 100644
--- a/1-js/10-es-modern/8-symbol/article.md
+++ b/1-js/10-es-modern/8-symbol/article.md
@@ -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`.
Например:
@@ -115,11 +113,15 @@ for(let key in user) alert(key); // name, age
- ...и т.п.
-Смысл здесь в том, что допустим надо добавить к объекту "особый" функционал, например преобразование к примитиву или функцию итерации, или ещё что-то...
+Мы легко поймём смысл введения нового типа "символ", если поставим себя на место создателей языка 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`. Мы будем активно его использовать позже, в главе про итераторы.
-
## Итого
- Символы -- новый примитивный тип, предназначенный для уникальных идентификаторов.
- Все символы уникальны, символы с одинаковым именем не равны друг другу.
- Существует глобальный реестр символов, доступных через метод `Symbol.for(name)`. Для глобального символа можно получить имя вызовом и `Symbol.keyFor(sym)`.
-- Основная область использования символов -- это системные свойства объектов. Поддержка у них пока небольшая, но она растёт. Символы позволяют добавлять в стандарт новые "особые" свойства объектов, при этом не резервируя соответствующие названия.
+Основная область использования символов -- это системные свойства объектов. Поддержка у них пока небольшая, но она растёт. Системные символы позволяют добавлять в стандарт новые "особые" свойства объектов, при этом не резервируя соответствующие названия.
-
+Но, конечно, мы можем создавать и свои локальные и глобальные символы, использовать их в своих объектах.