Эта статья была впервые опубликована на личном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, спасибо~