Эта статья была впервые опубликована на личномGithub, вопросы/fxxk приветствуются.
предисловие
ES6
Первая версия15
год6
месяц, и эта статья была впервые создана в16
Годы, то есть первые дни работы автора на фронтенде. в это время,ES6
Многие функции все еще вstage
Сцена гораздо менее популярна, чем сейчас, чтобы писать было легче.JavaScript
, автор потратил целый день, внимательно разбирайтесь в прототипе - это для зрелогоJavaScript
Гора, которую разработчикам предстоит преодолеть.
ES6
Приносит слишком много синтаксического сахара, где стрелочные функции маскируютthis
магия, иclass
Это также скрывает то, о чем эта статья будет подробно говорить.原型
.
Недавно я переписал эту статью, благодаря этой статье вы узнаете:
- Как использовать
ES5
Мачные классы; - понимать
prototype
а также__proto__
; - Понимание цепочек прототипов и прототипного наследования;
- Узнать больше
JavaScript
этот язык.
Введение: обычные объекты и функциональные объекты
существуетJavaScript
Всегда существовала поговорка, что все является объектом. Фактически, вJavaScript
, объект тоже другой, мы можем разделить его на普通对象
а также函数对象
.Object
а такжеFunction
являетсяJavaScript
Два типичных函数对象
. Функциональный объект — это чистая функция, так называемая函数对象
, фактически с помощьюJavaScript
существует模拟类
.
Итак, что именно普通对象
, что函数对象
Шерстяная ткань? См. пример ниже:
Сначала мы создали три отдельныхFunction
а такжеObject
Пример:
function fn1() {}
const fn2 = function() {}
const fn3 = new Function('language', 'console.log(language)')
const ob1 = {}
const ob2 = new Object()
const ob3 = new fn1()
Распечатав следующие результаты, можно получить:
console.log(typeof Object); // function
console.log(typeof Function); // function
console.log(typeof ob1); // object
console.log(typeof ob2); // object
console.log(typeof ob3); // object
console.log(typeof fn1); // function
console.log(typeof fn2); // function
console.log(typeof fn3); // function
В приведенном выше примереob1
,ob2
,ob3
являются обычными объектами (обаObject
экземпляр), в то время какfn1
,fn2
,fn3
обаFunction
экземпляр, называемый函数对象
.
Как это отличить? На самом деле, просто запомните эту фразу:
- все
Function
Примеры函数对象
, в то время как другие普通对象
.
Говоря об этом, внимательные студенты отправят вопрос.В начале мы уже упоминали,Object
а такжеFunction
оба函数对象
, и здесь мы говорим: всеFunction
Примеры函数对象
, этоFunction
СлишкомFunction
пример?
Держите этот вопрос пока. Далее краткое содержание этого раздела:
Как видно из рисунка, реализация самого объекта еще зависит отКонструктор. Это原型链
Для чего его используют?
Как мы все знаем, как объектно-ориентированный язык, он должен иметь следующие характеристики:
- уникальность объекта
- абстракция
- наследование
- полиморфизм
Цель исходной цепочки — достичь继承
.
Дополнительно: прототип и __proto__
Как именно цепочка прототипов реализует наследование? Во-первых, мы должны представить двух братьев:prototype
а также__proto__
, это вJavaScript
Две переменные, которые вездесущи (если вы много отлаживаете), однако эти две переменные существуют не на всех объектах, сначала посмотрите на таблицу:
тип объекта | prototype |
__proto__ |
---|---|---|
Обычный объект (НЕТ) | ❎ | ✅ |
Функциональный объект (FO) | ✅ | ✅ |
Прежде всего, мы делаем следующие выводы:
- Только
函数对象
имеютprototype
это свойство; -
prototype
а также__proto__
обеJavaScript
Создается автоматически при определении функции или объекта预定义属性
.
Далее проверим два вышеприведенных вывода:
function fn() {}
console.log(typeof fn.__proto__); // function
console.log(typeof fn.prototype); // object
const ob = {}
console.log(typeof ob.__proto__); // function
console.log(typeof ob.prototype); // undefined,哇!果然普通对象没有 prototype
Поскольку это предустановленный атрибут на уровне языка, в чем разница между ними? Мы все же исходим из вывода и приводим следующие два вывода:
-
prototype
инстанс__proto__
остроконечный (пассивный) -
__proto__
конструкторуprototype
(инициатива)
Ничего себе, это означает, что следующий код имеет место:
console.log(fn.__proto__ === Function.prototype); // true
console.log(ob.__proto__ === Object.prototype); // true
Выглядит классно, и вывод моментально подтверждается. А так ли это хорошо? Тогда возникает вопрос: Так какfn
является функциональным объектом, тоfn.prototype.__proto__
Что именно это означает?
Вот моя попытка решить эту проблему:
- первое использование
typeof
получатьfn.prototype
тип:"object"
- вау, так как это
"object"
,Этоfn.prototype
Разве это не экземпляр Object ? Согласно вышеприведенным выводам, быстро напишите проверочный код:
console.log(fn.prototype.__proto__ === Object.prototype) // true
Далее, если вы хотите писать быстро, при создании функцииJavaScript
Можете ли вы также быстро написать код инициализации прототипа функции:
// 实际代码
function fn1() {}
// JavaScript 自动执行
fn1.protptype = {
constructor: fn1,
__proto__: Object.prototype
}
fn1.__proto__ = Function.prototype
В этот момент у вас есть чувство просветления? Кроме того, поскольку обычные объекты函数对象
создать экземпляр (new
), и экземпляр не может быть создан снова, равно как и экземпляр другого объекта.__proto__
указывая на этоprototype
Так что ссылка в начале этого раздела普通对象没有 prototype 属性
Этот вывод кажется очень простым для понимания. Из вышеприведенного анализа мы также можем видеть, чтоfn1.protptype
Это просто обычный объект, его не существуетprotptype
Атрибуты.
Оглядываясь назад на предыдущий раздел, у нас остался один вопрос:
- Это
Function
СлишкомFunction
пример?
пришло время избавиться от应该
Пусть настроится. Так что в этот момент, пожалуйста, покажите мне свой код!
Посмотреть ответ
console.log(Function.__proto__ === Function.prototype) // true
Фокус: цепь прототипов
В предыдущем разделе мы подробно объяснилиprototype
а также__proto__
, на самом деле этим двум братьям в основном предстоит строитьСеть прототипови существуют.
Сначала предыдущий фрагмент кода:
const Person = function(name, age) {
this.name = name
this.age = age
} /* 1 */
Person.prototype.getName = function() {
return this.name
} /* 2 */
Person.prototype.getAge = function() {
return this.age
} /* 3 */
const ulivz = new Person('ulivz', 24); /* 4 */
console.log(ulivz) /* 5 */
console.log(ulivz.getName(), ulivz.getAge()) /* 6 */
Объясните детали реализации:
- воплощать в жизнь
1
, который создает конструкторPerson
, следует отметить, что, как было сказано ранее, в это времяPerson.prototype
был создан автоматически, он содержитconstructor
а также__proto__
эти два свойства; - воплощать в жизнь
2
, к объектуPerson.prototype
добавил методgetName()
; - воплощать в жизнь
3
, к объектуPerson.prototype
добавил методgetAge()
; - воплощать в жизнь
4
, конструкторPerson
создал экземплярulivz
, стоит отметить, что при создании экземпляра конструктора он автоматически выполнит конструктор. - войти в браузер
5
выход, то естьulivz
должно быть:
{
name: 'ulivz',
age: 24
__proto__: Object // 实际上就是 `Person.prototype`
}
Объединяя опыт предыдущего раздела, справедливо следующее уравнение:
console.log(ulivz.__proto__ == Person.prototype) // true
- воплощать в жизнь
6
, так какulivz
не найдено вgetName()
а такжеgetAge()
Эти два метода продолжат поиск по цепочке прототипов, то есть через__proto__
Посмотрите вверх, так что скоро вulviz.__proto__
в, т.е.Person.prototype
Нашел эти два метода в , так что остановите поиск и выполните, чтобы получить результат.
ЭтоJavaScript
прототипическое наследование. точно,JavaScript
прототипное наследование осуществляется через__proto__
и с помощьюprototype
быть реализованным.
Таким образом, мы можем сделать следующий вывод:
- функциональный объект
__proto__
направлениеFunction.prototype
;(рассмотрение) - функциональный объект
prototype
направлениеinstance.__proto__
;(рассмотрение) - общий объект
__proto__
направлениеObject.prototype
;(рассмотрение) - Обычные предметы не
prototype
атрибут; (обзор) - При доступе к свойству/методу объекта, если он не найден в текущем объекте, он попытается получить доступ
ob.__proto__
, то есть прототип конструктора, обращающегося к объектуobCtr.prototype
, если он все еще не найден, он продолжит поискobCtr.prototype.__proto__
, как поиск в последовательности. Если в какой-то момент свойство будет найдено, оно немедленно вернет значение и прекратит поиск цепочки прототипов, если не будет найдено, вернетundefined
.
Чтобы проверить свое понимание вышеизложенного, проанализируйте следующие два вопроса:
- Каков результат следующего кода?
console.log(ulivz.__proto__ === Function.prototype)
Посмотреть Результаты
false-
Person.__proto__
а такжеPerson.prototype.__proto__
Куда они указывают?
Посмотреть аналитику
Как упоминалось ранее, вJavaScript
Все вещи являются объектами.Person
очевидноFunction
экземпляр, следовательно,Person.__proto__
направлениеFunction.prototype
:
console.log(Person.__proto__ === Function.prototype) // true
потому чтоPerson.prototype
обычный объект, поэтомуPerson.prototype.__proto__
направлениеObject.prototype
console.log(Person.prototype.__proto__ === Object.prototype) // true
чтобы доказатьPerson.__proto__
не находится в цепочке прототиповObject
,так же какPerson.prototype.__proto__
не находится в цепочке прототиповFunction
, в сочетании со следующим утверждением для проверки:
console.log(Person.__proto__ === Object.prototype) // false
console.log(Person.prototype.__proto__ == Function.prototype) // false
Ultimate: цепная схема прототипа
В предыдущем разделе мы фактически оставили вопрос:
- Если цепочку прототипов искать один за другим, если она не будет найдена, когда она остановится? То есть где конец цепочки прототипов?
Мы можем быстро проверить с помощью следующего кода:
function Person() {}
const ulivz = new Person()
console.log(ulivz.name)
Очевидно, приведенный выше выводundefined
. Процесс поиска кратко описан ниже:
ulivz // 是一个对象,可以继续
ulivz['name'] // 不存在,继续查找
ulivz.__proto__ // 是一个对象,可以继续
ulivz.__proto__['name'] // 不存在,继续查找
ulivz.__proto__.__proto__ // 是一个对象,可以继续
ulivz.__proto__.__proto__['name'] // 不存在, 继续查找
ulivz.__proto__.__proto__.__proto__ // null !!!! 停止查找,返回 undefined
Ничего себе, оказывается, конец дороги пуст.
Наконец, вернитесь и посмотрите на демо-код из предыдущего раздела:
const Person = function(name, age) {
this.name = name
this.age = age
} /* 1 */
Person.prototype.getName = function() {
return this.name
} /* 2 */
Person.prototype.getAge = function() {
return this.age
} /* 3 */
const ulivz = new Person('ulivz', 24); /* 4 */
console.log(ulivz) /* 5 */
console.log(ulivz.getName(), ulivz.getAge()) /* 6 */
Давайте нарисуем диаграмму цепочки прототипов, или, другими словами, нарисуем всю цепочку прототипов? Пожалуйста, посмотрите на изображение ниже:
PS: я изменил chl (аббревиатура моего китайского имени) на ulivz (имя на Github), так что chl на этой картинке на самом деле ulivz.Когда я рисовал эту картинку, я все еще использовал окна ==
Нарисовав эту картинку, в основном можно ответить на все предыдущие вопросы.
Вместо того, чтобы говорить, что все является объектом, кажется более образным, что все пусто.
Приправа: конструктор
Это упоминалось ранее, но только объекты-прототипыconstructor
это свойство,constructor
Используется для указания на объект функции, который на него ссылается.
Person.prototype.constructor === Person //true
console.log(Person.prototype.constructor.prototype.constructor === Person) //true
Это своего рода циклическая ссылка. Конечно, вы также можете нарисовать его на диаграмме цепочки прототипов в предыдущем разделе, поэтому я не буду здесь вдаваться в подробности.
Дополнение: Прототипное наследование 6 встроенных (функций) объектов в JavaScript
Благодаря предыдущему обсуждению в сочетании с соответствующей проверкой кода выявляется следующая схема цепочки прототипов:
Таким образом, мы дополнительно усиливаем эти два момента:
- Любой встроенный функциональный объект (класс) сам по себе
__proto__
оба указывают наFunction
объект-прототип;- Кроме
Oject
объекта-прототипа__proto__
направлениеnull
, объект-прототип всех других встроенных функциональных объектов__proto__
оба указывают наobject
.
Чтобы сократить время, затрачиваемое читателями на ввод кода, специально дается проверочный код, надеясь облегчить ваше понимание.
Array:
console.log(arr.__proto__)
console.log(arr.__proto__ == Array.prototype) // true
console.log(Array.prototype.__proto__== Object.prototype) // true
console.log(Object.prototype.__proto__== null) // true
RegExp:
var reg = new RegExp;
console.log(reg.__proto__)
console.log(reg.__proto__ == RegExp.prototype) // true
console.log(RegExp.prototype.__proto__== Object.prototype) // true
Date:
var date = new Date;
console.log(date.__proto__)
console.log(date.__proto__ == Date.prototype) // true
console.log(Date.prototype.__proto__== Object.prototype) // true
Boolean:
var boo = new Boolean;
console.log(boo.__proto__)
console.log(boo.__proto__ == Boolean.prototype) // true
console.log(Boolean.prototype.__proto__== Object.prototype) // true
Number:
var num = new Number;
console.log(num.__proto__)
console.log(num.__proto__ == Number.prototype) // true
console.log(Number.prototype.__proto__== Object.prototype) // true
String:
var str = new String;
console.log(str.__proto__)
console.log(str.__proto__ == String.prototype) // true
console.log(String.prototype.__proto__== Object.prototype) // true
Суммировать
Несколько кратких резюме:
- подобно
A
пройти черезnew
созданныйB
,ноB.__proto__ = A.prototype
; -
__proto__
является отправной точкой поиска цепочки прототипов; - воплощать в жизнь
B.a
, если вB
не найдено вa
, будет вB.__proto__
в, то естьA.prototype
искать, еслиA.prototype
до сих пор не нашел вObject.prototype
, если не найдено, потому чтоObject.prototype.__proto__
направлениеnull
, поэтому возвращаетсяundefined
; - Почему все пусто, или это предложение наверху цепочки прототипов должно быть
Object.prototype.__proto__ ——> null
.
Наконец, я оставляю вас с вопросом:
- Как использовать
JavaScript
Как насчет наследования классов реализации?
Смотрите следующую статью в моей серии Prototype«Углубленные принципы наследования JavaScript»Выяснить.
Выше полный текст заканчивается.
Примечание: Кроме того, эта статья является личным резюме, и в некоторых выражениях могут быть пропуски.Если вы обнаружите, что в этой статье не хватает, во избежание недоразумений, пожалуйста, не стесняйтесь указать на это в комментариях, или дать мне советissue, спасибо~