fixes
This commit is contained in:
parent
413e552c29
commit
549af795e0
2 changed files with 143 additions and 43 deletions
|
@ -76,26 +76,23 @@ alert( user["мой зелёный крокодил"] ); // Вася
|
||||||
<li>`Object.getPrototypeOf(obj)`</li>
|
<li>`Object.getPrototypeOf(obj)`</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
В современной JavaScript также добавился сеттер:
|
В ES-2015 также добавился сеттер:
|
||||||
<ul>
|
<ul>
|
||||||
<li>`Object.setPrototypeOf(obj, newProto)`</li>
|
<li>`Object.setPrototypeOf(obj, newProto)`</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
...А также "узаконено" свойство `__proto__`, которое даёт прямой доступ к прототипу. Его, в качестве "нестандартного", но удобного способа работы с прототипом реализовали почти все браузеры (кроме IE10-), так что было принято решение добавить его в стандарт.
|
...А также "узаконено" свойство `__proto__`, которое даёт прямой доступ к прототипу. Его, в качестве "нестандартного", но удобного способа работы с прототипом реализовали почти все браузеры (кроме IE10-), так что было принято решение добавить его в стандарт.
|
||||||
|
|
||||||
По стандарту оно реализовано через геттеры-сеттеры `Object.getPrototypeOf/setPrototypeOf`.
|
|
||||||
|
|
||||||
|
|
||||||
## Object.assign
|
## Object.assign
|
||||||
|
|
||||||
|
Функция `Object.assign` получает список объектов и копирует в первый `target` свойства из остальных.
|
||||||
|
|
||||||
Синтаксис:
|
Синтаксис:
|
||||||
```js
|
```js
|
||||||
Object.assign(target, src1, src2...)
|
Object.assign(target, src1, src2...)
|
||||||
```
|
```
|
||||||
|
|
||||||
Функция `Object.assign` получает список объектов и копирует в первый `target` свойства из остальных.
|
При этом последующие свойства перезаписывают предыдущие.
|
||||||
|
|
||||||
Последующие свойства перезаписывают предыдущие.
|
|
||||||
|
|
||||||
Например:
|
Например:
|
||||||
|
|
||||||
|
@ -109,14 +106,31 @@ let admin = { isAdmin: true };
|
||||||
|
|
||||||
Object.assign(user, visitor, admin);
|
Object.assign(user, visitor, admin);
|
||||||
|
|
||||||
|
// user <- visitor <- admin
|
||||||
alert( JSON.stringify(user) ); // user: Вася, visits: true, isAdmin: true
|
alert( JSON.stringify(user) ); // user: Вася, visits: true, isAdmin: true
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Его также можно использовать для 1-уровневого клонирования объекта:
|
||||||
|
|
||||||
|
```js
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
let user = { name: "Вася", isAdmin: false };
|
||||||
|
|
||||||
|
*!*
|
||||||
|
// clone = пустой объект + все свойства user
|
||||||
|
let clone = Object.assign({}, user);
|
||||||
|
*/!*
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
## Object.is(value1, value2)
|
## Object.is(value1, value2)
|
||||||
|
|
||||||
Возвращает `true`, если `value1 === value2`, иначе `false`.
|
Новая функция для проверки равенства значений.
|
||||||
|
|
||||||
Есть, однако, два отличия от обычного `===`, а именно:
|
Возвращает `true`, если значения `value1` и `value2` равны, иначе `false`.
|
||||||
|
|
||||||
|
Она похожа на обычное строгое равенство `===`, но есть отличия:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
//+ run
|
//+ run
|
||||||
|
@ -130,20 +144,20 @@ alert( Object.is(NaN, NaN) ); // true
|
||||||
alert( NaN === NaN ); // false
|
alert( NaN === NaN ); // false
|
||||||
```
|
```
|
||||||
|
|
||||||
При сравнении объектов через `Object.is` успользуется алгоритм [SameValue](http://www.ecma-international.org/ecma-262/6.0/index.html#sec-samevalue), который неявно применяется во многих других местах современного стандарта.
|
Отличия эти в большинстве ситуаций некритичны, так что непохоже, чтобы эта функция вытеснила обычную проверку `===`. Что интересно -- этот алгоритм сравнения, который называется [SameValue](http://www.ecma-international.org/ecma-262/6.0/index.html#sec-samevalue), применяется во внутренних реализациях различных методов современного стандарта.
|
||||||
|
|
||||||
|
|
||||||
## Методы объекта
|
## Методы объекта
|
||||||
|
|
||||||
Долгое время в JavaScript термин "метод объекта" был просто альтернативным названием для свойства-функции.
|
Долгое время в JavaScript термин "метод объекта" был просто альтернативным названием для свойства-функции.
|
||||||
|
|
||||||
Теперь это уже не так, добавлены именно "методы объекта". Они, по сути, являются "свойствами-функциями, привязанными к объекту".
|
Теперь это уже не так, добавлены именно "методы объекта", которые, по сути, являются свойствами-функциями, привязанными к объекту.
|
||||||
|
|
||||||
Их особенности:
|
Их особенности:
|
||||||
|
|
||||||
<ol>
|
<ol>
|
||||||
<li>Более короткий синтаксис.</li>
|
<li>Более короткий синтаксис объявления.</li>
|
||||||
<li>Наличие в методах специального внутреннего свойства `[[HomeObject]]` ("домашний объект"), ссылающегося на объект, которому метод принадлежит. Мы посмотрим его использование чуть дальше, в разделе про `super`.</li>
|
<li>Наличие в методах специального внутреннего свойства `[[HomeObject]]` ("домашний объект"), ссылающегося на объект, которому метод принадлежит. Мы посмотрим его использование чуть дальше.</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
Для объявления метода вместо записи `"prop: function() {…}"` нужно написать просто `"prop() { … }"`.
|
Для объявления метода вместо записи `"prop: function() {…}"` нужно написать просто `"prop() { … }"`.
|
||||||
|
@ -158,7 +172,7 @@ let name = "Вася";
|
||||||
let user = {
|
let user = {
|
||||||
name,
|
name,
|
||||||
*!*
|
*!*
|
||||||
/* вместо sayHi: function() { */
|
// вместо "sayHi: function() {" пишем "sayHi() {"
|
||||||
sayHi() {
|
sayHi() {
|
||||||
alert(this.name);
|
alert(this.name);
|
||||||
}
|
}
|
||||||
|
@ -168,7 +182,7 @@ let user = {
|
||||||
user.sayHi(); // Вася
|
user.sayHi(); // Вася
|
||||||
```
|
```
|
||||||
|
|
||||||
Как видно, создание такого метода -- чуть короче, а обращение -- не отличается от обычной функции.
|
Как видно, для создания метода нужно писать меньше букв. Что же касается вызова -- он ничем не отличается от обычной функции. На данном этапе можно считать, что "метод" -- это просто сокращённый синтаксис для свойства-функции. Дополнительные возможности, которые даёт такое объявление, мы рассмотрим позже.
|
||||||
|
|
||||||
Также методами станут объявления геттеров `get prop()` и сеттеров `set prop()`:
|
Также методами станут объявления геттеров `get prop()` и сеттеров `set prop()`:
|
||||||
|
|
||||||
|
@ -207,11 +221,17 @@ let user = {
|
||||||
alert( user.getFirstName() ); // Вася
|
alert( user.getFirstName() ); // Вася
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Итак, мы рассмотрели синтаксические улучшения. Если коротко, то не надо писать слово "function". Теперь перейдём к другим отличиям.
|
||||||
|
|
||||||
## super
|
## super
|
||||||
|
|
||||||
|
В ES-2015 появилось новое ключевое слово `super`. Оно предназначено только для использования в методах объекта.
|
||||||
|
|
||||||
Вызов `super.parentProperty` позволяет из метода объекта получить свойство его прототипа.
|
Вызов `super.parentProperty` позволяет из метода объекта получить свойство его прототипа.
|
||||||
|
|
||||||
Например, в коде ниже `rabbit` наследует от `animal`. Вызов `super.walk` из метода объекта `rabbit` обращается к `animal.walk`:
|
Например, в коде ниже `rabbit` наследует от `animal`.
|
||||||
|
|
||||||
|
Вызов `super.walk()` из метода объекта `rabbit` обращается к `animal.walk()`:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
//+ run
|
//+ run
|
||||||
|
@ -236,9 +256,11 @@ let rabbit = {
|
||||||
rabbit.walk();
|
rabbit.walk();
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Как правило, это используется в [классах](/class), которые мы рассмотрим в следующем разделе, но важно понимать, что "классы" здесь на самом деле не при чём. Свойство `super` работает через прототип, на уровне методов объекта.
|
||||||
|
|
||||||
При обращении через `super` используется `[[HomeObject]]` текущего метода, и от него берётся `__proto__`. Поэтому `super` работает только внутри методов.
|
При обращении через `super` используется `[[HomeObject]]` текущего метода, и от него берётся `__proto__`. Поэтому `super` работает только внутри методов.
|
||||||
|
|
||||||
Например, если переписать этот код, оформив `rabbit.walk` как обычное свойство-функцию, то будет ошибка:
|
В частности, если переписать этот код, оформив `rabbit.walk` как обычное свойство-функцию, то будет ошибка:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
//+ run
|
//+ run
|
||||||
|
@ -253,7 +275,7 @@ let animal = {
|
||||||
let rabbit = {
|
let rabbit = {
|
||||||
__proto__: animal,
|
__proto__: animal,
|
||||||
*!*
|
*!*
|
||||||
walk: function() {
|
walk: function() { // Надо: walk() {
|
||||||
super.walk(); // Будет ошибка!
|
super.walk(); // Будет ошибка!
|
||||||
}
|
}
|
||||||
*/!*
|
*/!*
|
||||||
|
@ -289,9 +311,11 @@ let rabbit = {
|
||||||
rabbit.walk();
|
rabbit.walk();
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Ранее мы говорили о том, что у функций-стрелок нет своего `this`, `arguments`: они используют те, которые во внешней функции. Теперь к этому списку добавился ещё и `super`.
|
||||||
|
|
||||||
[smart header="Свойство `[[HomeObject]]` -- не изменяемое"]
|
[smart header="Свойство `[[HomeObject]]` -- не изменяемое"]
|
||||||
|
|
||||||
При создании метода -- он привязан к своему объекту навсегда. Технически можно даже скопировать его и запустить независимо:
|
При создании метода -- он привязан к своему объекту навсегда. Технически можно даже скопировать его и запустить отдельно, и `super` продолжит работать:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
//+ run
|
//+ run
|
||||||
|
@ -310,14 +334,14 @@ let rabbit = {
|
||||||
|
|
||||||
let walk = rabbit.walk; // скопируем метод в переменную
|
let walk = rabbit.walk; // скопируем метод в переменную
|
||||||
*!*
|
*!*
|
||||||
walk();
|
walk(); // вызовет animal.walk()
|
||||||
// I'm walking
|
// I'm walking
|
||||||
*/!*
|
*/!*
|
||||||
```
|
```
|
||||||
|
|
||||||
В примере выше метод `walk()` запускается отдельно от объекта, но всё равно сохраняется через `super` доступ к его прототипу, благодаря `[[HomeObject]]`.
|
В примере выше метод `walk()` запускается отдельно от объекта, но всё равно сохраняется через `super` доступ к его прототипу, благодаря `[[HomeObject]]`.
|
||||||
|
|
||||||
Это относится именно к `super`. Правила `this` для методов те же, что и для обычных функций. В примере выше при вызове `walk()` без объекта `this` будет `undefined`.
|
Это -- скорее технический момент, так как методы объекта, всё же, предназначены для вызова в контексте этого объекта. В частности, правила для `this` в методах -- те же, что и для обычных функций. В примере выше при вызове `walk()` без объекта `this` будет `undefined`.
|
||||||
[/smart]
|
[/smart]
|
||||||
|
|
||||||
## Итого
|
## Итого
|
||||||
|
|
|
@ -1,11 +1,20 @@
|
||||||
|
|
||||||
# Классы
|
# Классы
|
||||||
|
|
||||||
В современном JavaScript появился новый, "более красивый" синтаксис для классов. Он естественным образом продолжает синтаксис для объектов и методов, который мы рассмотрели раньше.
|
В современном JavaScript появился новый, "более красивый" синтаксис для классов.
|
||||||
|
|
||||||
|
Новая конструкция `class` -- удобный "синтаксический сахар" для задания конструктора вместе с прототипом.
|
||||||
|
|
||||||
## Class
|
## Class
|
||||||
|
|
||||||
Новая конструкция `class` -- удобный "синтаксический сахар" для задания конструктора вместе с прототипом.
|
Синтаксис для классов выглядит так:
|
||||||
|
|
||||||
|
```js
|
||||||
|
class Название [extends Родитель] {
|
||||||
|
constructor
|
||||||
|
методы
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
Например:
|
Например:
|
||||||
|
|
||||||
|
@ -29,6 +38,8 @@ let user = new User("Вася");
|
||||||
user.sayHi(); // Вася
|
user.sayHi(); // Вася
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Функция `constructor` запускается при создании `new User`, остальные методы -- записываются в `User.prototype`.
|
||||||
|
|
||||||
Это объявление примерно аналогично такому:
|
Это объявление примерно аналогично такому:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
|
@ -43,24 +54,24 @@ User.prototype.sayHi = function() {
|
||||||
|
|
||||||
В обоих случаях `new User` будет создавать объекты. Метод `sayHi` -- также в обоих случаях находится в прототипе.
|
В обоих случаях `new User` будет создавать объекты. Метод `sayHi` -- также в обоих случаях находится в прототипе.
|
||||||
|
|
||||||
Но есть и отличия при объявлении через `class`:
|
Но при объявлении через `class` есть и ряд отличий:
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<li>`User`, объявленный как класс, нельзя вызывать без `new`, будет ошибка.</li>
|
<li>`User` нельзя вызывать без `new`, будет ошибка.</li>
|
||||||
<li>Объявление класса с точки зрения области видимости ведёт себя как `let`.</li>
|
<li>Объявление класса с точки зрения области видимости ведёт себя как `let`. В частности, оно видно только текущем в блоке и только в коде, который находится ниже объявления (Function Declaration видно и до объявления).</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
Методы, объявленные внутри `class`, также имеют ряд особенностей:
|
Методы, объявленные внутри `class`, также имеют ряд особенностей:
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<li>Метод `sayHi` является именно методом, то есть имеет доступ к `super`.</li>
|
<li>Метод `sayHi` является именно методом, то есть имеет доступ к `super`.</li>
|
||||||
<li>Все методы класса работают в `use strict`, даже если он не указан.</li>
|
<li>Все методы класса работают в режиме `use strict`, даже если он не указан.</li>
|
||||||
<li>Все методы класса не перечислимы, то есть в `for..in` по объекту их не будет.</li>
|
<li>Все методы класса не перечислимы. То есть в цикле `for..in` по объекту их не будет.</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
## Class Expression
|
## Class Expression
|
||||||
|
|
||||||
Так же, как и Function Expression, классы можно задавать "инлайн" в выражении.
|
Так же, как и Function Expression, классы можно задавать "инлайн", в любом выражении и внутри вызова функции.
|
||||||
|
|
||||||
Это называется Class Expression:
|
Это называется Class Expression:
|
||||||
|
|
||||||
|
@ -94,6 +105,39 @@ new User(); // ошибка
|
||||||
|
|
||||||
В примере выше имя `User` будет доступно только внутри класса и может быть использовано, например для создания новых объектов данного типа.
|
В примере выше имя `User` будет доступно только внутри класса и может быть использовано, например для создания новых объектов данного типа.
|
||||||
|
|
||||||
|
Наиболее очевидная область применения этой возможности -- создание вспомогательного класса прямо при вызове функции.
|
||||||
|
|
||||||
|
Например, функция `createModel` в примере ниже создаёт объект по классу и данным, добавляет ему `_id` и пишет в "реестр" `allModels`:
|
||||||
|
|
||||||
|
```js
|
||||||
|
//+ run
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
let allModels = {};
|
||||||
|
|
||||||
|
function createModel(Model, ...args) {
|
||||||
|
let model = new Model(...args);
|
||||||
|
|
||||||
|
model._id = Math.random().toString(36).slice(2);
|
||||||
|
allModels[model._id] = model;
|
||||||
|
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
let user = createModel(class User {
|
||||||
|
constructor(name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
sayHi() {
|
||||||
|
alert(this.name);
|
||||||
|
}
|
||||||
|
}, "Вася");
|
||||||
|
|
||||||
|
user.sayHi(); // Вася
|
||||||
|
|
||||||
|
alert( allModels[user._id].name ); // Вася
|
||||||
|
```
|
||||||
|
|
||||||
## Геттеры, сеттеры и вычисляемые свойства
|
## Геттеры, сеттеры и вычисляемые свойства
|
||||||
|
|
||||||
В классах, как и в обычных объектах, можно объявлять геттеры и сеттеры через `get/set`, а также использовать `[…]` для свойств с вычисляемыми именами:
|
В классах, как и в обычных объектах, можно объявлять геттеры и сеттеры через `get/set`, а также использовать `[…]` для свойств с вычисляемыми именами:
|
||||||
|
@ -108,20 +152,23 @@ class User {
|
||||||
this.lastName = lastName;
|
this.lastName = lastName;
|
||||||
}
|
}
|
||||||
|
|
||||||
// геттер
|
|
||||||
*!*
|
*!*
|
||||||
|
// геттер
|
||||||
|
*/!*
|
||||||
get fullName() {
|
get fullName() {
|
||||||
return `${this.firstName} ${this.lastName}`;
|
return `${this.firstName} ${this.lastName}`;
|
||||||
}
|
}
|
||||||
*/!*
|
|
||||||
|
|
||||||
// сеттер
|
|
||||||
*!*
|
*!*
|
||||||
|
// сеттер
|
||||||
|
*/!*
|
||||||
set fullName(newValue) {
|
set fullName(newValue) {
|
||||||
[this.firstName, this.lastName] = newValue.split(' ');
|
[this.firstName, this.lastName] = newValue.split(' ');
|
||||||
}
|
}
|
||||||
*/!*
|
|
||||||
|
|
||||||
|
*!*
|
||||||
|
// вычисляемое название метода
|
||||||
|
*/!*
|
||||||
["test".toUpperCase()]: true
|
["test".toUpperCase()]: true
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -135,9 +182,23 @@ alert( user.TEST ); // true
|
||||||
|
|
||||||
При чтении `fullName` будет вызван метод `get fullName()`, при присвоении -- метод `set fullName` с новым значением.
|
При чтении `fullName` будет вызван метод `get fullName()`, при присвоении -- метод `set fullName` с новым значением.
|
||||||
|
|
||||||
|
[warn header="`class` не позволяет задавать свойства-значения"]
|
||||||
|
|
||||||
|
В синтаксисе классов, как мы видели выше, можно создавать методы. Они будут записаны в прототип, как например `User.prototype.sayHi`.
|
||||||
|
|
||||||
|
Однако, нет возможности задать в прототипе обычное значение (не функцию), такое как `User.prototype.key = "value"`.
|
||||||
|
|
||||||
|
Конечно, никто не мешает после объявления класса в прототип дописать подобные свойства, однако предполагается, что в прототипе должны быть только методы.
|
||||||
|
|
||||||
|
Если свойство-значение, всё же, необходимо, то, можно создать геттер, который будет нужное значение возвращать.
|
||||||
|
[/warn]
|
||||||
|
|
||||||
|
|
||||||
## Статические свойства
|
## Статические свойства
|
||||||
|
|
||||||
Статические свойства класса -- это свойства непосредственно класса `User`.
|
Класс, как и функция, является объектом. Статические свойства класса `User` -- это свойства непосредственно `User`, то есть доступные из него "через точку".
|
||||||
|
|
||||||
|
Для их объявления используется ключевое слово `static`.
|
||||||
|
|
||||||
Например:
|
Например:
|
||||||
|
|
||||||
|
@ -165,9 +226,22 @@ alert( user.firstName ); // Гость
|
||||||
alert( User.createGuest ); // createGuest ... (функция)
|
alert( User.createGuest ); // createGuest ... (функция)
|
||||||
```
|
```
|
||||||
|
|
||||||
Как правило, они используются для операций, не требующих наличия объекта, например -- для фабричных, как в примере выше, то есть как альтернативные варианты конструктора.
|
Как правило, они используются для операций, не требующих наличия объекта, например -- для фабричных, как в примере выше, то есть как альтернативные варианты конструктора. Или же, можно добавить метод `User.compare`, который будет сравнивать двух пользователей для целей сортировки.
|
||||||
|
|
||||||
Или же, можно добавить метод `User.compare`, который будет сравнивать двух пользователей для целей сортировки.
|
Также статическими удобно делать константы:
|
||||||
|
|
||||||
|
```js
|
||||||
|
//+ run
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
class Menu {
|
||||||
|
static get elemClass() {
|
||||||
|
return "menu"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
alert( Menu.elemClass ); // menu
|
||||||
|
```
|
||||||
|
|
||||||
## Наследование
|
## Наследование
|
||||||
|
|
||||||
|
@ -178,7 +252,7 @@ class Child extends Parent {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
В примере ниже объявлено два класса: `Animal` и наследующий от него `Rabbit`:
|
Посмотрим, как это выглядит на практике. В примере ниже объявлено два класса: `Animal` и наследующий от него `Rabbit`:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
//+ run
|
//+ run
|
||||||
|
@ -208,8 +282,9 @@ new Rabbit("Вася").walk();
|
||||||
// and jump!
|
// and jump!
|
||||||
```
|
```
|
||||||
|
|
||||||
[smart header="Стандартная цепочка прототипов"]
|
Как видим, в `new Rabbit` доступны как свои методы, так и (через `super`) методы родителя.
|
||||||
При наследовании формируется стандартная цепочка прототипов: методы `Rabbit` находятся в `Rabbit.prototype`, методы `Animal` -- в `Animal.prototype`, и они связаны через `__proto__`:
|
|
||||||
|
Это потому, что при наследовании через `extends` формируется стандартная цепочка прототипов: методы `Rabbit` находятся в `Rabbit.prototype`, методы `Animal` -- в `Animal.prototype`, и они связаны через `__proto__`:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
//+ run
|
//+ run
|
||||||
|
@ -220,15 +295,14 @@ class Rabbit extends Animal { }
|
||||||
|
|
||||||
alert( Rabbit.prototype.__proto__ == Animal.prototype ); // true
|
alert( Rabbit.prototype.__proto__ == Animal.prototype ); // true
|
||||||
```
|
```
|
||||||
[/smart]
|
|
||||||
|
|
||||||
Как видно из примера выше, методы родителя можно переопределить в наследнике. При этом для обращения к родительскому методу используют `super.method()`.
|
Как видно из примера выше, методы родителя (`walk`) можно переопределить в наследнике. При этом для обращения к родительскому методу используют `super.walk()`.
|
||||||
|
|
||||||
Немного особая история -- с конструктором.
|
Немного особая история -- с конструктором.
|
||||||
|
|
||||||
Конструктор `constructor` родителя наследуется автоматически. То есть, если в потомке не указан свой `constructor`, то используется родительский. В примере выше `Rabbit`, таким образом, использует `constructor` от `Animal`.
|
Конструктор `constructor` родителя наследуется автоматически. То есть, если в потомке не указан свой `constructor`, то используется родительский. В примере выше `Rabbit`, таким образом, использует `constructor` от `Animal`.
|
||||||
|
|
||||||
Если `constructor` переопределить, то чтобы в нём вызвать конструктор родителя -- используется синтаксис `super()` с аргументами для родителя.
|
Если же у потомка свой `constructor`, то чтобы в нём вызвать конструктор родителя -- используется синтаксис `super()` с аргументами для родителя.
|
||||||
|
|
||||||
Например, вызовем конструктор `Animal` в `Rabbit`:
|
Например, вызовем конструктор `Animal` в `Rabbit`:
|
||||||
|
|
||||||
|
@ -258,7 +332,7 @@ class Rabbit extends Animal {
|
||||||
new Rabbit().walk(); // I walk: Кроль
|
new Rabbit().walk(); // I walk: Кроль
|
||||||
```
|
```
|
||||||
|
|
||||||
...Однако, здесь есть небольшие ограничения:
|
Для такого вызова есть небольшие ограничения:
|
||||||
<ul>
|
<ul>
|
||||||
<li>Вызвать конструктор родителя можно только изнутри конструктора потомка. В частности, `super()` нельзя вызвать из произвольного метода.</li>
|
<li>Вызвать конструктор родителя можно только изнутри конструктора потомка. В частности, `super()` нельзя вызвать из произвольного метода.</li>
|
||||||
<li>В конструкторе потомка мы обязаны вызвать `super()` до обращения к `this`. До вызова `super` не существует `this`, так как по спецификации в этом случае именно `super` инициализует `this`.</li>
|
<li>В конструкторе потомка мы обязаны вызвать `super()` до обращения к `this`. До вызова `super` не существует `this`, так как по спецификации в этом случае именно `super` инициализует `this`.</li>
|
||||||
|
@ -281,6 +355,8 @@ class Rabbit extends Animal {
|
||||||
constructor() {
|
constructor() {
|
||||||
alert(this); // ошибка, this не определён!
|
alert(this); // ошибка, this не определён!
|
||||||
// обязаны вызвать super() до обращения к this
|
// обязаны вызвать super() до обращения к this
|
||||||
|
super();
|
||||||
|
// а вот здесь уже можно использовать this
|
||||||
}
|
}
|
||||||
*/!*
|
*/!*
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue