Я уверен, что вы прошли через основы JavaScript. К настоящему времени вы могли знать, что массивы, функции, объекты, даты - все это объекты. JavaScript - это прототип. основанный на языке, который означает, что наследование работает с помощью так называемых прототипов. Каждый объект в JS имеет свойство прототипа.

Хорошо, теперь хватит возни с прототипами 😅, давайте разберемся.

Обычный подход к конструктору функций

Как вы, возможно, знаете о подходе конструктора функций в JS для создания объектов, мы собираемся создать конструктор функции как таковой:

var Person = function(name,yearOfBirth,job) {
                      this.name = name;
                      this.yearOfBirth = yearOfBirth;
                      this.job = job;
                      this.calcAge = function(){
                      console.log(2020 - this.yearOfBirth)
                      }
              }

Теперь мы создадим два объекта, Адам и Ева, с помощью функции конструктора Person:

var Adam = new Person('Adam', 1998, 'teacher');
var Eve = new Person('Eve', 1993, 'painter');

Проблема с описанным выше подходом заключается в том, что при выполнении кода JS создает по две копии каждого из свойств и методов для двух объектов. Неэффективно иметь два экземпляра функции calcAge () для хранения отдельных экземпляров функций для Каждый объект тратит впустую память. Вот тут-то и появляются прототипы.

Прототипы

Каждый объект в JS имеет свойство прототипа. Это свойство прототипа является объектом, известным как объект прототипа. Это свойство прототипа, которое упрощает наследование в JS.

Рассматривая тот же пример, если объект Адам хочет унаследовать метод или свойство от объекта Person, мы должны добавить этот метод или свойство к свойству prototype Person. Что действительно важно отметить здесь, так это то, что Прототип человека - это не прототип самого человека, а всех экземпляров, которые создаются с помощью чертежа человека. Итак, в этом примере свойство прототипа человека является прототипом Адама.

Итак, модифицированный конструктор Person выглядит так:

var Person = function(name,yearOfBirth,job) {
                      this.name = name;
                      this.yearOfBirth = yearOfBirth;
                      this.job = job;
              }
Person.prototype.calcAge = function(){
                      console.log(2020 - this.yearOfBirth)
                      }

Итак, теперь, когда метод calcAge () вызывается как для объектов Adam, так и для Eve:

Adam.calcAge(); //22
Eve.calcAge(); //27

Теперь вы можете заметить, что хотя метод calcAge () не находится непосредственно в объекте Person, мы все еще можем получить к нему доступ, потому что он находится в свойстве prototype конструктора функции Person.

Свойства объекта также можно добавить через конструктор функции:

Person.prototype.lastName = 'Wallace';

к которому можно получить доступ как:

Adam.lastName; // Wallace
Eve.lastName; // Wallace

Давайте откроем прототип Адама, Евы и человека в консоли Chrome:

Обратите внимание, как свойства __proto__ Адама, Евы и Person.prototype выглядят одинаково. Давайте проверим, совпадают они на самом деле или нет:

Person.prototype === Adam.__proto__ //true
Adam.__proto__ === Eve.__proto__ //true

Приведенное выше утверждение доказывает, что свойства __proto__ Адама и Евы указывают на объект-прототип функции конструктора Person.

Цепочка прототипов

В JS каждый объект, который мы когда-либо создавали, является экземпляром конструктора Object, который имеет набор методов в свойстве prototype.

Звучит запутанно, правда? Давайте снова рассмотрим наш исходный пример:

Мы уже знаем, что Адам является экземпляром конструктора Person. Но это еще не все, потому что сам объект Person является экземпляром еще более крупного конструктора, которым является объект Object. Звучит повальное увлечение, верно? но вот как это работает.

Каждый объект, который мы создаем, является экземпляром конструктора Object, который имеет набор методов в свойстве prototype.

Как вы уже могли догадаться, объект Person может наследовать эти методы и вызывать их, плюс объект Adam также наследует эти методы и также может их использовать. Давайте проверим это в консоли, проверив объект Adam

Если мы посмотрим на прототип Адама, мы заметим, что к нему также прикреплен объект __proto__, это свойство prototype конструктора Object, которое содержит набор методов, связанных со свойством prototype конструктора функции Object. Это объясняет все в целом. цепочка прототипов.

Как мы уже обсуждали, у нас есть доступ к этим методам конструктора объектов, и их можно использовать для объекта Adam.

Adam.hasOwnProperty('job') // true
Adam.hasOwnProperty('lastName') // false
//because hasOwnProperty() looks for only own properties and we inherited lastName from the prototype,it's not really Adam's own property

Adam instanceof Person // true

Итак, это все для нашего примера, но прежде чем завершить, давайте взглянем на массивы в контексте цепочки прототипов.

Мы определим массив и посмотрим на него в консоли.

Расширяя его, мы замечаем, что свойство length, которое вы, возможно, помните, используется для вычисления длины массива, и оно всегда сохраняется прямо здесь, в экземпляре массива, и поэтому мы можем использовать его так легко. еще лучше, поскольку мы видим прототип этого массива __proto__, который, конечно же, является конструктором функции Array, который встроен прямо в JS и позволяет нам строить массивы, и здесь, в его свойстве прототипа, мы можем увидеть все методы, которые мы можем использовать с массивами, такими как pop (), push (), slice () и т. д. Итак, теперь вы точно знаете, как вы можете вызывать эти методы для массивов, благодаря цепочке прототипов

Вот как на самом деле полезно наследование!

Удачного кодирования !! 😇