renovations: finished the 1st part
Before Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 45 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 32 KiB |
|
@ -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();
|
||||
rabbit.run();
|
||||
```
|
||||
|
||||
<ul>
|
||||
<li>Если метод или свойство начинается с `_`, то оно существует исключительно для внутренних нужд объекта, не нужно обращаться к нему из внешнего кода, но можно -- из методов объекта и наследников.</li>
|
||||
<li>Остальные свойства и методы -- публичные.</li>
|
||||
</ul>
|
||||
|
||||
|
|
|
@ -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 |
|
@ -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:-"Кроль"" 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 |
|
@ -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:-"Кроль"" 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 |
|
@ -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>
|
||||
|
||||
|
||||
|
|