четверг, 19 ноября 2020 г.

A programmer: JavaScript #23. Prototype inheritance (Прототипное наследование)

Если коротко, наследование - это способ получения полей и методов без того, чтобы явно декларировать их в самом объекте. Осуществляется это указанием, что данный объект является "наследником" какого-то другого объекта, а значит, он по умолчанию имеет те же поля и методы, что и "родитель", плюс обычно добавляет какие-то собственные или переопределяет родительские. Это удобно.

В JavaScript используется "прототипное наследование". Что это значит и в чем отличие от "классического" наследования. Под классическим наследованием я здесь буду понимать то, которое используется в Java. В классическом наследовании наследуются не объекты, а классы. А объект имеет те поля и методы, которые должны иметь объекты его класса (включая наследуемые от классов-родителей). Это создает структуру наследования, что имеет свои преимущества и недостатки.

Прототипное наследование отличается тем, что наследуются именно объекты и именно от объектов. То есть некоторый объект может объявить другой объект своим прототипом:

var object = Object.create(prototypeObject);

Здесь object объявляет себя наследником объекта prototypeObject, слеовательно, он имеет такие же, как у него поля и методы.

Собственно, можно наследовать не только объект от объекта, но и класс от класса - через конструкторы:

ClassA.prototype = Object.create(ClassB.prototype);

Только в этом случае сам конструктор тоже наследуется - и у ClassA будет конструктор от ClassB. Чтобы этого не происходило нужно отдельно его прописать (как бы "вернуть" себе обратно):

ClassA.prototype.constructor = ClassA;

Технически это выглядит так, что любой объект имеет скрытое свойство [[Prototype]], которое можно явно задать (иначе оно равно null):

let object.__proto__ = prototypeObject;

 Здесь __proto__ - геттер/сеттер для [[Prototype]],в более поздних версиях языка он заменяется функциями Object.getPrototypeOf и , хотя __proto__ тоже поддерживается.

Метод, которого нет в наследнике, но естьь в прототипе, может быть в переопределен в наследнике, но не в прототипе. Исключением являются вызываемые наследником геттеры и сеттеры, существующие в прототипе.

Важно отметить, что this всегда указывает на сам объект, но не на его протоип. То есть прототип выступает для наследника как хранилище объектов. Поясняю:

Пусть в прототипе есть метод:

setState() {

    this.state = true;

}

Если его вызвать на экземпляре наследника (object.setState()), то state = true будет присвоен наследнику, а не прототипу.

Информация взята отсюда, отсюда и отсюда.

Комментариев нет: