Три минуты на чтение прототипа JavaScript и цепочки прототипов

внешний интерфейс JavaScript

прелюдия

  • Текст относительно короткий, и прочитать его за три минуты не составит труда (ну...).
  • Конечно, лучше потратить еще полчаса на обдумывание и осмысление.

текст

Конструктор и прототип

В отличие от большинства объектно-ориентированных языков, JavaScript не вводит понятие класса, но JavaScript по-прежнему много использует объекты.Чтобы обеспечить связь между объектами, JavaScript вводит понятие прототипа и цепочки прототипов.

В Java объявление экземпляра записывается так:

ClassName obj = new ClassName()

Чтобы гарантировать, что JavaScript «выглядит как Java», в JavaScript также добавлен новый оператор:

var obj = new FunctionName()

Видно, что, в отличие от Java, за оператором new в JavaScript следует не имя класса, а имя функции.JavaScript создает экземпляр не через класс, а непосредственно через конструктор.

function Dog(name, color) {
    this.name = name
    this.color = color
    this.bark = () => {
        console.log('wangwang~')
    }
}

const dog1 = new Dog('dog1', 'black')
const dog2 = new Dog('dog2', 'white')

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

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


Здесь нужно использоватьпрототип:

  • У каждого конструктора есть свойство прототипа, которое указывает на объект, объект-прототип. Когда экземпляр создается с помощью этого конструктора, объект-прототип, на который указывает свойство прототипа, становится объектом-прототипом экземпляра.
  • Объект-прототип по умолчанию имеет свойство конструктора, которое указывает на конструктор, указывающий на него (то есть конструктор и объект-прототип указывают друг на друга).
  • У каждого объекта есть скрытое свойство [[prototype]], указывающее на его объект-прототип, доступ к которому можно получить черезObject.getPrototypeOf(obj)илиobj.__proto__посетить.
  • На самом деле свойство прототипа конструктора и свойство [[prototype]] объекта-экземпляра, который он создает, указывают на один и тот же объект, т.е.对象.__proto__ === 函数.prototype.
  • Как упоминалось выше, объект-прототип используется для хранения части свойств, общих для экземпляра.
  • В JavaScript все объекты наследуются от своих объектов-прототипов, и наоборот, все объекты могут существовать как объекты-прототипы.
  • При доступе к свойствам объекта JavaScript сначала просматривает свойства самого объекта, и если он не будет найден, он перейдет к объекту-прототипу объекта, чтобы найти его.

Затем приведенный выше код можно немного изменить, здесь метод bark помещается в прототип конструктора Dog:

function Dog(name, color) {
    this.name = name
    this.color = color
}

Dog.prototype.bark = () => {
    console.log('wangwang~')
}

Затем снова создайте экземпляр через этот конструктор и вызовите его метод bark:

const dog1 = new Dog('dog1', 'black')
dog1.bark()  //'wangwang~'

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

const dog2 = new Dog('dog2', 'white')
dog2.bark() = () => {
    console.log('miaomiaomiao???')
}
dog1.bark()  //'wangwang~'
dog2.bark()  //'miaomiaomiao???'

Здесь dog2 переписывает метод bark и не оказывает никакого влияния на dog1, потому что его операция по перезаписи метода bark фактически добавляет новый метод для себя, так что метод bark в прототипе перезаписывается, а не напрямую модифицирует метод прототипа. Если вы хотите изменить метод в прототипе, вам нужно передать свойство прототипа конструктора:

Dog.prototype.bark = () => {
    console.log('haha~')
}
dog1.bark()  //'haha~'
dog2.bark()  //'haha~'

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

Цепочка прототипов и наследование

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

Все цепочки прототипов заканчиваются свойством прототипа функции Object, поскольку объекты в JavaScript создаются с помощью Object() по умолчанию. Объект-прототип, на который указывает Objec.prototype, также имеет прототип, но его прототип имеет значение null, а у null нет прототипа.

Наследование в JavaScript может быть достигнуто через цепочку прототипов. Наследование в JavaScript достаточно гибкое. Существует множество способов реализации наследования. Здесь мы только представляем один из наиболее часто используемых методов наследования, которыйнаследование композиции.

function Dog(name, color) {
    this.name = name
    this.color = color
}

Dog.prototype.bark = () => {
    console.log('wangwang~')
}

function Husky(name, color, weight) {
    Dog.call(this, name, color)
    this.weight = weight
}

Husky.prototype = new Dog()

Здесь объявляется новый конструктор Husky, который наследует свойства Dog через метод call (функция метода call может быть просто понята как добавление свойств Dog в Husky, потому что она также включает в себя другие точки знаний, так что я не буду' не вдаваться в подробности) и добавил атрибут веса. Затем используйте функцию Dog для создания экземпляра в качестве объекта-прототипа Husky и назначьте его Husky.prototype для наследования метода. Таким образом, экземпляр, созданный функцией Husky, имеет свойства и методы Dog.

Эпилог

Если вы хотите узнать больше об объектах и ​​цепочках прототипов в JavaScript, я рекомендую Красную книгу ("Расширенное программирование на JavaScript (3-е издание)"). Глава 6 содержит довольно подробное объяснение цепочек прототипов.