renovations: finished the 1st part

This commit is contained in:
Ilya Kantor 2015-01-17 12:50:00 +03:00
parent b4e31de966
commit 56b567a5fb
13 changed files with 245 additions and 108 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

View file

@ -1,25 +1,29 @@
# Наследование классов в JavaScript
*Наследование* -- это когда мы на основе одного объекта создаём другой, который его расширяет: добавляет свои свойства, методы и так далее.
Наследование на уровне объектов в JavaScript, как мы видели, реализуется через ссылку `__proto__`.
Теперь поговорим о наследовании на уровне классов, то есть когда объекты, создаваемые, к примеру, через `new Admin`, должны иметь все методы, которые есть у объектов, создаваемых через `new User`, и ещё какие-то свои.
[cut]
## Наследование Array от Object
Примеры организации наследования мы встречаем среди встроенных типов JavaScript. Например, массивы, которые создаются при помощи `new Array` (или квадратных скобок `[...]`), используют методы из своего прототипа `Array.prototype`, а если их там нет -- то методы из родителя `Object.prototype`.
Для реализации наследования в наших классах мы будем использовать тот же подход, который принят внутри JavaScript.
Как JavaScript это организует?
Взглянем на него ещё раз на примере `Array`, который наследует от `Object`:
Очень просто: встроенный `Array.prototype` имеет ссылку `__proto__` на `Object.prototype`:
<img src="class-inheritance-array-object.svg">
<img src="array-object-prototype.png">
<ul>
<li>Методы массивов `Array` хранятся в `Array.prototype`.</li>
<li>`Array.prototype` имеет прототипом `Object.prototype`.</li>
</ul>
То есть, как и раньше, методы хранятся в прототипах, а для наследования прототипы организованы в `__proto__`-цепочку.
Поэтому когда экземпляры класса `Array` хотят получить метод массива -- они берут его из своего прототипа, например `Array.prototype.slice`.
Самый наглядный способ это увидеть -- запустить в консоли команду `console.dir([1,2,3])`.
Если же нужен метод объекта, например, `hasOwnProperty`, то его в `Array.prototype` нет, и он берётся из `Object.prototype`.
Отличный способ "потрогать это руками" -- запустить в консоли команду `console.dir([1,2,3])`.
Вывод в Chrome будет примерно таким:
@ -33,7 +37,7 @@
## Наследование в наших классах
Применим тот же подход для наших классах. Объявим класс `Rabbit`, который будет наследовать от `Animal`.
Применим тот же подход для наших классов: объявим класс `Rabbit`, который будет наследовать от `Animal`.
Вначале создадим два этих класса по отдельности, они пока что будут совершенно независимы.
@ -42,10 +46,9 @@
```js
function Animal(name) {
this.name = name;
this.speed = 0;
}
Animal.prototype.speed = 0;
Animal.prototype.run = function(speed) {
this.speed += speed;
alert(this.name + ' бежит, скорость ' + this.speed);
@ -62,6 +65,7 @@ Animal.prototype.stop = function() {
```js
function Rabbit(name) {
this.name = name;
this.speed = 0;
}
Rabbit.prototype.jump = function() {
@ -76,15 +80,21 @@ var rabbit = new Rabbit('Кроль');
Если ещё короче -- порядок поиска свойств и методов должен быть таким: `rabbit -> Rabbit.prototype -> Animal.prototype`, по аналогии с тем, как это сделано для объектов и массивов.
Для этого можно поставить ссылку `__proto__` явно: `Rabbit.prototype.__proto__ = Animal.prototype`. Но это не будет работать в IE10-.
Для этого можно поставить ссылку `__proto__` с `Rabbit.prototype` на `Animal.prototype`.
Поэтому лучше используем функцию `Object.create`, благо она либо встроена либо легко эмулируется во всех браузерах.
Можно сделать это так:
```js
Rabbit.prototype.__proto__ = Animal.prototype;
```
Класс `Animal` остаётся без изменений, а для `Rabbit` добавим установку `prototype`:
Однако, прямой доступ к `__proto__` не поддерживается в IE10-, поэтому для поддержки этих браузеров мы используем функцию `Object.create`. Она либо встроена либо легко эмулируется во всех браузерах.
Класс `Animal` остаётся без изменений, а `Rabbit.prototype` мы будем создавать с нужным прототипом, используя `Object.create`:
```js
function Rabbit(name) {
this.name = name;
this.speed = 0;
}
*!*
@ -98,7 +108,16 @@ Rabbit.prototype.jump = function() { ... };
Теперь выглядеть иерархия будет так:
<img src="10.png">
<img src="class-inheritance-rabbit-animal.svg">
В `prototype` по умолчанию всегда находится свойство `constructor`, указывающее на функцию-конструктор. В частности, `Rabbit.prototype.constructor == Rabbit`. Если мы рассчитываем использовать это свойство, то при замене `prototype` через `Object.create` нужно его явно сохранить:
```js
Rabbit.prototype = Object.create(Animal.prototype);
Rabbit.prototype.constructor = Rabbit;
```
## Полный код наследования
Для наглядности -- вот итоговый код с двумя классами `Animal` и `Rabbit`:
@ -106,10 +125,10 @@ Rabbit.prototype.jump = function() { ... };
// 1. Конструктор Animal
function Animal(name) {
this.name = name;
this.speed = 0;
}
// 1.1. Методы и свойства по умолчанию -- в прототип
Animal.prototype.speed = 0;
// 1.1. Методы -- в прототип
Animal.prototype.stop = function() {
this.speed = 0;
@ -125,10 +144,12 @@ Animal.prototype.run = function(speed) {
// 2. Конструктор Rabbit
function Rabbit(name) {
this.name = name;
this.speed = 0;
}
// 2.1. Наследование
Rabbit.prototype = Object.create(Animal.prototype);
Rabbit.prototype.constructor = Rabbit;
// 2.2. Методы Rabbit
Rabbit.prototype.jump = function() {
@ -141,26 +162,21 @@ Rabbit.prototype.jump = function() {
Обратим внимание: `Rabbit.prototype = Object.create(proto)` присваивается сразу после объявления конструктора, иначе он перезатрёт уже записанные в прототип методы.
[warn header="Альтернативный вариант: `Rabbit.prototype = new Animal`"]
[warn header="Неправильный вариант: `Rabbit.prototype = new Animal`"]
Можно унаследовать от `Animal` и по-другому:
В некоторых устаревших руководствах предлагают вместо `Object.create(Animal.prototype)` записывать в прототип `new Animal`, вот так:
```js
// вместо Rabbit.prototype = Object.create(Animal.prototype)
Rabbit.prototype = new Animal();
```
В этом случае мы получаем в прототипе не пустой объект с прототипом `Animal.prototype`, а полноценный `Animal`.
Можно даже сконфигурировать его:
Частично, он рабочий, поскольку иерархия прототипов будет такая же, ведь `new Animal` -- это объект с прототипом `Animal.prototype`, как и `Object.create(Animal.prototype)`. Они в этом плане идентичны.
```js
Rabbit.prototype = new Animal("Зверь номер два");
```
Но у этого подхода важный недостаток. Как правило мы не хотим создавать `Animal`, а хотим только унаследовать его методы!
Теперь новые `Rabbit` будут создаваться на основе конкретного экземпляра `Animal`. Это интересная возможность, но как правило мы не хотим создавать `Animal`, а хотим только унаследовать его методы!
Более того, на практике создание объекта, скажем, меню `new Menu`, может иметь побочные эффекты, показывать что-то посетителю, и так далее, и этого мы хотели бы избежать. Поэтому рекомендуется использовать вариант с `Object.create`.
Более того, на практике создание объекта может требовать обязательных аргументов, влиять на страницу в браузере, делать запросы к серверу и что-то ещё, чего мы хотели бы избежать. Поэтому рекомендуется использовать вариант с `Object.create`.
[/warn]
## Вызов конструктора родителя
@ -170,10 +186,12 @@ Rabbit.prototype = new Animal("Зверь номер два");
```js
function Animal(name) {
this.name = name;
this.speed = 0;
}
function Rabbit(name) {
this.name = name;
this.speed = 0;
}
```
@ -206,20 +224,10 @@ Rabbit.prototype.run = function(speed) {
Вызов `rabbit.run()` теперь будет брать `run` из своего прототипа:
<img src="11.png">
<img src="class-inheritance-rabbit-run-animal.svg">
[smart]
Кстати, можно назначить метод и на уровне конкретного объекта:
```js
rabbit.run = function() {
alert('Особый метод подпрыгивания для этого кролика');
};
```
[/smart]
### Вызов метода родителя после переопределения
### Вызов метода родителя внутри своего
Более частая ситуация -- когда мы хотим не просто заменить метод на свой, а взять метод родителя и расширить его. Скажем, кролик бежит так же, как и другие звери, но время от времени подпрыгивает.
@ -230,13 +238,17 @@ rabbit.run = function() {
Rabbit.prototype.run = function() {
*!*
// вызвать метод родителя, передав ему текущие аргументы
Animal.prototype.run.apply(this, arguments);
this.jump();
*/!*
this.jump();
}
```
Обратите внимание на `apply` и явное указание контекста. Если вызвать просто `Animal.prototype.run()`, то в качестве `this` функция `run` получит `Animal.prototype`, а это неверно, нужен текущий объект.
Обратите внимание на вызов через `apply` и явное указание контекста.
Если вызвать просто `Animal.prototype.run()`, то в качестве `this` функция `run` получит `Animal.prototype`, а это неверно, нужен текущий объект.
## Итого
@ -277,44 +289,46 @@ Rabbit.prototype.run = function() {
```js
//+ run
// --------- *!*Класс-Родитель*/!* ------------
// Конструктор родителя
*!*
// --------- Класс-Родитель ------------
*/!*
// Конструктор родителя пишет свойства конкретного объекта
function Animal(name) {
this.name = name;
this.speed = 0;
}
// Методы родителя
Animal.prototype.run= function() {
// Методы хранятся в прототипе
Animal.prototype.run = function() {
alert(this + " бежит!")
}
Animal.prototype.toString = function() {
return this.name;
}
// --------- *!*Класс-потомок*/!* -----------
*!*
// --------- Класс-потомок -----------
*/!*
// Конструктор потомка
function Rabbit(name) {
Animal.apply(this, arguments);
}
// *!*Унаследовать*/!*
// Унаследовать
*!*
Rabbit.prototype = Object.create(Animal.prototype);
*/!*
// Желательно и constructor сохранить
Rabbit.prototype.constructor = Rabbit;
// Методы потомка
Rabbit.prototype.run = function() {
Animal.prototype.run.apply(this); // метод родителя вызвать
// Вызов метода родителя внутри своего
Animal.prototype.run.apply(this);
alert(this + " подпрыгивает!");
};
// Готово, можно создавать объекты
var rabbit = new Rabbit('Кроль');
rabbit.run();
```
<ul>
<li>Если метод или свойство начинается с `_`, то оно существует исключительно для внутренних нужд объекта, не нужно обращаться к нему из внешнего кода, но можно -- из методов объекта и наследников.</li>
<li>Остальные свойства и методы -- публичные.</li>
</ul>

View file

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="201px" height="298px" viewBox="0 0 201 298" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
<!-- Generator: Sketch 3.2.2 (9983) - http://www.bohemiancoding.com/sketch -->
<title>class-inheritance-array-object</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="class-inheritance-array-object" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
<g sketch:type="MSLayerGroup" transform="translate(1.000000, 1.000000)">
<g id="Rectangle-1-+-eats:-true-+-animal" transform="translate(0.000000, 133.000000)">
<rect id="Rectangle-1" stroke="#979797" sketch:type="MSShapeGroup" x="0" y="17" width="200" height="60"></rect>
<text id="slice:-function" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="normal" fill="#000000">
<tspan x="11" y="41">slice: function</tspan>
<tspan x="11" y="57">...</tspan>
</text>
<text id="Array.prototype" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-style="italic" font-weight="normal" fill="#000000">
<tspan x="0" y="10">Array.prototype</tspan>
</text>
<text id="arr" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-style="italic" font-weight="normal" fill="#000000">
<tspan x="0" y="127">arr</tspan>
</text>
</g>
<g id="Rectangle-1-+-eats:-true-+-animal-3">
<rect id="Rectangle-1" stroke="#979797" sketch:type="MSShapeGroup" x="0" y="17" width="200" height="60"></rect>
<text id="hasOwnProperty:-func" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="normal" fill="#000000">
<tspan x="11" y="41">hasOwnProperty: function</tspan>
<tspan x="11" y="57">...</tspan>
</text>
<text id="Object.prototype" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-style="italic" font-weight="normal" fill="#000000">
<tspan x="0" y="10">Object.prototype</tspan>
</text>
</g>
<g id="Rectangle-1-+-eats:-true-+-animal-2" transform="translate(0.000000, 267.000000)" stroke="#979797" sketch:type="MSShapeGroup">
<rect id="Rectangle-1" x="0" y="0" width="200" height="30"></rect>
</g>
<path d="M60.5,258.5 L60.5,217.5" id="Line" stroke="#4990E2" stroke-linecap="square" fill="#4990E2" sketch:type="MSShapeGroup"></path>
<path id="Line-decoration-1" d="M60.5,217.5 C59.45,221.28 58.55,224.52 57.5,228.3 C59.6,228.3 61.4,228.3 63.5,228.3 C62.45,224.52 61.55,221.28 60.5,217.5 C60.5,217.5 60.5,217.5 60.5,217.5 Z" stroke="#4990E2" stroke-linecap="square" fill="#4990E2"></path>
<text id="__proto__" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="normal" fill="#4990E2">
<tspan x="70" y="239">__proto__</tspan>
</text>
<path d="M60.5,126.5 L60.5,85.5" id="Line-2" stroke="#4990E2" stroke-linecap="square" fill="#4990E2" sketch:type="MSShapeGroup"></path>
<path id="Line-2-decoration-1" d="M60.5,85.5 C59.45,89.28 58.55,92.52 57.5,96.3 C59.6,96.3 61.4,96.3 63.5,96.3 C62.45,92.52 61.55,89.28 60.5,85.5 C60.5,85.5 60.5,85.5 60.5,85.5 Z" stroke="#4990E2" stroke-linecap="square" fill="#4990E2"></path>
<text id="__proto__-2" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="normal" fill="#4990E2">
<tspan x="70" y="107">__proto__</tspan>
</text>
<text id="[1,-2,-3]" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="normal" fill="#000000">
<tspan x="8" y="286">[1, 2, 3]</tspan>
</text>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4 KiB

View file

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="201px" height="293px" viewBox="0 0 201 293" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
<!-- Generator: Sketch 3.2.2 (9983) - http://www.bohemiancoding.com/sketch -->
<title>class-inheritance-rabbit-animal</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="class-inheritance-rabbit-animal" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
<g sketch:type="MSLayerGroup" transform="translate(1.000000, 1.000000)">
<g id="Rectangle-1-+-eats:-true-+-animal" transform="translate(0.000000, 128.000000)">
<rect id="Rectangle-1" stroke="#979797" sketch:type="MSShapeGroup" x="0" y="17" width="200" height="30"></rect>
<text id="jump:-function" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="normal" fill="#000000">
<tspan x="11" y="36">jump: function</tspan>
</text>
<text id="Rabbit.prototype" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-style="italic" font-weight="normal" fill="#000000">
<tspan x="0" y="10">Rabbit.prototype</tspan>
</text>
<text id="rabbit" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-style="italic" font-weight="normal" fill="#000000">
<tspan x="0" y="106">rabbit</tspan>
</text>
</g>
<g id="Rectangle-1-+-eats:-true-+-animal-3">
<rect id="Rectangle-1" stroke="#979797" sketch:type="MSShapeGroup" x="0" y="17" width="200" height="50"></rect>
<text id="run:-function" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="normal" fill="#000000">
<tspan x="11" y="38">run: function</tspan>
<tspan x="11" y="54">stop: function</tspan>
</text>
<text id="Animal.prototype" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-style="italic" font-weight="normal" fill="#000000">
<tspan x="0" y="10">Animal.prototype</tspan>
</text>
</g>
<g id="Rectangle-1-+-eats:-true-+-animal-2" transform="translate(0.000000, 242.000000)" stroke="#979797" sketch:type="MSShapeGroup">
<rect id="Rectangle-1" x="0" y="0" width="200" height="50"></rect>
</g>
<path d="M60.5,229.5 L60.5,188.5" id="Line" stroke="#4990E2" stroke-linecap="square" fill="#4990E2" sketch:type="MSShapeGroup"></path>
<path id="Line-decoration-1" d="M60.5,188.5 C59.45,192.28 58.55,195.52 57.5,199.3 C59.6,199.3 61.4,199.3 63.5,199.3 C62.45,195.52 61.55,192.28 60.5,188.5 C60.5,188.5 60.5,188.5 60.5,188.5 Z" stroke="#4990E2" stroke-linecap="square" fill="#4990E2"></path>
<text id="__proto__" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="normal" fill="#4990E2">
<tspan x="70" y="210">__proto__</tspan>
</text>
<path d="M60.5,121.5 L60.5,80.5" id="Line-2" stroke="#4990E2" stroke-linecap="square" fill="#4990E2" sketch:type="MSShapeGroup"></path>
<path id="Line-2-decoration-1" d="M60.5,80.5 C59.45,84.28 58.55,87.52 57.5,91.3 C59.6,91.3 61.4,91.3 63.5,91.3 C62.45,87.52 61.55,84.28 60.5,80.5 C60.5,80.5 60.5,80.5 60.5,80.5 Z" stroke="#4990E2" stroke-linecap="square" fill="#4990E2"></path>
<text id="__proto__-2" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="normal" fill="#4990E2">
<tspan x="70" y="102">__proto__</tspan>
</text>
<text id="name:-&quot;Кроль&quot;" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="normal" fill="#000000">
<tspan x="8" y="263">name: "Кроль"</tspan>
<tspan x="8" y="279">speed: 0</tspan>
</text>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

View file

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="201px" height="305px" viewBox="0 0 201 305" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
<!-- Generator: Sketch 3.2.2 (9983) - http://www.bohemiancoding.com/sketch -->
<title>class-inheritance-rabbit-run-animal</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="class-inheritance-rabbit-run-animal" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
<g sketch:type="MSLayerGroup" transform="translate(1.000000, 1.000000)">
<g id="Rectangle-1-+-eats:-true-+-animal" transform="translate(0.000000, 128.000000)">
<rect id="Rectangle-1" stroke="#979797" sketch:type="MSShapeGroup" x="0" y="17" width="200" height="50"></rect>
<text id="jump:-function" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="normal">
<tspan x="11" y="38" fill="#000000">jump: function</tspan>
<tspan x="11" y="54" fill="#D0011B">run: function</tspan>
</text>
<text id="Rabbit.prototype" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-style="italic" font-weight="normal" fill="#000000">
<tspan x="0" y="10">Rabbit.prototype</tspan>
</text>
<text id="rabbit" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-style="italic" font-weight="normal" fill="#000000">
<tspan x="0" y="119">rabbit</tspan>
</text>
</g>
<g id="Rectangle-1-+-eats:-true-+-animal-3">
<rect id="Rectangle-1" stroke="#979797" sketch:type="MSShapeGroup" x="0" y="17" width="200" height="50"></rect>
<text id="run:-function" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="normal" fill="#000000">
<tspan x="11" y="38">run: function</tspan>
<tspan x="11" y="54">stop: function</tspan>
</text>
<text id="Animal.prototype" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-style="italic" font-weight="normal" fill="#000000">
<tspan x="0" y="10">Animal.prototype</tspan>
</text>
</g>
<g id="Rectangle-1-+-eats:-true-+-animal-2" transform="translate(0.000000, 254.000000)" stroke="#979797" sketch:type="MSShapeGroup">
<rect id="Rectangle-1" x="0" y="0" width="200" height="50"></rect>
</g>
<path d="M60.5,244.5 L60.5,203.5" id="Line" stroke="#4990E2" stroke-linecap="square" fill="#4990E2" sketch:type="MSShapeGroup"></path>
<path id="Line-decoration-1" d="M60.5,203.5 C59.45,207.28 58.55,210.52 57.5,214.3 C59.6,214.3 61.4,214.3 63.5,214.3 C62.45,210.52 61.55,207.28 60.5,203.5 C60.5,203.5 60.5,203.5 60.5,203.5 Z" stroke="#4990E2" stroke-linecap="square" fill="#4990E2"></path>
<text id="__proto__" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="normal" fill="#4990E2">
<tspan x="70" y="225">__proto__</tspan>
</text>
<path d="M60.5,121.5 L60.5,80.5" id="Line-2" stroke="#4990E2" stroke-linecap="square" fill="#4990E2" sketch:type="MSShapeGroup"></path>
<path id="Line-2-decoration-1" d="M60.5,80.5 C59.45,84.28 58.55,87.52 57.5,91.3 C59.6,91.3 61.4,91.3 63.5,91.3 C62.45,87.52 61.55,84.28 60.5,80.5 C60.5,80.5 60.5,80.5 60.5,80.5 Z" stroke="#4990E2" stroke-linecap="square" fill="#4990E2"></path>
<text id="__proto__-2" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="normal" fill="#4990E2">
<tspan x="70" y="102">__proto__</tspan>
</text>
<text id="name:-&quot;Кроль&quot;" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="normal" fill="#000000">
<tspan x="8" y="275">name: "Кроль"</tspan>
<tspan x="8" y="291">speed: 0</tspan>
</text>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.2 KiB

View file

@ -1,18 +1,14 @@
# Проверка класса: "instanceof"
Оператор `instanceof` позволяет проверить, какому классу принадлежит объект, с учетом прототипного наследования.
[cut]
[cut]
## Алгоритм работы instanceof [#ref-instanceof]
Вызов `obj instanceof Constructor` возвращает `true`, если объект принадлежит классу `Constructor` или его родителям.
Как это часто бывает в JavaScript, здесь есть ряд тонкостей. В некоторых ситуациях, проверка может даже ошибаться!
Вначале -- простейший пример.
Вот так, по замыслу, должен работать `instanceof`:
Пример использования:
```js
//+ run
@ -21,15 +17,15 @@ function Rabbit() { }
*!*
// создаём объект
*/!*
var rabbit = new Rabbit;
var rabbit = new Rabbit();
*!*
// проверяем -- этот объект создан Rabbit?
*!*
alert(rabbit instanceof Rabbit); // true, верно
*/!*
```
Для встроенных объектов:
Массив `arr` принадлежит классу `Array`, но также и является объектом `Object`. Это верно, так как массивы наследуют от объектов:
```js
//+ run
@ -38,9 +34,7 @@ alert(arr instanceof Array); // true
alert(arr instanceof Object); // true
```
Массив `arr` принадлежит классу `Array`, но также и является объектом `Object`. Это верно, так как массивы наследуют от объектов.
По этой же логике вызов `rabbit instanceof Object` -- тоже будет `true`, так как `rabbit` является объектом.
Как это часто бывает в JavaScript, здесь есть ряд тонкостей. В некоторых ситуациях, проверка может даже ошибаться!
**Алгоритм проверки `obj instanceof Constructor`:**
@ -50,42 +44,11 @@ alert(arr instanceof Object); // true
<li>Если не совпадает, тогда заменить `obj` на `obj.__proto__` и повторить проверку на шаге 2 до тех пор, пока либо не найдется совпадение (результат `true`), либо цепочка прототипов не закончится (результат `false`).</li>
</ol>
В проверки `rabbit instanceof Rabbit`, совпадение находится тут же, на первом же шаге, так как: `rabbit.__proto__ == Rabbit.prototype`.
В проверке `rabbit instanceof Rabbit`, совпадение на первом же шаге этго алгоритма, так как: `rabbit.__proto__ == Rabbit.prototype`.
А если рассмотреть `rabbit instanceof Rabbit`, то совпадение будет найдено на следующем шаге, т.к. `rabbit.__proto__.__proto__ == Object.prototype`.
А если рассмотреть `arr instanceof Object`, то совпадение будет найдено на следующем шаге, так как `arr.__proto__.__proto__ == Object.prototype`.
[smart header="Примитивы -- не объекты, доказательство"]
Проверим, является ли число объектом `Number`:
```js
//+ run
alert(123 instanceof Number); // false, нет!
```
...С другой стороны, если создать встроенный объект `Number` (не делайте так):
```js
//+ run
alert( new Number(123) instanceof Number ); // true
```
[/smart]
[warn header="Не друзья: `instanceof` и фреймы"]
**Оператор `instanceof` не срабатывает, когда значение приходит из другого окна или фрейма.**
Например, массив, который создан в ифрейме и передан родительскому окну -- будет массивом *в том ифрейме*, но не в родительском окне. Проверка `instanceof Array` в родительском окне вернёт `false`.
Вообще, у каждого окна и фрейма -- своя иерархия объектов и свой `window` .
Как правило, эта проблема возникает со встроенными объектами, в этом случае используется проверка внутреннего свойства `[[Class]]`. Более подробно это описано в главе [](/class-property).
[/warn]
[smart header="Вызов `instanceof Rabbit` не использует саму функцию `Rabbit`"]
Забавно, что сама функция-констуктор не участвует в процессе проверки!
Используется только цепочка прототипов для проверяемого объекта и `Rabbit.prototype`.
Забавно, что сама функция-констуктор не участвует в процессе проверки! Важна только цепочка прототипов для проверяемого объекта.
Это может приводить к забавному результату и даже ошибкам в проверке при изменении `prototype`, например:
@ -104,8 +67,19 @@ alert( rabbit instanceof Rabbit ); // false
*/!*
```
Стоит ли говорить, что это один из доводов для того, чтобы никогда не менять `prototype`? Так сказать, во избежание...
[/smart]
Стоит ли говорить, что это один из доводов для того, чтобы никогда не менять `prototype`? Так сказать, во избежание.
[warn header="Не друзья: `instanceof` и фреймы"]
Оператор `instanceof` не срабатывает, когда значение приходит из другого окна или фрейма.
Например, массив, который создан в ифрейме и передан родительскому окну -- будет массивом *в том ифрейме*, но не в родительском окне. Проверка `instanceof Array` в родительском окне вернёт `false`.
Вообще, у каждого окна и фрейма -- своя иерархия объектов и свой `window` .
Как правило, эта проблема возникает со встроенными объектами, в этом случае используется проверка внутреннего свойства `[[Class]]`. Более подробно это описано в главе [](/class-property).
[/warn]
## instanceof + наследование + try..catch = ♡
@ -232,9 +206,7 @@ try {
## Итого
<ul>
<li>Оператор `obj instanceof Func` проверяет тот факт, что `obj` является результатом вызова `new Func`. Он учитывает цепочку `__proto__`, поэтому наследование поддерживается. </li>
<li>Оператор `obj instanceof Func` проверяет тот факт, что `obj` является результатом вызова `new Func`. Он учитывает цепочку `__proto__`, поэтому наследование поддерживается.</li>
<li>Оператор `instanceof` не сможет проверить тип значения, если объект создан в одном окне/фрейме, а проверяется в другом. Это потому, что в каждом окне -- своя иерархия объектов. Для точной проверки типов встроенных объектов можно использовать свойство `[[Class]]`.</li>
<li>Оператор `instanceof` особенно востребован в случаях, когда мы работаем с иерархиями классов. Это наилучший способ проверить принадлежность тому или иному классу с учётом наследования.</li>
</ul>