ÐаÑледование клаÑÑов â ÑÑо ÑпоÑоб ÑаÑÑиÑÐµÐ½Ð¸Ñ Ð¾Ð´Ð½Ð¾Ð³Ð¾ клаÑÑа дÑÑгим клаÑÑом.
Таким обÑазом, Ð¼Ñ Ð¼Ð¾Ð¶ÐµÐ¼ добавиÑÑ Ð½Ð¾Ð²Ñй ÑÑнкÑионал к Ñже ÑÑÑеÑÑвÑÑÑемÑ.
ÐлÑÑевое Ñлово «extends»
ÐопÑÑÑим, Ñ Ð½Ð°Ñ ÐµÑÑÑ ÐºÐ»Ð°ÑÑ Animal:
class Animal {
constructor(name) {
this.speed = 0;
this.name = name;
}
run(speed) {
this.speed = speed;
alert(`${this.name} Ð±ÐµÐ¶Ð¸Ñ Ñо ÑкоÑоÑÑÑÑ ${this.speed}.`);
}
stop() {
this.speed = 0;
alert(`${this.name} ÑÑÐ¾Ð¸Ñ Ð½ÐµÐ¿Ð¾Ð´Ð²Ð¸Ð¶Ð½Ð¾.`);
}
}
let animal = new Animal("Ðой пиÑомеÑ");
ÐÐ¾Ñ ÐºÐ°Ðº Ð¼Ñ Ð¼Ð¾Ð¶ÐµÐ¼ пÑедÑÑавиÑÑ Ð¾Ð±ÑÐµÐºÑ animal и клаÑÑ Animal гÑаÑиÑеÑки:
â¦Ð Ð¼Ñ Ñ
оÑели Ð±Ñ ÑоздаÑÑ ÐµÑÑ Ð¾Ð´Ð¸Ð½ class Rabbit.
ÐоÑколÑÐºÑ ÐºÑолики â ÑÑо живоÑнÑе, клаÑÑ Rabbit должен бÑÑÑ Ð¾Ñнован на Animal, и имеÑÑ Ð´Ð¾ÑÑÑп к меÑодам живоÑнÑÑ
, Ñак ÑÑÐ¾Ð±Ñ ÐºÑолики могли делаÑÑ Ñо, ÑÑо могÑÑ Ð´ÐµÐ»Ð°ÑÑ Â«Ð¾Ð±Ñие» живоÑнÑе.
СинÑакÑÐ¸Ñ Ð´Ð»Ñ ÑаÑÑиÑÐµÐ½Ð¸Ñ Ð´ÑÑгого клаÑÑа ÑледÑÑÑий: class Child extends Parent.
ÐавайÑе Ñоздадим class Rabbit, коÑоÑÑй наÑледÑеÑÑÑ Ð¾Ñ Animal:
class Rabbit extends Animal {
hide() {
alert(`${this.name} пÑÑÑеÑÑÑ!`);
}
}
let rabbit = new Rabbit("ÐелÑй кÑолик");
rabbit.run(5); // ÐелÑй кÑолик Ð±ÐµÐ¶Ð¸Ñ Ñо ÑкоÑоÑÑÑÑ 5.
rabbit.hide(); // ÐелÑй кÑолик пÑÑÑеÑÑÑ!
ÐбÑÐµÐºÑ ÐºÐ»Ð°ÑÑа Rabbit Ð¸Ð¼ÐµÐµÑ Ð´Ð¾ÑÑÑп как к меÑодам Rabbit, Ñаким как rabbit.hide(), Ñак и к меÑодам Animal, Ñаким как rabbit.run().
ÐнÑÑÑи клÑÑевое Ñлово extends ÑабоÑÐ°ÐµÑ Ð¿Ð¾ ÑÑаÑой добÑой меÑ
анике пÑоÑоÑипов. Ðно ÑÑÑÐ°Ð½Ð°Ð²Ð»Ð¸Ð²Ð°ÐµÑ Rabbit.prototype.[[Prototype]] в Animal.prototype. Таким обÑазом, еÑли меÑода не оказалоÑÑ Ð² Rabbit.prototype, JavaScript беÑÐµÑ ÐµÐ³Ð¾ из Animal.prototype.
ÐапÑимеÑ, ÑÑÐ¾Ð±Ñ Ð½Ð°Ð¹Ñи меÑод rabbit.run, движок пÑовеÑÑÐµÑ (ÑÐ½Ð¸Ð·Ñ Ð²Ð²ÐµÑÑ
на каÑÑинке):
- ÐбÑекÑ
rabbit(не имееÑrun). - Ðго пÑоÑоÑип, Ñо еÑÑÑ
Rabbit.prototype(имееÑhide, но не имееÑrun). - Ðго пÑоÑоÑип, Ñо еÑÑÑ (вÑледÑÑвие
extends)Animal.prototype, в коÑоÑом, наконеÑ, еÑÑÑ Ð¼ÐµÑодrun.
Ðак Ð¼Ñ Ð¿Ð¾Ð¼Ð½Ð¸Ð¼ из Ð³Ð»Ð°Ð²Ñ ÐÑÑÑоеннÑе пÑоÑоÑипÑ, Ñам JavaScript иÑполÑзÑÐµÑ Ð½Ð°Ñледование на пÑоÑоÑипаÑ
Ð´Ð»Ñ Ð²ÑÑÑоеннÑÑ
обÑекÑов. ÐапÑимеÑ, Date.prototype.[[Prototype]] ÑвлÑеÑÑÑ Object.prototype, поÑÑÐ¾Ð¼Ñ Ñ Ð´Ð°Ñ ÐµÑÑÑ ÑнивеÑÑалÑнÑе меÑÐ¾Ð´Ñ Ð¾Ð±ÑекÑа.
extends ÑазÑеÑÐµÐ½Ñ Ð»ÑбÑе вÑÑажениÑСинÑакÑÐ¸Ñ ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ ÐºÐ»Ð°ÑÑа допÑÑÐºÐ°ÐµÑ ÑказÑваÑÑ Ð¿Ð¾Ñле extends не ÑолÑко клаÑÑ, но и лÑбое вÑÑажение.
ÐÑÐ¸Ð¼ÐµÑ Ð²Ñзова ÑÑнкÑии, коÑоÑÐ°Ñ Ð³ÐµÐ½ÐµÑиÑÑÐµÑ ÑодиÑелÑÑкий клаÑÑ:
function f(phrase) {
return class {
sayHi() { alert(phrase); }
};
}
class User extends f("ÐÑивеÑ") {}
new User().sayHi(); // ÐÑивеÑ
ÐдеÑÑ class User наÑледÑÐµÑ Ð¾Ñ ÑезÑлÑÑаÑа вÑзова f("ÐÑивеÑ").
ÐÑо Ð¼Ð¾Ð¶ÐµÑ Ð±ÑÑÑ Ð¿Ð¾Ð»ÐµÐ·Ð½Ð¾ Ð´Ð»Ñ Ð¿ÑодвинÑÑÑÑ Ð¿ÑиÑмов пÑоекÑиÑованиÑ, где Ð¼Ñ Ð¼Ð¾Ð¶ÐµÐ¼ иÑполÑзоваÑÑ ÑÑнкÑии Ð´Ð»Ñ Ð³ÐµÐ½ÐµÑаÑии клаÑÑов в завиÑимоÑÑи Ð¾Ñ Ð¼Ð½Ð¾Ð³Ð¸Ñ ÑÑловий и заÑем наÑледоваÑÑ Ð¸Ñ .
ÐеÑеопÑеделение меÑодов
ТепеÑÑ Ð´Ð°Ð²Ð°Ð¹Ñе пÑодвинемÑÑ Ð´Ð°Ð»ÑÑе и пеÑеопÑеделим меÑод. Ðо ÑмолÑÐ°Ð½Ð¸Ñ Ð²Ñе меÑодÑ, не ÑказаннÑе в клаÑÑе Rabbit, беÑÑÑÑÑ Ð½ÐµÐ¿Ð¾ÑÑедÑÑвенно «как еÑÑÑ» из клаÑÑа Animal.
Ðо еÑли Ð¼Ñ Ñкажем в Rabbit ÑобÑÑвеннÑй меÑод, напÑÐ¸Ð¼ÐµÑ stop(), Ñо он бÑÐ´ÐµÑ Ð¸ÑполÑзован вмеÑÑо него:
class Rabbit extends Animal {
stop() {
// ...ÑепеÑÑ ÑÑо бÑÐ´ÐµÑ Ð¸ÑполÑзоваÑÑÑÑ Ð´Ð»Ñ rabbit.stop()
// вмеÑÑо stop() из клаÑÑа Animal
}
}
ÐпÑоÑем, обÑÑно Ð¼Ñ Ð½Ðµ Ñ Ð¾Ñим полноÑÑÑÑ Ð·Ð°Ð¼ÐµÐ½Ð¸ÑÑ ÑодиÑелÑÑкий меÑод, а ÑкоÑее Ñ Ð¾Ñим ÑделаÑÑ Ð½Ð¾Ð²Ñй на его оÑнове, изменÑÑ Ð¸Ð»Ð¸ ÑаÑÑиÑÑÑ ÐµÐ³Ð¾ ÑÑнкÑионалÑноÑÑÑ. ÐÑ Ð´ÐµÐ»Ð°ÐµÐ¼ ÑÑо-Ñо в наÑем меÑоде и вÑзÑваем ÑодиÑелÑÑкий меÑод до/поÑле или в пÑоÑеÑÑе.
У клаÑÑов еÑÑÑ ÐºÐ»ÑÑевое Ñлово "super" Ð´Ð»Ñ ÑакиÑ
ÑлÑÑаев.
super.method(...)вÑзÑÐ²Ð°ÐµÑ ÑодиÑелÑÑкий меÑод.super(...)Ð´Ð»Ñ Ð²Ñзова ÑодиÑелÑÑкого конÑÑÑÑкÑоÑа (ÑабоÑÐ°ÐµÑ ÑолÑко внÑÑÑи наÑего конÑÑÑÑкÑоÑа).
ÐÑÑÑÑ Ð½Ð°Ñ ÐºÑолик авÑомаÑиÑеÑки пÑÑÑеÑÑÑ Ð¿Ñи оÑÑановке:
class Animal {
constructor(name) {
this.speed = 0;
this.name = name;
}
run(speed) {
this.speed = speed;
alert(`${this.name} Ð±ÐµÐ¶Ð¸Ñ Ñо ÑкоÑоÑÑÑÑ ${this.speed}.`);
}
stop() {
this.speed = 0;
alert(`${this.name} ÑÑоиÑ.`);
}
}
class Rabbit extends Animal {
hide() {
alert(`${this.name} пÑÑÑеÑÑÑ!`);
}
stop() {
super.stop(); // вÑзÑваем ÑодиÑелÑÑкий меÑод stop
this.hide(); // и заÑем hide
}
}
let rabbit = new Rabbit("ÐелÑй кÑолик");
rabbit.run(5); // ÐелÑй кÑолик Ð±ÐµÐ¶Ð¸Ñ Ñо ÑкоÑоÑÑÑÑ 5.
rabbit.stop(); // ÐелÑй кÑолик ÑÑоиÑ. ÐелÑй кÑолик пÑÑÑеÑÑÑ!
ТепеÑÑ Ñ ÐºÐ»Ð°ÑÑа Rabbit еÑÑÑ Ð¼ÐµÑод stop, коÑоÑÑй вÑзÑÐ²Ð°ÐµÑ ÑодиÑелÑÑкий super.stop() в пÑоÑеÑÑе вÑполнениÑ.
superÐак ÑпоминалоÑÑ Ð² главе ÐовÑоÑÑем ÑÑÑелоÑнÑе ÑÑнкÑии, ÑÑÑелоÑнÑе ÑÑнкÑии не имеÑÑ super.
ÐÑи обÑаÑении к super ÑÑÑелоÑной ÑÑнкÑии он беÑÑÑÑÑ Ð¸Ð· внеÑней ÑÑнкÑии:
class Rabbit extends Animal {
stop() {
setTimeout(() => super.stop(), 1000); // вÑзÑÐ²Ð°ÐµÑ ÑодиÑелÑÑкий stop поÑле 1 ÑекÑндÑ
}
}
РпÑимеÑе super в ÑÑÑелоÑной ÑÑнкÑии ÑÐ¾Ñ Ð¶Ðµ ÑамÑй, ÑÑо и в stop(), поÑÑÐ¾Ð¼Ñ Ð¼ÐµÑод оÑÑабаÑÑÐ²Ð°ÐµÑ ÐºÐ°Ðº и ожидаеÑÑÑ. ÐÑли Ð±Ñ Ð¼Ñ Ñказали здеÑÑ Â«Ð¾Ð±ÑÑнÑÑ» ÑÑнкÑиÑ, бÑла Ð±Ñ Ð¾Ñибка:
// Unexpected super
setTimeout(function() { super.stop() }, 1000);
ÐеÑеопÑеделение конÑÑÑÑкÑоÑа
С конÑÑÑÑкÑоÑами немного Ñложнее.
Ðо ÑиÑ
Ð¿Ð¾Ñ Ñ Rabbit не бÑло Ñвоего конÑÑÑÑкÑоÑа.
СоглаÑно ÑпеÑиÑикаÑии, еÑли клаÑÑ ÑаÑÑиÑÑÐµÑ Ð´ÑÑгой клаÑÑ Ð¸ не Ð¸Ð¼ÐµÐµÑ ÐºÐ¾Ð½ÑÑÑÑкÑоÑа, Ñо авÑомаÑиÑеÑки ÑоздаÑÑÑÑ Ñакой «пÑÑÑой» конÑÑÑÑкÑоÑ:
class Rabbit extends Animal {
// генеÑиÑÑеÑÑÑ Ð´Ð»Ñ ÐºÐ»Ð°ÑÑов-поÑомков, Ñ ÐºÐ¾ÑоÑÑÑ
Ð½ÐµÑ Ñвоего конÑÑÑÑкÑоÑа
constructor(...args) {
super(...args);
}
}
Ðак Ð¼Ñ Ð²Ð¸Ð´Ð¸Ð¼, он пÑоÑÑо вÑзÑÐ²Ð°ÐµÑ ÐºÐ¾Ð½ÑÑÑÑкÑÐ¾Ñ ÑодиÑелÑÑкого клаÑÑа. Так бÑÐ´ÐµÑ Ð¿ÑоиÑÑ Ð¾Ð´Ð¸ÑÑ, пока Ð¼Ñ Ð½Ðµ Ñоздадим ÑобÑÑвеннÑй конÑÑÑÑкÑоÑ.
ÐавайÑе добавим конÑÑÑÑкÑÐ¾Ñ Ð´Ð»Ñ Rabbit. Ðн бÑÐ´ÐµÑ ÑÑÑанавливаÑÑ earLength в дополнение к name:
class Animal {
constructor(name) {
this.speed = 0;
this.name = name;
}
// ...
}
class Rabbit extends Animal {
constructor(name, earLength) {
this.speed = 0;
this.name = name;
this.earLength = earLength;
}
// ...
}
// Ðе ÑабоÑаеÑ!
let rabbit = new Rabbit("ÐелÑй кÑолик", 10); // Error: this is not defined.
УпÑ! ÐÑи Ñоздании кÑолика â оÑибка! ЧÑо не Ñак?
ÐÑли коÑоÑко, Ñо:
- ÐонÑÑÑÑкÑоÑÑ Ð² наÑледÑемÑÑ
клаÑÑаÑ
Ð´Ð¾Ð»Ð¶Ð½Ñ Ð¾Ð±ÑзаÑелÑно вÑзÑваÑÑ
super(...), и (!) делаÑÑ ÑÑо пеÑед иÑполÑзованиемthis.
â¦Ðо поÑемÑ? ЧÑо пÑоиÑÑ Ð¾Ð´Ð¸Ñ? ÐÑо ÑÑебование кажеÑÑÑ Ð´Ð¾Ð²Ð¾Ð»Ñно ÑÑÑаннÑм.
ÐонеÑно, вÑÐµÐ¼Ñ ÐµÑÑÑ ÑÐ²Ð¾Ñ Ð¾Ð±ÑÑÑнение. ÐавайÑе ÑглÑбимÑÑ Ð² деÑали, ÑÑÐ¾Ð±Ñ Ð²Ñ Ð´ÐµÐ¹ÑÑвиÑелÑно понÑли, ÑÑо пÑоиÑÑ Ð¾Ð´Ð¸Ñ.
Ð JavaScript ÑÑÑеÑÑвÑÐµÑ ÑазлиÑие Ð¼ÐµÐ¶Ð´Ñ Â«ÑÑнкÑией-конÑÑÑÑкÑоÑом наÑледÑÑÑего клаÑÑа» и вÑеми оÑÑалÑнÑми. РнаÑледÑÑÑем клаÑÑе ÑооÑвеÑÑÑвÑÑÑÐ°Ñ ÑÑнкÑиÑ-конÑÑÑÑкÑÐ¾Ñ Ð¿Ð¾Ð¼ÐµÑена ÑпеÑиалÑнÑм внÑÑÑенним ÑвойÑÑвом [[ConstructorKind]]:"derived".
РазниÑа в ÑледÑÑÑем:
- Ðогда вÑполнÑеÑÑÑ Ð¾Ð±ÑÑнÑй конÑÑÑÑкÑоÑ, он ÑоздаÑÑ Ð¿ÑÑÑой обÑÐµÐºÑ Ð¸ пÑиÑÐ²Ð°Ð¸Ð²Ð°ÐµÑ ÐµÐ³Ð¾
this. - Ðогда запÑÑкаеÑÑÑ ÐºÐ¾Ð½ÑÑÑÑкÑÐ¾Ñ ÑнаÑледованного клаÑÑа, он ÑÑого не делаеÑ. ÐмеÑÑо ÑÑого он ждÑÑ, ÑÑо ÑÑо ÑÐ´ÐµÐ»Ð°ÐµÑ ÐºÐ¾Ð½ÑÑÑÑкÑÐ¾Ñ ÑодиÑелÑÑкого клаÑÑа.
ÐоÑÑомÑ, еÑли Ð¼Ñ ÑоздаÑм ÑобÑÑвеннÑй конÑÑÑÑкÑоÑ, Ð¼Ñ Ð´Ð¾Ð»Ð¶Ð½Ñ Ð²ÑзваÑÑ super, в пÑоÑивном ÑлÑÑае обÑÐµÐºÑ Ð´Ð»Ñ this не бÑÐ´ÐµÑ Ñоздан, и Ð¼Ñ Ð¿Ð¾Ð»ÑÑим оÑибкÑ.
ЧÑÐ¾Ð±Ñ ÐºÐ¾Ð½ÑÑÑÑкÑÐ¾Ñ Rabbit ÑабоÑал, он должен вÑзваÑÑ super() до Ñого, как иÑполÑзоваÑÑ this, ÑÑÐ¾Ð±Ñ Ð½Ðµ бÑло оÑибки:
class Animal {
constructor(name) {
this.speed = 0;
this.name = name;
}
// ...
}
class Rabbit extends Animal {
constructor(name, earLength) {
super(name);
this.earLength = earLength;
}
// ...
}
// ÑепеÑÑ ÑабоÑаеÑ
let rabbit = new Rabbit("ÐелÑй кÑолик", 10);
alert(rabbit.name); // ÐелÑй кÑолик
alert(rabbit.earLength); // 10
ÐеÑеопÑеделение полей клаÑÑа: Ñонкое замеÑание
Ð ÑÑом подÑазделе пÑедполагаеÑÑÑ, ÑÑо Ñ Ð²Ð°Ñ Ñже еÑÑÑ Ð¾Ð¿ÑеделÑннÑй опÑÑ ÑабоÑÑ Ñ ÐºÐ»Ð°ÑÑами, возможно, в дÑÑÐ³Ð¸Ñ ÑзÑÐºÐ°Ñ Ð¿ÑогÑаммиÑованиÑ.
ÐÑо даÑÑ Ð»ÑÑÑее пÑедÑÑавление о ÑзÑке, а Ñакже обÑÑÑнÑÐµÑ Ð¿Ð¾Ð²ÐµÐ´ÐµÐ½Ð¸Ðµ, коÑоÑое Ð¼Ð¾Ð¶ÐµÑ Ð±ÑÑÑ Ð¸ÑÑоÑником оÑибок (но не оÑÐµÐ½Ñ ÑаÑÑо).
ÐÑли Ð²Ñ ÑÑиÑаеÑе ÑÑÐ¾Ñ Ð¼Ð°ÑеÑиал ÑлиÑком ÑÑÑднÑм Ð´Ð»Ñ Ð¿Ð¾Ð½Ð¸Ð¼Ð°Ð½Ð¸Ñ, пÑоÑÑо пÑодолжайÑе ÑиÑаÑÑ Ð´Ð°Ð»ÑÑе, а заÑем веÑниÑеÑÑ Ðº Ð½ÐµÐ¼Ñ ÑеÑез некоÑоÑое вÑемÑ.
ÐÑ Ð¼Ð¾Ð¶ÐµÐ¼ пеÑеопÑеделÑÑÑ Ð½Ðµ ÑолÑко меÑодÑ, но и Ð¿Ð¾Ð»Ñ ÐºÐ»Ð°ÑÑа.
Ðднако, когда Ð¼Ñ Ð¿Ð¾Ð»ÑÑаем доÑÑÑп к пеÑеопÑÐµÐ´ÐµÐ»ÐµÐ½Ð½Ð¾Ð¼Ñ Ð¿Ð¾Ð»Ñ Ð² ÑодиÑелÑÑком конÑÑÑÑкÑоÑе, ÑÑо поведение оÑлиÑаеÑÑÑ Ð¾Ñ Ð±Ð¾Ð»ÑÑинÑÑва дÑÑÐ³Ð¸Ñ ÑзÑков пÑогÑаммиÑованиÑ.
РаÑÑмоÑÑим ÑÑÐ¾Ñ Ð¿ÑимеÑ:
class Animal {
name = 'animal';
constructor() {
alert(this.name); // (*)
}
}
class Rabbit extends Animal {
name = 'rabbit';
}
new Animal(); // animal
new Rabbit(); // animal
ÐдеÑÑ, клаÑÑ Rabbit ÑаÑÑиÑÑÐµÑ Animal и пеÑеопÑеделÑÐµÑ Ð¿Ð¾Ð»Ðµ name Ñвоим ÑобÑÑвеннÑм знаÑением.
Ð Rabbit Ð½ÐµÑ ÑобÑÑвенного конÑÑÑÑкÑоÑа, поÑÑÐ¾Ð¼Ñ Ð²ÑзÑваеÑÑÑ ÐºÐ¾Ð½ÑÑÑÑкÑÐ¾Ñ Animal.
ЧÑо инÑеÑеÑно, в обоиÑ
ÑлÑÑаÑÑ
: new Animal() и new Rabbit(), alert в ÑÑÑоке (*) показÑÐ²Ð°ÐµÑ animal.
ÐÑÑгими Ñловами, ÑодиÑелÑÑкий конÑÑÑÑкÑÐ¾Ñ Ð²Ñегда иÑполÑзÑÐµÑ ÑÐ²Ð¾Ñ ÑобÑÑвенное знаÑение полÑ, а не пеÑеопÑеделÑнное.
ЧÑо же в ÑÑом ÑÑÑанного?
ÐÑли ÑÑо еÑÑ Ð½Ðµ ÑÑно, ÑÑавниÑе Ñ Ð¼ÐµÑодами.
ÐÐ¾Ñ ÑÐ¾Ñ Ð¶Ðµ код, но вмеÑÑо Ð¿Ð¾Ð»Ñ this.name, Ð¼Ñ Ð²ÑзÑваем меÑод this.showName():
class Animal {
showName() { // вмеÑÑо this.name = 'animal'
alert('animal');
}
constructor() {
this.showName(); // вмеÑÑо alert(this.name);
}
}
class Rabbit extends Animal {
showName() {
alert('rabbit');
}
}
new Animal(); // animal
new Rabbit(); // rabbit
ÐбÑаÑиÑе внимание: ÑепеÑÑ ÑезÑлÑÑÐ°Ñ Ð´ÑÑгой.
Ð ÑÑо Ñо, Ñего мÑ, еÑÑеÑÑвенно, ожидаем. Ðогда ÑодиÑелÑÑкий конÑÑÑÑкÑÐ¾Ñ Ð²ÑзÑваеÑÑÑ Ð² пÑоизводном клаÑÑе, он иÑполÑзÑÐµÑ Ð¿ÐµÑеопÑеделÑннÑй меÑод.
â¦Ðо Ð´Ð»Ñ Ð¿Ð¾Ð»ÐµÐ¹ клаÑÑа ÑÑо не Ñак. Ðак Ñже бÑло Ñказано, ÑодиÑелÑÑкий конÑÑÑÑкÑÐ¾Ñ Ð²Ñегда иÑполÑзÑÐµÑ ÑодиÑелÑÑкое поле.
ÐоÑÐµÐ¼Ñ Ð¶Ðµ наблÑдаеÑÑÑ ÑазниÑа?
ЧÑо ж, пÑиÑина заклÑÑаеÑÑÑ Ð² поÑÑдке иниÑиализаÑии полей. Ðоле клаÑÑа иниÑиализиÑÑеÑÑÑ:
- ÐеÑед конÑÑÑÑкÑоÑом Ð´Ð»Ñ Ð±Ð°Ð·Ð¾Ð²Ð¾Ð³Ð¾ клаÑÑа (коÑоÑÑй ниÑего не ÑаÑÑиÑÑеÑ),
- СÑÐ°Ð·Ñ Ð¿Ð¾Ñле
super()Ð´Ð»Ñ Ð¿Ñоизводного клаÑÑа.
РнаÑем ÑлÑÑае Rabbit â ÑÑо пÑоизводнÑй клаÑÑ. Рнем Ð½ÐµÑ ÐºÐ¾Ð½ÑÑÑÑкÑоÑа constructor(). Ðак бÑло Ñказано Ñанее, ÑÑо Ñо же Ñамое, как еÑли Ð±Ñ Ð±Ñл пÑÑÑой конÑÑÑÑкÑоÑ, ÑодеÑжаÑий ÑолÑко super(...args).
ÐÑак, new Rabbit() вÑзÑÐ²Ð°ÐµÑ super(), Ñаким обÑазом, вÑполнÑÑ ÑодиÑелÑÑкий конÑÑÑÑкÑоÑ, и (ÑоглаÑно пÑÐ°Ð²Ð¸Ð»Ñ Ð´Ð»Ñ Ð¿ÑоизводнÑÑ
клаÑÑов) ÑолÑко поÑле ÑÑого иниÑиализиÑÑÑÑÑÑ Ð¿Ð¾Ð»Ñ ÐµÐ³Ð¾ клаÑÑа. Ðа Ð¼Ð¾Ð¼ÐµÐ½Ñ Ð²ÑÐ¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ ÑодиÑелÑÑкого конÑÑÑÑкÑоÑа еÑÑ Ð½ÐµÑ Ð¿Ð¾Ð»ÐµÐ¹ клаÑÑа Rabbit, поÑÑÐ¾Ð¼Ñ Ð¸ÑполÑзÑÑÑÑÑ Ð¿Ð¾Ð»Ñ Animal.
ÐÑо Ñонкое ÑазлиÑие Ð¼ÐµÐ¶Ð´Ñ Ð¿Ð¾Ð»Ñми и меÑодами Ñ Ð°ÑакÑеÑно Ð´Ð»Ñ JavaScript.
Ð ÑÑаÑÑÑÑ, Ñакое поведение пÑоÑвлÑеÑÑÑ ÑолÑко в Ñом ÑлÑÑае, когда пеÑеопÑеделенное поле иÑполÑзÑеÑÑÑ Ð² ÑодиÑелÑÑком конÑÑÑÑкÑоÑе. Тогда Ð¼Ð¾Ð¶ÐµÑ Ð±ÑÑÑ ÑÑÑдно понÑÑÑ, ÑÑо пÑоиÑÑ Ð¾Ð´Ð¸Ñ, поÑÑÐ¾Ð¼Ñ Ð¼Ñ Ð¾Ð±ÑÑÑнÑем ÑÑо здеÑÑ.
ÐÑли ÑÑо ÑÑановиÑÑÑ Ð¿Ñоблемой, ÐµÑ Ð¼Ð¾Ð¶Ð½Ð¾ ÑеÑиÑÑ, иÑполÑзÑÑ Ð¼ÐµÑÐ¾Ð´Ñ Ð¸Ð»Ð¸ геÑÑеÑÑ/ÑеÑÑеÑÑ Ð²Ð¼ÐµÑÑо полей.
УÑÑÑойÑÑво super, [[HomeObject]]
ÐÑли Ð²Ñ ÑиÑаеÑе ÑÑебник пеÑвÑй Ñаз â ÑÑÑ ÑекÑÐ¸Ñ Ð¼Ð¾Ð¶Ð½Ð¾ пÑопÑÑÑиÑÑ.
Ðна ÑаÑÑказÑÐ²Ð°ÐµÑ Ð¾ внÑÑÑеннем ÑÑÑÑойÑÑве наÑÐ»ÐµÐ´Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¸ вÑзовe super.
ÐавайÑе заглÑнем «под капоÑ» super. ÐдеÑÑ ÐµÑÑÑ Ð½ÐµÐºÐ¾ÑоÑÑе инÑеÑеÑнÑе моменÑÑ.
ÐообÑе, иÑÑ
Ð¾Ð´Ñ Ð¸Ð· наÑиÑ
знаний до ÑÑого моменÑа, super вообÑе не Ð¼Ð¾Ð¶ÐµÑ ÑабоÑаÑÑ!
ÐÑ Ð¿Ñавда, давайÑе ÑпÑоÑим ÑÐµÐ±Ñ â как он должен ÑабоÑаÑÑ, ÑиÑÑо ÑеÑ
ниÑеÑки? Ðогда меÑод обÑекÑа вÑполнÑеÑÑÑ, он полÑÑÐ°ÐµÑ ÑекÑÑий обÑÐµÐºÑ ÐºÐ°Ðº this. ÐÑли Ð¼Ñ Ð²ÑзÑваем super.method(), Ñо Ð´Ð²Ð¸Ð¶ÐºÑ Ð½ÐµÐ¾Ð±Ñ
одимо полÑÑиÑÑ method из пÑоÑоÑипа ÑекÑÑего обÑекÑа. Ркак ÐµÐ¼Ñ ÑÑо ÑделаÑÑ?
ÐадаÑа Ð¼Ð¾Ð¶ÐµÑ Ð¿Ð¾ÐºÐ°Ð·Ð°ÑÑÑÑ Ð¿ÑоÑÑой, но ÑÑо не Ñак. Ðвижок Ð·Ð½Ð°ÐµÑ ÑекÑÑий this и мог Ð±Ñ Ð¿Ð¾Ð¿ÑÑаÑÑÑÑ Ð¿Ð¾Ð»ÑÑиÑÑ ÑодиÑелÑÑкий меÑод как this.__proto__.method. Ðднако, ÑвÑ, Ñакой «наивнÑй» пÑÑÑ Ð½Ðµ ÑабоÑаеÑ.
ÐÑодемонÑÑÑиÑÑем пÑоблемÑ. Ðез клаÑÑов, иÑполÑзÑÑ Ð¿ÑоÑÑÑе обÑекÑÑ Ð´Ð»Ñ Ð½Ð°Ð³Ð»ÑдноÑÑи.
ÐÑ Ð¼Ð¾Ð¶ÐµÑе пÑопÑÑÑиÑÑ ÑÑÑ ÑаÑÑÑ Ð¸ пеÑейÑи ниже к подÑекÑии [[HomeObject]], еÑли не Ñ
оÑиÑе знаÑÑ Ð´ÐµÑали. ÐÑеда не бÑдеÑ. Ðли ÑиÑайÑе далее, еÑли Ñ
оÑиÑе ÑазобÑаÑÑÑÑ.
РпÑимеÑе ниже rabbit.__proto__ = animal. ÐопÑобÑем в rabbit.eat() вÑзваÑÑ animal.eat(), иÑполÑзÑÑ this.__proto__:
let animal = {
name: "Animal",
eat() {
alert(`${this.name} еÑÑ.`);
}
};
let rabbit = {
__proto__: animal,
name: "ÐÑолик",
eat() {
// Ð²Ð¾Ñ ÐºÐ°Ðº пÑедположиÑелÑно Ð¼Ð¾Ð¶ÐµÑ ÑабоÑаÑÑ super.eat()
this.__proto__.eat.call(this); // (*)
}
};
rabbit.eat(); // ÐÑолик еÑÑ.
Ð ÑÑÑоке (*) Ð¼Ñ Ð±ÐµÑÑм eat из пÑоÑоÑипа (animal) и вÑзÑваем его в конÑекÑÑе ÑекÑÑего обÑекÑа. ÐбÑаÑиÑе внимание, ÑÑо .call(this) здеÑÑ Ð½ÐµÑпÑоÑÑа: пÑоÑÑой вÑзов this.__proto__.eat() бÑÐ´ÐµÑ Ð²ÑполнÑÑÑ ÑодиÑелÑÑкий eat в конÑекÑÑе пÑоÑоÑипа, а не ÑекÑÑего обÑекÑа.
ÐÑиведÑннÑй вÑÑе код ÑабоÑÐ°ÐµÑ Ñак, как задÑмано: вÑполнÑеÑÑÑ Ð½ÑжнÑй alert.
ТепеÑÑ Ð´Ð°Ð²Ð°Ð¹Ñе добавим еÑÑ Ð¾Ð´Ð¸Ð½ обÑÐµÐºÑ Ð² ÑепоÑÐºÑ Ð½Ð°ÑÐ»ÐµÐ´Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¸ Ñвидим, как вÑе ÑломаеÑÑÑ:
let animal = {
name: "ÐивоÑное",
eat() {
alert(`${this.name} еÑÑ.`);
}
};
let rabbit = {
__proto__: animal,
eat() {
// ...делаем ÑÑо-Ñо ÑпеÑиÑиÑное Ð´Ð»Ñ ÐºÑолика и вÑзÑваем ÑодиÑелÑÑкий (animal) меÑод
this.__proto__.eat.call(this); // (*)
}
};
let longEar = {
__proto__: rabbit,
eat() {
// ...делаем ÑÑо-Ñо, ÑвÑзанное Ñ Ð´Ð»Ð¸Ð½Ð½Ñми ÑÑами, и вÑзÑваем ÑодиÑелÑÑкий (rabbit) меÑод
this.__proto__.eat.call(this); // (**)
}
};
longEar.eat(); // Error: Maximum call stack size exceeded
ТепеÑÑ ÐºÐ¾Ð´ не ÑабоÑаеÑ! ÐÑибка Ð²Ð¾Ð·Ð½Ð¸ÐºÐ°ÐµÑ Ð¿Ñи попÑÑке вÑзова longEar.eat().
Ðа пеÑвÑй взглÑд вÑе не Ñак оÑевидно, но еÑли Ð¼Ñ Ð¿ÑоÑледим вÑзов longEar.eat(), Ñо Ñможем понÑÑÑ Ð¿ÑиÑÐ¸Ð½Ñ Ð¾Ñибки. РобеиÑ
ÑÑÑокаÑ
(*) и (**) знаÑение this â ÑÑо ÑекÑÑий обÑÐµÐºÑ (longEar). ÐÑо важно: Ð´Ð»Ñ Ð²ÑеÑ
меÑодов обÑекÑа this ÑказÑÐ²Ð°ÐµÑ Ð½Ð° ÑекÑÑий обÑекÑ, а не на пÑоÑоÑип или ÑÑо-Ñо еÑÑ.
ÐÑак, в обеиÑ
линиÑÑ
(*) и (**) знаÑение this.__proto__ одно и Ñо же: rabbit. РобоиÑ
ÑлÑÑаÑÑ
меÑод rabbit.eat вÑзÑваеÑÑÑ Ð² беÑконеÑном Ñикле не поднимаÑÑÑ Ð¿Ð¾ ÑепоÑке вÑзовов.
ÐаÑÑина Ñого, ÑÑо пÑоиÑÑ Ð¾Ð´Ð¸Ñ:
-
ÐнÑÑÑи
longEar.eat()ÑÑÑока(**)вÑзÑваеÑrabbit.eatÑо знаÑениемthis=longEar.// внÑÑÑи longEar.eat() Ñ Ð½Ð°Ñ this = longEar this.__proto__.eat.call(this) // (**) // ÑÑановиÑÑÑ longEar.__proto__.eat.call(this) // Ñо же ÑÑо и rabbit.eat.call(this); -
Ð ÑÑÑоке
(*)вrabbit.eatÐ¼Ñ Ñ Ð¾Ñим пеÑедаÑÑ Ð²Ñзов вÑÑе по ÑепоÑке, ноthis=longEar, поÑÑомÑthis.__proto__.eatÑнова Ñавенrabbit.eat!// внÑÑÑи rabbit.eat() Ñ Ð½Ð°Ñ Ñакже this = longEar this.__proto__.eat.call(this) // (*) // ÑÑановиÑÑÑ longEar.__proto__.eat.call(this) // или (Ñнова) rabbit.eat.call(this); -
â¦
rabbit.eatвÑзÑÐ²Ð°ÐµÑ ÑÐµÐ±Ñ Ð² беÑконеÑном Ñикле, поÑÐ¾Ð¼Ñ ÑÑо не Ð¼Ð¾Ð¶ÐµÑ Ð¿Ð¾Ð´Ð½ÑÑÑÑÑ Ð´Ð°Ð»ÑÑе по ÑепоÑке.
ÐÑоблема не Ð¼Ð¾Ð¶ÐµÑ Ð±ÑÑÑ ÑеÑена Ñ Ð¿Ð¾Ð¼Ð¾ÑÑÑ Ð¾Ð´Ð½Ð¾Ð³Ð¾ ÑолÑко this.
[[HomeObject]]
ÐÐ»Ñ ÑеÑÐµÐ½Ð¸Ñ ÑÑой пÑÐ¾Ð±Ð»ÐµÐ¼Ñ Ð² JavaScript бÑло добавлено ÑпеÑиалÑное внÑÑÑеннее ÑвойÑÑво Ð´Ð»Ñ ÑÑнкÑий: [[HomeObject]].
Ðогда ÑÑнкÑÐ¸Ñ Ð¾Ð±ÑÑвлена как меÑод внÑÑÑи клаÑÑа или обÑекÑа, ÐµÑ ÑвойÑÑво [[HomeObject]] ÑÑановиÑÑÑ Ñавно ÑÑÐ¾Ð¼Ñ Ð¾Ð±ÑекÑÑ.
ÐаÑем super иÑполÑзÑÐµÑ ÐµÐ³Ð¾, ÑÑÐ¾Ð±Ñ Ð¿Ð¾Ð»ÑÑиÑÑ Ð¿ÑоÑоÑип ÑодиÑÐµÐ»Ñ Ð¸ его меÑодÑ.
ÐавайÑе поÑмоÑÑим, как ÑÑо ÑабоÑÐ°ÐµÑ â опÑÑÑ Ð¶Ðµ, иÑполÑзÑÑ Ð¿ÑоÑÑÑе обÑекÑÑ:
let animal = {
name: "ÐивоÑное",
eat() { // animal.eat.[[HomeObject]] == animal
alert(`${this.name} еÑÑ.`);
}
};
let rabbit = {
__proto__: animal,
name: "ÐÑолик",
eat() { // rabbit.eat.[[HomeObject]] == rabbit
super.eat();
}
};
let longEar = {
__proto__: rabbit,
name: "ÐлинноÑÑ
",
eat() { // longEar.eat.[[HomeObject]] == longEar
super.eat();
}
};
// ÑабоÑÐ°ÐµÑ Ð²ÐµÑно
longEar.eat(); // ÐлинноÑÑ
еÑÑ.
ÐÑо ÑабоÑÐ°ÐµÑ ÐºÐ°Ðº задÑмано благодаÑÑ [[HomeObject]]. ÐеÑод, Ñакой как longEar.eat, Ð·Ð½Ð°ÐµÑ Ñвой [[HomeObject]] и полÑÑÐ°ÐµÑ Ð¼ÐµÑод ÑодиÑÐµÐ»Ñ Ð¸Ð· его пÑоÑоÑипа. ÐообÑе без иÑполÑÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ this.
ÐеÑÐ¾Ð´Ñ Ð½Ðµ «ÑвободнÑ»
Ðо ÑÑого Ð¼Ñ Ð½ÐµÐ¾Ð´Ð½Ð¾ÐºÑаÑно видели, ÑÑо ÑÑнкÑии в JavaScript «ÑвободнÑ», не пÑивÑÐ·Ð°Ð½Ñ Ðº обÑекÑам. ÐÑ
можно копиÑоваÑÑ Ð¼ÐµÐ¶Ð´Ñ Ð¾Ð±ÑекÑами и вÑзÑваÑÑ Ñ Ð»ÑбÑм this.
Ðо Ñамо ÑÑÑеÑÑвование [[HomeObject]] наÑÑÑÐ°ÐµÑ ÑÑÐ¾Ñ Ð¿ÑинÑип, Ñак как меÑÐ¾Ð´Ñ Ð·Ð°Ð¿Ð¾Ð¼Ð¸Ð½Ð°ÑÑ Ñвои обÑекÑÑ. [[HomeObject]] нелÑÐ·Ñ Ð¸Ð·Ð¼ÐµÐ½Ð¸ÑÑ, ÑÑа ÑвÑÐ·Ñ â навÑегда.
ÐдинÑÑвенное меÑÑо в ÑзÑке, где иÑполÑзÑеÑÑÑ [[HomeObject]] â ÑÑо super. ÐоÑÑÐ¾Ð¼Ñ ÐµÑли меÑод не иÑполÑзÑÐµÑ super, Ñо Ð¼Ñ Ð²Ñе еÑÑ Ð¼Ð¾Ð¶ÐµÐ¼ ÑÑиÑаÑÑ ÐµÐ³Ð¾ ÑвободнÑм и копиÑоваÑÑ Ð¼ÐµÐ¶Ð´Ñ Ð¾Ð±ÑекÑами. Ð Ð²Ð¾Ñ ÐµÑли super в коде еÑÑÑ, Ñо Ð²Ð¾Ð·Ð¼Ð¾Ð¶Ð½Ñ Ð¿Ð¾Ð±Ð¾ÑнÑе ÑÑÑекÑÑ.
ÐÐ¾Ñ Ð¿ÑÐ¸Ð¼ÐµÑ Ð½ÐµÐ²ÐµÑного ÑезÑлÑÑаÑа super поÑле копиÑованиÑ:
let animal = {
sayHi() {
alert("Я живоÑное");
}
};
// rabbit наÑледÑÐµÑ Ð¾Ñ animal
let rabbit = {
__proto__: animal,
sayHi() {
super.sayHi();
}
};
let plant = {
sayHi() {
alert("Я ÑаÑÑение");
}
};
// tree наÑледÑÐµÑ Ð¾Ñ plant
let tree = {
__proto__: plant,
sayHi: rabbit.sayHi // (*)
};
tree.sayHi(); // Я живоÑное (?!?)
ÐÑзов tree.sayHi() показÑÐ²Ð°ÐµÑ Â«Ð¯ живоÑное». ÐпÑеделÑнно невеÑно.
ÐÑиÑина пÑоÑÑа:
- Ð ÑÑÑоке
(*), меÑодtree.sayHiÑкопиÑован изrabbit. Ðозможно, Ð¼Ñ Ñ Ð¾Ñели избежаÑÑ Ð´ÑблиÑÐ¾Ð²Ð°Ð½Ð¸Ñ ÐºÐ¾Ð´Ð°? - Ðго
[[HomeObject]]â ÑÑоrabbit, Ð²ÐµÐ´Ñ Ð¾Ð½ бÑл Ñоздан вrabbit. СвойÑÑво[[HomeObject]]никогда не менÑеÑÑÑ. - Ркоде
tree.sayHi()еÑÑÑ Ð²Ñзовsuper.sayHi(). Ðн идÑÑ Ð²Ð²ÐµÑÑ Ð¾Ñrabbitи беÑÑÑ Ð¼ÐµÑод изanimal.
ÐÐ¾Ñ Ð´Ð¸Ð°Ð³Ñамма пÑоиÑÑ Ð¾Ð´ÑÑего:
ÐеÑодÑ, а не ÑвойÑÑва-ÑÑнкÑии
СвойÑÑво [[HomeObject]] опÑеделено Ð´Ð»Ñ Ð¼ÐµÑодов как клаÑÑов, Ñак и обÑÑнÑÑ
обÑекÑов. Ðо Ð´Ð»Ñ Ð¾Ð±ÑекÑов меÑÐ¾Ð´Ñ Ð´Ð¾Ð»Ð¶Ð½Ñ Ð±ÑÑÑ Ð¾Ð±ÑÑÐ²Ð»ÐµÐ½Ñ Ð¸Ð¼ÐµÐ½Ð½Ð¾ как method(), а не "method: function()".
ÐÐ»Ñ Ð½Ð°Ñ ÑазлиÑий неÑ, но они еÑÑÑ Ð´Ð»Ñ JavaScript.
РпÑиведÑнном ниже пÑимеÑе иÑполÑзÑеÑÑÑ ÑинÑакÑÐ¸Ñ Ð½Ðµ меÑода, ÑвойÑÑва-ÑÑнкÑии. ÐоÑÑÐ¾Ð¼Ñ Ñ Ð½ÐµÐ³Ð¾ Ð½ÐµÑ [[HomeObject]], и наÑледование не ÑабоÑаеÑ:
let animal = {
eat: function() { // намеÑенно пиÑем Ñак, а не eat() { ...
// ...
}
};
let rabbit = {
__proto__: animal,
eat: function() {
super.eat();
}
};
rabbit.eat(); // ÐÑибка вÑзова super (поÑÐ¾Ð¼Ñ ÑÑо Ð½ÐµÑ [[HomeObject]])
ÐÑого
- ЧÑÐ¾Ð±Ñ ÑнаÑледоваÑÑ Ð¾Ñ ÐºÐ»Ð°ÑÑа:
class Child extends Parent:- ÐÑи ÑÑом
Child.prototype.__proto__бÑÐ´ÐµÑ ÑавенParent.prototype, Ñак ÑÑо меÑÐ¾Ð´Ñ Ð±ÑдÑÑ ÑнаÑледованÑ.
- ÐÑи ÑÑом
- ÐÑи пеÑеопÑеделении конÑÑÑÑкÑоÑа:
- ÐбÑзаÑелен вÑзов конÑÑÑÑкÑоÑа ÑодиÑелÑ
super()в конÑÑÑÑкÑоÑеChildдо обÑаÑÐµÐ½Ð¸Ñ Ðºthis.
- ÐбÑзаÑелен вÑзов конÑÑÑÑкÑоÑа ÑодиÑелÑ
- ÐÑи пеÑеопÑеделении дÑÑгого меÑода:
- ÐÑ Ð¼Ð¾Ð¶ÐµÐ¼ вÑзваÑÑ
super.method()в меÑодеChildÐ´Ð»Ñ Ð¾Ð±ÑаÑÐµÐ½Ð¸Ñ Ðº меÑÐ¾Ð´Ñ ÑодиÑелÑParent.
- ÐÑ Ð¼Ð¾Ð¶ÐµÐ¼ вÑзваÑÑ
- ÐнÑÑÑенние деÑали:
- ÐеÑÐ¾Ð´Ñ Ð·Ð°Ð¿Ð¾Ð¼Ð¸Ð½Ð°ÑÑ Ñвой обÑÐµÐºÑ Ð²Ð¾ внÑÑÑеннем ÑвойÑÑве
[[HomeObject]]. ÐлагодаÑÑ ÑÑÐ¾Ð¼Ñ ÑабоÑаеÑsuper, он в его пÑоÑоÑипе иÑÐµÑ ÑодиÑелÑÑкие меÑодÑ. - ÐоÑÑÐ¾Ð¼Ñ ÐºÐ¾Ð¿Ð¸ÑоваÑÑ Ð¼ÐµÑод, иÑполÑзÑÑÑий
super, Ð¼ÐµÐ¶Ð´Ñ ÑазнÑми обÑекÑами небезопаÑно.
- ÐеÑÐ¾Ð´Ñ Ð·Ð°Ð¿Ð¾Ð¼Ð¸Ð½Ð°ÑÑ Ñвой обÑÐµÐºÑ Ð²Ð¾ внÑÑÑеннем ÑвойÑÑве
Также:
- У ÑÑÑелоÑнÑÑ
ÑÑнкÑий Ð½ÐµÑ Ñвоего
thisиsuper, поÑÑÐ¾Ð¼Ñ Ð¾Ð½Ð¸ «пÑозÑаÑно» вÑÑÑаиваÑÑÑÑ Ð²Ð¾ внеÑний конÑекÑÑ.

ÐомменÑаÑии
<code>, Ð´Ð»Ñ Ð½ÐµÑколÑÐºÐ¸Ñ ÑÑÑок кода — Ñег<pre>, еÑли болÑÑе 10 ÑÑÑок — ÑÑÑÐ»ÐºÑ Ð½Ð° пеÑоÑниÑÑ (plnkr, JSBin, codepenâ¦)