прелюдия
- Текст относительно короткий, и прочитать его за три минуты не составит труда (ну...).
- Конечно, лучше потратить еще полчаса на обдумывание и осмысление.
текст
Конструктор и прототип
В отличие от большинства объектно-ориентированных языков, 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 содержит довольно подробное объяснение цепочек прототипов.