Своё прототипное наследование в JavaScript

2

Own prototypical inheritance

Допустим у нас есть следующий код. Функция Circle со свойством radius и два метода, указанные в прототипе.

function Circle(radius) {
    this.radius = radius;
}

Circle.prototype.draw = function () {
    console.log('draw');
}

Circle.prototype.duplicate = function () {
    console.log('duplicate');
}

Давайте представим, что завтра мы захотим добавить сюда функцию square. И у этой функции будет точно такая же функция duplicate как и у Circle. Мы же не хотим поторять эту реализацию.
Другими словами мы не хотим определять конструктор Square, и потом переопределять заново метод duplicate в этом конструкторе.

function Square() {

}

Square.prototype.duplicate = function () {
    console.log('duplicate');
}

Вместо этого мы будем использовать наследование. Итак мы можем определить объект Shape и положить туда этот duplicate метод. А затем унаследовать Square и Circle от объекта Shape.
Итак, давайте определим конструктор Shape

function Shape() {

}

Shape.prototype.duplicate = function () {
    console.log('duplicate');
}

function Circle(radius) {
    this.radius = radius;
}

Circle.prototype.draw = function () {
    console.log('draw');
}

const s = new Shape();
const c = new Circle(1);

Вот что у нас сейчас в памяти. У нас есть объект c, который наследуется от circleBase (Circle.prototype), а circleBase наследуется от objectBae (Object).
own inherit one

Аналогично с объектом s, который наследуется от shapeBase (shape.prototype), а он в свою очередь наследуется от objectBase (Object).

own inherit two

Чтобы установить наследование здесь, нам нужно чтобы circleBase наследовался от shapeBase.

own inherit three

Это довольно просто. В javascript есть метод для создания объектов с заданным прототипом — Object.create().
У этого метода один параметр — объект, который будет использоваться как прототип. Мы хотим чтобы новый объект circleBase наследовался от shapeBase, вместо objectBase. Теперь shapeBase является shape.prototype. И так мы передаем аргументом Shape.prototype

Object.create(Shape.prototype);

Этот метод возвращает объект, который унаследован от shapeBase. Всё что нам осталось сделать это использовать этот объект как прототип для наших circles.
Этот метод создаёт новый объект с указанными объектом прототипа и свойствами.

Circle.prototype = Object.create(Shape.prototype);

До этой записи Circle.prototype выглядел так:

Circle.prototype = Object.create(Object.prototype) //objectBase;

Resetting the constructor

Но есть небольшя проблема при такой реализации.

Давайте уберем Object.create и кое на что посмотрим. Ранее, мы обсуждали, что у каждого объекта в JavaScript есть конструктор, который возвращает функцию, которая использовалась для конструирования или создания объекта. Давайте посмотрим на наш circle объект.
Посмотрим на прототип объекта в переменной c. Там мы увидим свойство constructor, которое ссылается на функцию Circle. Теперь, технически, с помощью этой конструкции мы можем создать новый объект.
constructor
Эти два выражение полностью одинаковы.

new Circle.prototype.constructor(1) === new Circle(1);

Теоретически таким образом мы можем динамически создавать объекты в нашем коде используя это свойство constructor.

Теперь давайте опять вернем на место наш Object.create(Shape.prototype).

Теперь у нас нет свойства конструктора на прежнем месте. Теперь свойство constructor переместилось глубже по цепочке, но теперь оно возвращает функцию Shape, но никак не функцию Circle. Другими словами, то что есть сейчас. Мы больше не можем создавать объекты circle, основанный на его конструкторе динамически. Если мы попробуем воспользоваться конструктором динамически в функции (для создания объектов) и затем попытаемся получить доступ к свойству prototype.constructor и попробуем вызвать его с помощью оператора new, то мы получим объект Shape, но не Circle.
constructor
Причина этой проблемы в том, что мы сбросили прототип circle.
До того как мы вызвали Object.create(Shape.prototype) у прототипа нашего Circle было свойство constructor равное Circle.

Circle.prototype.constructor = Circle;

Best Practice: каждый раз, когда мы переназначаем прототип объекта, нам также следует переназначить конструктор.

Т.е. в нашем случае, после Object.create мы должны указать конструктором объектов circle функцию Circle:

Circle.prototype.constructor = Circle;

constructor

You might also like More from author

Leave A Reply

Your email address will not be published.