Каждому разработчику javascript в его учебном цикле трудно осмыслить это прототипное наследование. Наследование в javascript уникально, оно полностью отличается от того, что мы видим в других языках, таких как Java или Python. В таких языках, как Java и C ++, существует концепция классов, в то время как в Javascript нет таких классов, как Java (да, ES6 предоставляет нам классы, но это не то же самое, это просто синтаксический сахар).

Наследование в javascript - большая тема, поэтому нам нужно пройти через некоторые основные концепции, например, шаг за шагом. Это 4 основных компонента: -

  1. Конструкторы
  2. Прототип
  3. Object.create ()
  4. Цепочка прототипов

В этой статье я хотел бы пройтись по первым двум темам, и нашим основным мотивом было бы разобраться в них, прежде чем переходить к двум другим. Лучшее понимание этих тем необходимо для понимания наследования.

Конструктор

Конструктор помогает нам создавать и инициализировать объекты в javascript. Просто? Давайте посмотрим, как это сделать.

//Example of a Constructor
const Ball = function(size){
   this.size = size;
   this.getSize = function(){
     console.log(this.size);
}
};
  1. Вышеупомянутый конструктор - это простейший тип конструктора.
  2. Конструктор отчасти похож на функции javascript, которые принимают объект и присоединяют к нему свойство (здесь size, getSize).
  3. Первая буква конструктора должна быть в верхнем регистре (не правило большого пальца, но это предпочтительно).
//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

Давайте посмотрим, что происходит в приведенном выше коде.

  1. Мы создаем конструктор Ball, который имеет свойство size и getSize.
  2. Затем мы создали экземпляр мяча под названием football, используя ключевое слово new.
  3. new является ключевым здесь. new создает пустой объект, равный ему экземпляру football, а затем передает этот пустой объект (экземпляр football) в функцию-конструктор и присоединяется к нему. на это ключевое слово.
  4. Короче говоря, новый делает это, 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__, как и конструктор. Зачем это ????

  1. __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

Попробуем понять, что происходит,

  1. Как и ожидалось, у нас есть свойство size в экземпляре football.
  2. __proto__ должен указывать на своего создателя, а создателем экземпляра football является конструктор Ball.
  3. Возможно, вы думаете, что 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» и «цепочку прототипов» и то, как работает наследование, когда все эти четыре компонента объединены.