diff --git a/1-js/9-prototypes/4-classes/7.png b/1-js/9-prototypes/4-classes/7.png
deleted file mode 100755
index 9a43f55e..00000000
Binary files a/1-js/9-prototypes/4-classes/7.png and /dev/null differ
diff --git a/1-js/9-prototypes/4-classes/7@2x.png b/1-js/9-prototypes/4-classes/7@2x.png
deleted file mode 100755
index add9a340..00000000
Binary files a/1-js/9-prototypes/4-classes/7@2x.png and /dev/null differ
diff --git a/1-js/9-prototypes/5-class-inheritance/10.png b/1-js/9-prototypes/5-class-inheritance/10.png
deleted file mode 100755
index f5fa8c74..00000000
Binary files a/1-js/9-prototypes/5-class-inheritance/10.png and /dev/null differ
diff --git a/1-js/9-prototypes/5-class-inheritance/10@2x.png b/1-js/9-prototypes/5-class-inheritance/10@2x.png
deleted file mode 100755
index 53c247af..00000000
Binary files a/1-js/9-prototypes/5-class-inheritance/10@2x.png and /dev/null differ
diff --git a/1-js/9-prototypes/5-class-inheritance/11.png b/1-js/9-prototypes/5-class-inheritance/11.png
deleted file mode 100755
index 826d0239..00000000
Binary files a/1-js/9-prototypes/5-class-inheritance/11.png and /dev/null differ
diff --git a/1-js/9-prototypes/5-class-inheritance/11@2x.png b/1-js/9-prototypes/5-class-inheritance/11@2x.png
deleted file mode 100755
index 995a70eb..00000000
Binary files a/1-js/9-prototypes/5-class-inheritance/11@2x.png and /dev/null differ
diff --git a/1-js/9-prototypes/5-class-inheritance/array-object-prototype.png b/1-js/9-prototypes/5-class-inheritance/array-object-prototype.png
deleted file mode 100755
index 1c732c4b..00000000
Binary files a/1-js/9-prototypes/5-class-inheritance/array-object-prototype.png and /dev/null differ
diff --git a/1-js/9-prototypes/5-class-inheritance/array-object-prototype@2x.png b/1-js/9-prototypes/5-class-inheritance/array-object-prototype@2x.png
deleted file mode 100755
index 4905d1e2..00000000
Binary files a/1-js/9-prototypes/5-class-inheritance/array-object-prototype@2x.png and /dev/null differ
diff --git a/1-js/9-prototypes/5-class-inheritance/article.md b/1-js/9-prototypes/5-class-inheritance/article.md
index 32995e28..c4d13e09 100644
--- a/1-js/9-prototypes/5-class-inheritance/article.md
+++ b/1-js/9-prototypes/5-class-inheritance/article.md
@@ -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`:
+
-
+
+
Методы массивов `Array` хранятся в `Array.prototype`.
+
`Array.prototype` имеет прототипом `Object.prototype`.
+
-То есть, как и раньше, методы хранятся в прототипах, а для наследования прототипы организованы в `__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() { ... };
Теперь выглядеть иерархия будет так:
-
+
+
+В `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` из своего прототипа:
-
+
-[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();
```
-
-
Если метод или свойство начинается с `_`, то оно существует исключительно для внутренних нужд объекта, не нужно обращаться к нему из внешнего кода, но можно -- из методов объекта и наследников.
-
Остальные свойства и методы -- публичные.
-
diff --git a/1-js/9-prototypes/5-class-inheritance/class-inheritance-array-object.svg b/1-js/9-prototypes/5-class-inheritance/class-inheritance-array-object.svg
new file mode 100644
index 00000000..96e1a15d
--- /dev/null
+++ b/1-js/9-prototypes/5-class-inheritance/class-inheritance-array-object.svg
@@ -0,0 +1,50 @@
+
+
\ No newline at end of file
diff --git a/1-js/9-prototypes/5-class-inheritance/class-inheritance-rabbit-animal.svg b/1-js/9-prototypes/5-class-inheritance/class-inheritance-rabbit-animal.svg
new file mode 100644
index 00000000..15b609ee
--- /dev/null
+++ b/1-js/9-prototypes/5-class-inheritance/class-inheritance-rabbit-animal.svg
@@ -0,0 +1,50 @@
+
+
\ No newline at end of file
diff --git a/1-js/9-prototypes/5-class-inheritance/class-inheritance-rabbit-run-animal.svg b/1-js/9-prototypes/5-class-inheritance/class-inheritance-rabbit-run-animal.svg
new file mode 100644
index 00000000..6baf41fd
--- /dev/null
+++ b/1-js/9-prototypes/5-class-inheritance/class-inheritance-rabbit-run-animal.svg
@@ -0,0 +1,51 @@
+
+
\ No newline at end of file
diff --git a/1-js/9-prototypes/6-instanceof/article.md b/1-js/9-prototypes/6-instanceof/article.md
index 827ad460..dff06ccd 100644
--- a/1-js/9-prototypes/6-instanceof/article.md
+++ b/1-js/9-prototypes/6-instanceof/article.md
@@ -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
Если не совпадает, тогда заменить `obj` на `obj.__proto__` и повторить проверку на шаге 2 до тех пор, пока либо не найдется совпадение (результат `true`), либо цепочка прототипов не закончится (результат `false`).
-В проверки `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 {
## Итого
-
Оператор `obj instanceof Func` проверяет тот факт, что `obj` является результатом вызова `new Func`. Он учитывает цепочку `__proto__`, поэтому наследование поддерживается.
+
Оператор `obj instanceof Func` проверяет тот факт, что `obj` является результатом вызова `new Func`. Он учитывает цепочку `__proto__`, поэтому наследование поддерживается.
Оператор `instanceof` не сможет проверить тип значения, если объект создан в одном окне/фрейме, а проверяется в другом. Это потому, что в каждом окне -- своя иерархия объектов. Для точной проверки типов встроенных объектов можно использовать свойство `[[Class]]`.
Оператор `instanceof` особенно востребован в случаях, когда мы работаем с иерархиями классов. Это наилучший способ проверить принадлежность тому или иному классу с учётом наследования.