Каждому разработчику javascript в его учебном цикле трудно осмыслить это прототипное наследование. Наследование в javascript уникально, оно полностью отличается от того, что мы видим в других языках, таких как Java или Python. В таких языках, как Java и C ++, существует концепция классов, в то время как в Javascript нет таких классов, как Java (да, ES6 предоставляет нам классы, но это не то же самое, это просто синтаксический сахар).
Наследование в javascript - большая тема, поэтому нам нужно пройти через некоторые основные концепции, например, шаг за шагом. Это 4 основных компонента: -
- Конструкторы
- Прототип
- Object.create ()
- Цепочка прототипов
В этой статье я хотел бы пройтись по первым двум темам, и нашим основным мотивом было бы разобраться в них, прежде чем переходить к двум другим. Лучшее понимание этих тем необходимо для понимания наследования.
Конструктор
Конструктор помогает нам создавать и инициализировать объекты в javascript. Просто? Давайте посмотрим, как это сделать.
//Example of a Constructor const Ball = function(size){ this.size = size; this.getSize = function(){ console.log(this.size); } };
- Вышеупомянутый конструктор - это простейший тип конструктора.
- Конструктор отчасти похож на функции javascript, которые принимают объект и присоединяют к нему свойство (здесь size, getSize).
- Первая буква конструктора должна быть в верхнем регистре (не правило большого пальца, но это предпочтительно).
//Complete code for object creation with constructor const Ball = function(size){ this.size = size; this.getSize = function(){ console.log(this.size); } }; let football = new Ball('big'); console.log(football.getSize()); //big
Давайте посмотрим, что происходит в приведенном выше коде.
- Мы создаем конструктор Ball, который имеет свойство size и getSize.
- Затем мы создали экземпляр мяча под названием football, используя ключевое слово new.
- new является ключевым здесь. new создает пустой объект, равный ему экземпляру football, а затем передает этот пустой объект (экземпляр football) в функцию-конструктор и присоединяется к нему. на это ключевое слово.
- Короче говоря, новый делает это, Ball.apply (футбол, размер)
Прототип
Итак, теперь мы знаем конструкторы, давайте посмотрим на прототипы. Конструктор и функции в javascript являются объектами. А у объектов есть свойства, не так ли? Итак, конструкторы также должны иметь свойства, и одно из таких свойств - прототип.
console.dir(Ball); //ƒ Ball(size) arguments: null caller: null length: 1 name: "Ball" prototype: constructor: ƒ (size) __proto__: Object __proto__: ƒ () [[FunctionLocation]]: (index):67 [[Scopes]]: Scopes[1]
console.dir позволяет мне смотреть на объект более подробно. В приведенном выше коде мы могли видеть свойство, прикрепленное к конструктору Ball, под названием prototype. И этот прототип имеет свойство с именем __proto__, как и конструктор. Зачем это ????
- __proto__ - Каждый объект в javascript имеет это свойство. Он относится к создателю этого объекта. И когда я говорю «каждый предмет», я буквально это имею в виду. Есть сомнения? Проверь это.
console.dir({}) /* Object __proto__: Object */
Этот __proto__ указывает на своего создателя (т. Е. На главный объект, Object), а конструктор __proto__ также указывает на Object.
2. прототип. Представьте, что у него есть свойство конструктора, к которому мы можем прикрепить некоторые методы (пока).
const Ball = function(size){ this.size = size; }; Ball.prototype.getSize = function(){ console.log(this.size); } let football = new Ball('big'); let tennisball = new Ball('small'); console.log(football.getSize()); //big console.log(tennisball.getSize()); //small
В приведенном выше коде мы присоединяем метод getSize к прототипу конструктора Ball, и все экземпляры имеют доступ к прототипу конструктора и прикрепленным к нему методам.
Хорошо, как?
console.dir(football); //Ball size : "big", __proto__ : getSize:ƒ () constructor: ƒ (size) __proto__: Object
Попробуем понять, что происходит,
- Как и ожидалось, у нас есть свойство size в экземпляре football.
- __proto__ должен указывать на своего создателя, а создателем экземпляра football является конструктор Ball.
- Возможно, вы думаете, что getSize делает с __proto__, когда мы прикрепили его к прототипу. На самом деле происходит то, что ключевое слово new создает ссылку на getSize на __proto__. Если вы развернете функцию конструктора в консоли, вы обнаружите, что к прототипу прикреплен getSize.
//constructor:ƒ (size) arguments:null caller:null length:1 name:"Ball" prototype: getSize:ƒ () constructor:ƒ (size) __proto__:Object
4. Видите, на прототипе есть ваш getSize. А __proto__ в прототипе конструктора относится к создателю (Мастер-объект).
И последнее, на что я хотел бы ответить в этой теме: Какая польза от прототипа ??? Здесь у нас есть два экземпляра, поэтому вы не можете понять причину, но предположим, что у нас есть еще чем 100 экземпляров, и к каждому экземпляру прикреплены некоторые методы, что замедлит работу вашего приложения.
Поэтому мы прикрепляем методы к прототипу конструктора и стараемся сохранить минимальное количество свойств и методов в этих экземплярах для лучшего времени выполнения. Сотни экземпляров с прикрепленными к ним различными методами - не лучший подход.
Вот почему мы прикрепляем все методы к прототипу, и каждый экземпляр, которому требуется доступ к методу, будет искать метод в функции-конструкторе, и если там он не находит метод, он ищет его в __proto__ и так далее.
Я надеюсь, что вы, ребята, сможете осмыслить эти две концепции, поскольку эти две темы являются базовой основой прототипного наследования.
В следующей статье мы обсудим «Object.create» и «цепочку прототипов» и то, как работает наследование, когда все эти четыре компонента объединены.