Пустой прототип JavaScript

внешний интерфейс GitHub JavaScript Windows
Пустой прототип JavaScript

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

Держите этот вопрос пока. Далее краткое содержание этого раздела:

image_1b4867lll1fqfiqt14o17gccjb1m.png-58.3kB

Как видно из рисунка, реализация самого объекта еще зависит отКонструктор. Это原型链Для чего его используют?

Как мы все знаем, как объектно-ориентированный язык, он должен иметь следующие характеристики:

  • уникальность объекта
  • абстракция
  • наследование
  • полиморфизм

Цель исходной цепочки — достичь继承.


Дополнительно: прототип и __proto__

Как именно цепочка прототипов реализует наследование? Во-первых, мы должны представить двух братьев:prototypeа также__proto__, это вJavaScriptДве переменные, которые вездесущи (если вы много отлаживаете), однако эти две переменные существуют не на всех объектах, сначала посмотрите на таблицу:

тип объекта prototype __proto__
Обычный объект (НЕТ)
Функциональный объект (FO)

Прежде всего, мы делаем следующие выводы:

  1. Только函数对象имеютprototypeэто свойство;
  2. 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

Поскольку это предустановленный атрибут на уровне языка, в чем разница между ними? Мы все же исходим из вывода и приводим следующие два вывода:

  1. prototypeинстанс__proto__остроконечный (пассивный)
  2. __proto__конструкторуprototype(инициатива)

Ничего себе, это означает, что следующий код имеет место:

console.log(fn.__proto__ === Function.prototype); // true
console.log(ob.__proto__ === Object.prototype); // true

Выглядит классно, и вывод моментально подтверждается. А так ли это хорошо? Тогда возникает вопрос: Так какfnявляется функциональным объектом, тоfn.prototype.__proto__Что именно это означает?

Вот моя попытка решить эту проблему:

  1. первое использованиеtypeofполучатьfn.prototypeтип:"object"
  2. вау, так как это"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. воплощать в жизнь1, который создает конструкторPerson, следует отметить, что, как было сказано ранее, в это времяPerson.prototypeбыл создан автоматически, он содержитconstructorа также__proto__эти два свойства;
  2. воплощать в жизнь2, к объектуPerson.prototypeдобавил методgetName();
  3. воплощать в жизнь3, к объектуPerson.prototypeдобавил методgetAge();
  4. воплощать в жизнь4, конструкторPersonсоздал экземплярulivz, стоит отметить, что при создании экземпляра конструктора он автоматически выполнит конструктор.
  5. войти в браузер5выход, то естьulivzдолжно быть:
{
     name: 'ulivz',
     age: 24
     __proto__: Object // 实际上就是 `Person.prototype`
}

Объединяя опыт предыдущего раздела, справедливо следующее уравнение:

    console.log(ulivz.__proto__ == Person.prototype)  // true
  1. воплощать в жизнь6, так какulivzне найдено вgetName()а такжеgetAge()Эти два метода продолжат поиск по цепочке прототипов, то есть через__proto__Посмотрите вверх, так что скоро вulviz.__proto__в, т.е.Person.prototypeНашел эти два метода в , так что остановите поиск и выполните, чтобы получить результат.

ЭтоJavaScriptпрототипическое наследование. точно,JavaScriptпрототипное наследование осуществляется через__proto__и с помощьюprototypeбыть реализованным.

Таким образом, мы можем сделать следующий вывод:

  1. функциональный объект__proto__направлениеFunction.prototype;(рассмотрение)
  2. функциональный объектprototypeнаправлениеinstance.__proto__;(рассмотрение)
  3. общий объект__proto__направлениеObject.prototype;(рассмотрение)
  4. Обычные предметы неprototypeатрибут; (обзор)
  5. При доступе к свойству/методу объекта, если он не найден в текущем объекте, он попытается получить доступob.__proto__, то есть прототип конструктора, обращающегося к объектуobCtr.prototype, если он все еще не найден, он продолжит поискobCtr.prototype.__proto__, как поиск в последовательности. Если в какой-то момент свойство будет найдено, оно немедленно вернет значение и прекратит поиск цепочки прототипов, если не будет найдено, вернетundefined.

Чтобы проверить свое понимание вышеизложенного, проанализируйте следующие два вопроса:

  1. Каков результат следующего кода?
console.log(ulivz.__proto__ === Function.prototype)
Посмотреть Результаты false

  1. 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 */

Давайте нарисуем диаграмму цепочки прототипов, или, другими словами, нарисуем всю цепочку прототипов? Пожалуйста, посмотрите на изображение ниже:

原型链.png-41.2kB

PS: я изменил chl (аббревиатура моего китайского имени) на ulivz (имя на Github), так что chl на этой картинке на самом деле ulivz.Когда я рисовал эту картинку, я все еще использовал окна ==

Нарисовав эту картинку, в основном можно ответить на все предыдущие вопросы.

Вместо того, чтобы говорить, что все является объектом, кажется более образным, что все пусто.


Приправа: конструктор

Это упоминалось ранее, но только объекты-прототипыconstructorэто свойство,constructorИспользуется для указания на объект функции, который на него ссылается.

Person.prototype.constructor === Person //true
console.log(Person.prototype.constructor.prototype.constructor === Person) //true

Это своего рода циклическая ссылка. Конечно, вы также можете нарисовать его на диаграмме цепочки прототипов в предыдущем разделе, поэтому я не буду здесь вдаваться в подробности.


Дополнение: Прототипное наследование 6 встроенных (функций) объектов в JavaScript

Благодаря предыдущему обсуждению в сочетании с соответствующей проверкой кода выявляется следующая схема цепочки прототипов:

image_1b496ie7el7m1rvltoi17he1b459.png-52.6kB

Таким образом, мы дополнительно усиливаем эти два момента:

  1. Любой встроенный функциональный объект (класс) сам по себе__proto__оба указывают наFunctionобъект-прототип;
  2. Кроме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 

Суммировать

Несколько кратких резюме:

  1. подобноAпройти черезnewсозданныйB,ноB.__proto__ = A.prototype;
  2. __proto__является отправной точкой поиска цепочки прототипов;
  3. воплощать в жизньB.a, если вBне найдено вa, будет вB.__proto__в, то естьA.prototypeискать, еслиA.prototypeдо сих пор не нашел вObject.prototype, если не найдено, потому чтоObject.prototype.__proto__направлениеnull, поэтому возвращаетсяundefined;
  4. Почему все пусто, или это предложение наверху цепочки прототипов должно бытьObject.prototype.__proto__ ——> null.

Наконец, я оставляю вас с вопросом:

  • Как использоватьJavaScriptКак насчет наследования классов реализации?

Смотрите следующую статью в моей серии Prototype«Углубленные принципы наследования JavaScript»Выяснить.

Выше полный текст заканчивается.

Примечание: Кроме того, эта статья является личным резюме, и в некоторых выражениях могут быть пропуски.Если вы обнаружите, что в этой статье не хватает, во избежание недоразумений, пожалуйста, не стесняйтесь указать на это в комментариях, или дать мне советissue, спасибо~