Чому прототипний успадкований дитячий клас - екземпляр іншого дитичного класу?

Може, я роблю це неправильно. Я хочу створити базовий клас і два класи, які успадкують від цього класу. Однак, це говорить мені, що одна дитина є інстанцією іншої дитини ... що не може бути правильним, чи не так? Що я тут робив неправильно?

function BaseClass(){}
function Child1(){}

Child1.prototype = BaseClass.prototype;
Child1.prototype.constructor = Child1;

function Child2(){}
Child2.prototype = BaseClass.prototype;
Child2.prototype.constructor = Child2;

console.log((new Child1() instanceof Child2));//true
0
Ось хороший ресурс із багатьох способів використання спадщини в JS: 3site.eu/doc
додано Автор Cᴏʀʏ, джерело

4 Відповіді

Ви встановили прототип обох ваших класів на той самий об'єкт ("BaseClass.prototype") і встановили властивість "конструктора" на "Child2". Іншими словами, "Child1.prototype" та "Child2.prototype" є однаковими, тому у вашому коді включено лише одне "конструктор", і воно закінчується "Child2".

Можливо, ви хочете встановити ці прототипи в екземплярах базового класу?

Child1.prototype = new BaseClass();
Child1.prototype.constructor = Child1;

Child2.prototype = new BaseClass();//a different object
Child2.prototype.constructor = Child2;

(@Raynos вказує, що при аналізі вартості прототипу це може спричинити тонкі проблеми, тому ви можете скористатися підходом у відповідь.)

3
додано
Ви насправді слід уникати .prototype = new X() , якщо ви не бажаєте ініціалізувати .prototype як екземпляр X, ви просто хочете екземпляр. (тобто не називайте конструктора!)
додано Автор Raynos, джерело
@ShyGuy ви ініціалізуєте їх тоді і там, ви робите це в конструкторі дитини.
додано Автор Raynos, джерело
@Pointy: (багато рамок об'єктів рекомендують "init", оскільки вони є брудними хаками, ви просто не використовуєте конструктори при успадкуванні (порада: Object.create робить те, що ви хочете).
додано Автор Raynos, джерело
@Pointy Так, я маю цю погану звичку мислити "ES5", коли хтось каже "JavaScript". Я просто не можу його відключити!
додано Автор Raynos, джерело
Що станеться, коли ваш конструктор базового класу має аргументи - чи потрібно їх ініціалізувати саме тоді і там? Мій справжній код дочірнього класу викликає до BaseClass.call (це, 'аргумент1', 'аргумент2'); у його конструкторі.
додано Автор Paul, джерело
OK @Raynos так, ймовірно, це правда; Я не роблю такого роду речі досить часто, щоб мати справу з такими питаннями :-) Я додам не.
додано Автор Pointy, джерело
@ShyGuy Я думаю, що тому багато основних об'єктів використовують конвенцію про наявність функції "init" для виклику.
додано Автор Pointy, джерело
@Raynos ви, безумовно, знаєте більше про це, ніж я :-) Я не написав "object-y" код у JavaScript протягом тривалого часу, хоча я пишу багато JavaScript. (Також не є Object.create об'єктом ES5? Можливо, в полі MDN є поліфайл).
додано Автор Pointy, джерело

Child1.prototype = BaseClass.prototype;

Should be Child1.prototype = Object.create(BaseClass.prototype)

You don't want the prototype object to be the same, you want a new prototype object that inherits from BaseClass

Приклад життя

Disclaimer: Object.create is ES5, use the ES5-shim for legacy platform support.

Навести більш глибокий приклад:

// Create base prototype
var Base = {
  method: function() {
    return this.things;
  },
  constructor: function (things) {
    this.things = things;
  }
};
// Link constructor and prototype
Base.constructor.prototype = Base;

// Create child prototype that inherits from Base
var Child = Object.create(Base);
// overwrite constructor
Child.constructor = function (things) {
  things++;
  Base.constructor.call(things);
}
// Link constructor and prototype
Child.constructor.prototype = Child;

// Swap prototype and constructor around to support new
ChildFactory = Child.constructor;

var c = new ChildFactory(41);
console.log(c.method());//42

Візуально:

var Base = {...}

тут ми просто створюємо об'єкт із методами та властивостями. Ми хочемо мати можливість створити екземпляр цього об'єкта. Таким чином, Base є "класом", і всі його методи та властивості доступні з екземплярів (включаючи constructor ).

Base.constructor.prototype = Base;

Вам потрібно пов'язати функцію «Конструктор», яку ви можете викликати за допомогою new , який дасть вам новий екземпляр об'єкта ConstructorFunction.prototype разом з об'єктом прототипу . Це в основному трохи клей, який потрібно зробити вручну, оскільки ES не робить цього для вас.

Якщо ви забули це зробити, то функція конструктора (що є X.constructor ) не має властивості .prototype , щоб зробити примірники inherit from.

var Child = Object.create (Base);

Створіть новий об'єкт, [[Prototype]] - це Base . Це, по суті, є початком вашого прототипу

Child -> ([[Prototype]] = Base) -> ([[Prototype]] = Object.prototype) -> null

Child.x = ...

Тепер ми додаємо властивості до нашого дочірнього об'єкта, які є методами, якими Дітям буде наслідувати. В основному ми створюємо новий об'єкт прототипу, який слід успадкувати від. Всі екземпляри надаватимуть доступ до методів, а також діляться методами з прототипом (включаючи Base ).

ChildFactory = Child.constructor;

ключове слово new працює тільки на функціях конструктора, а не на об'єкті прототипу, тому нам потрібно в принципі сказати: "переключіть нашу змінну об'єкта прототипу до змінної функції конструктора"

Особисто, будуючи "класи", я вважаю, що об'єкти прототипу дійсно приємні для обробки безпосередньо, і при створенні екземплярів я вважаю, що функції конструктора приємні для обробки.

Now when we call var c = new Child(41);

Він буде називати функцію конструктора, яку ми визначили, що збільшить речі , а потім викликати базового конструктора.

Зауважте, що в цьому місці прототип ланцюга c виглядає так

c -> ([[Prototype]] = Child) 
  -> ([[Prototype]] = Base) 
  -> ([[Prototype]] = Object.prototype) 
  -> null
2
додано
Дякую! Це працює як чарівність ... Мені доведеться ще більше почитати про це, щоб побачити, що відбувається за сценою. У мережі так багато різних прикладів про те, як правильно зробити спадщину, і всі вони, мабуть, мають параметримі конструктори, щоб уникнути жорстких бітів :)
додано Автор Paul, джерело

Це не означає, що об'єкт new Child1() створюється конструктором "Child1". Оператор instanceof приймає тільки прототип об'єкта - (new Child1 ()). [[Prototype]] і перевіряє його присутність в ланцюжку прототипів, прототип ". Оператор instanceof активується за допомогою методу внутрішнього [[HasInstance]] конструктора. Отже, вам потрібно змінити свій код таким чином:

function BaseClass(){}
function Child1(){}

Child1.prototype = new BaseClass();
Child1.prototype.constructor = Child1;

function Child2(){}
Child2.prototype = new BaseClass();
Child2.prototype.constructor = Child2;
console.log((new Child1() instanceof Child2));//false

І це буде правильне виконання OOP в JavaScript

1
додано

Якщо ви призначите прототип, чи не означає, що ви повністю перезаписали його? Отже, Child1 і Child2 фактично стають об'єктами BaseClass, а не дитячими класами.

Див. Також http://www.zipcon.net/~swhite /docs/computers/languages/object_oriented_JS/inheritance.html . Належне О.О. в JS займає чимало зусиль.

1
додано
ІТ КПІ - JavaScript
ІТ КПІ - JavaScript
504 учасників

співтовариство javascript розробників в Telegram