Как наследовать объект Date? Полностью понять наследование JS из вопроса.

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

предисловие

Мнения ограничены.Если есть какое-либо неподходящее описание, пожалуйста, помогите указать на него вовремя.Если есть какая-то ошибка, она будет исправлена ​​вовремя.

Обновление 20180201:

Измените описание слов, например, объедините паразитный тип с паразитным комбинированным типом, и исправьте множество опечаток (спасибо @Yao Ding за обратную связь).

----------Длинный текст + предупреждение с несколькими картинками, это займет определенное время----------

История начинается с реальной нужды. . .

Однажды кто-то попросил меня помочь в написании класса инструмента для работы с датами, требующего:

  • Этот класс наследуется отDate, имеет все свойства и объекты Date

  • Такие методы могут быть свободно расширены

Описание точки изображения заключается в том, что требования могут быть такими:

// 假设最终的类是 MyDate,有一个getTest拓展方法
let date = new MyDate();

// 调用Date的方法,输出GMT绝对毫秒数
console.log(date.getTime());
// 调用拓展的方法,随便输出什么,譬如helloworld!
console.log(date.getTest());

Итак, просто используйте классику в JSПаразитическая комбинацияЯ написал наследование, и тут, уже собираясь закончить работу на отлично, как только я ее запустил, появился следующий сценарий:

Но настроение такое:😳 囧

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

Итак, с помощью мощного поисковика я собрал информацию, и, наконец, сам ее обобщил, и тогда я придумал эту статью.

---------- Перед началом текста ----------

Перед тем, как текст начнется, вы можете сделать паузу, чтобы прочитать и попытаться увидеть, можно ли выполнить вышеуказанные требования без каких-либо сетевых данных? (просто возьми10分钟ограничить его)

контур

  • Поговорим о том, как быстро и быстро получить ответы

    • На stackoverflow уже есть ответы!

    • Если вы используете китайский для поиска.

  • Анализ ключа к проблеме

    • Что не так с классическим законом о наследовании?

    • Почему нельзя передать по наследству?

  • Как реализовать наследование?

    • Насильственный смешанный закон

    • ES5 Черная магия

    • ES6 Дафа

    • Написание ES6, затем упаковка Babel

  • Несколько тонких различий в наследовании

  • Разница между наследованием ES6 и наследованием ES5

  • Конструкторы и экземпляры объектов

  • [[Класс]] и внутренний слот

  • Как быстро определить, стоит ли наследовать?

  • последние слова

Поговорим о том, как быстро и быстро получить ответы

При столкновении с проблемой, которую невозможно решить, первая цель — быстро найти решение.

  • Сначала зайдите в stackoverflow, чтобы узнать, есть ли похожие вопросы. . .

Итак, искал с помощью поисковика, первый удовлетворил условиям, нажмите на него, чтобы увидеть описание

На stackoverflow уже есть ответы!

Сначала расскажу о результатах, и после некоторого просмотра я нашел решение, а затем оглянулся назад и был удивлен, потому что время для этого вопроса6 years, 7 months ago. То есть,2011Предлагается с начала года. . .

Я чувствую, что отстал от эпохи **>_

А так же нашел деталь, т.е.viewed:10,606 times, то есть на данный момент его прочитали всего более 10 000 раз.Учитывая количество сотрудников во фронтенд-отрасли, это соотношение на удивление низкое. Кажется, что мало кто сталкивается с этой проблемой.

Если вы используете китайский для поиска.

Нет ничего постыдного в поиске на китайском языке (моя интуиция — обращаться к Baidu, когда у меня возникают проблемы). Результат таков:

Что ж, похоже, результаты поиска по английским ключевым словам хорошие, и первое соответствует требованиям. Затем я снова попробовал китайский поиск.

Эффект неудовлетворительный, ищите первые несколько страниц, единственная похожая(segmentfaultтот, что выше), нажмите, чтобы увидеть

как сказать. . . Этот вопрос не получил особого внимания, количество браузеров невелико, а описание вышеприведенного вопроса немного отличается от ожидаемого, и на него все еще кто-то отвечает. Однако, хотя проблема была решена в определенной степени, ответчик обошел проблему невозможности наследования, которая немного не завершена. . .

Анализ ключа к проблеме

С помощью ответов на stackoverflow

Что не так с классическим законом о наследовании?

Давайте посмотрим на реализацию классического метода наследования, упомянутого в начале этой статьи, следующим образом:

/**
 * 经典的js寄生组合式继承
 */
function MyDate() {
    Date.apply(this, arguments);
    this.abc = 1;
}

function inherits(subClass, superClass) {
    function Inner() {}
    
    Inner.prototype = superClass.prototype;
    subClass.prototype = new Inner();
    subClass.prototype.constructor = subClass;
}

inherits(MyDate, Date);

MyDate.prototype.getTest = function() {
    return this.getTime();
};


let date = new MyDate();

console.log(date.getTest());

Именно этот код ⬆ также рекомендуется в JavaScript Elevation (Красная книга). . .

Давайте еще раз рассмотрим его ошибку:

Затем распечатайте его прототип, чтобы увидеть:

Как ни посмотри, потому что, согласно правилам возврата цепочки прототипов,DateВсе методы-прототипы доступны черезMyDateЦепочка прототипов объекта для резервного копирования. Присмотрелся и обнаружил, что не смог найти ключ не методом, аthis is not a Date object.

Ну, то есть, ключ:Поскольку вызываемый объект не является экземпляром Date, вызов не разрешен, даже если он наследуется самим прототипом

Почему нельзя передать по наследству?

Во-первых, посмотрите наMDNКак объяснялось выше, как упоминалось выше, объекты даты JavaScript могут передаваться только черезJavaScript DateСоздание экземпляра в качестве конструктора.

Затем посмотрите ответы на stackoverflow:

было упомянуто,v8В базовом коде движка есть ограничения, если вызывающий объект[[Class]]нетDate, вылетает ошибка.

В общем, совмещая эти два пункта, можно сделать один вывод:

Объект экземпляра для вызова метода Date должен быть создан с помощью Date, в противном случае не разрешается вызывать метод Date.

Как реализовать наследование?

Хоть причина и найдена, но проблему все равно надо решать, неужели нет выхода? Конечно нет, на самом деле есть еще много способов добиться этого.

Насильственный смешанный закон

Сначала поговорим о смешанном методе насилия, который выглядит так:

В конечном итоге: внутренне сгенерироватьDateобъекта, а затем в открытых методах этого класса поместите исходныйDateВсе методы в , проксируются один раз, и, строго говоря, это вообще не наследование (без обратного отслеживания цепочки прототипов).

ES5 Черная магия

Тогда давайте посмотрим, как это реализовано в ES5?

// 需要考虑polyfill情况
Object.setPrototypeOf = Object.setPrototypeOf ||
function(obj, proto) {
    obj.__proto__ = proto;

    return obj;
};

/**
 * 用了点技巧的继承,实际上返回的是Date对象
 */
function MyDate() {
    // bind属于Function.prototype,接收的参数是:object, param1, params2...
    var dateInst = new(Function.prototype.bind.apply(Date, [Date].concat(Array.prototype.slice.call(arguments))))();

    // 更改原型指向,否则无法调用MyDate原型上的方法
    // ES6方案中,这里就是[[prototype]]这个隐式原型对象,在没有标准以前就是__proto__
    Object.setPrototypeOf(dateInst, MyDate.prototype);

    dateInst.abc = 1;

    return dateInst;
}

// 原型重新指回Date,否则根本无法算是继承
Object.setPrototypeOf(MyDate.prototype, Date.prototype);

MyDate.prototype.getTest = function getTest() {
    return this.getTime();
};

let date = new MyDate();

// 正常输出,譬如1515638988725
console.log(date.getTest());

Выглядит ошеломленным с первого взгляда? Это не имеет значения, сначала посмотрите на следующий рисунок, чтобы понять: (отношения цепочки прототипов ясны с первого взгляда)

Как видите, используется очень хитрый подход:

  • Нормальная ситуация наследования выглядит следующим образом:

    • new MyDate()возвращаемый объект экземпляраdateКMyDateпостроен

    • Откат цепочки прототипов:date(MyDate对象)->date.__proto__->MyDate.prototype->MyDate.prototype.__proto__->Date.prototype

  • Наследование этой практики следующее:

    • new MyDate()возвращаемый объект экземпляраdateКDateпостроен

    • Откат цепочки прототипов:date(Date对象)->date.__proto__->MyDate.prototype->MyDate.prototype.__proto__->Date.prototype

Видно, что ключевыми моментами являются:

  • Конструктор возвращает реальныйDateобъект (поDateконструктор, поэтому в этих внутренних классах есть ключи[[Class]]флаг), поэтому он имеет вызовDateправа на методы на прототипах

  • Объект Date в конструкторе[[ptototype]](Для внешнего использования браузер может__proto__доступ) указывает наMyDate.prototype,ПотомMyDate.prototypeуказать сноваDate.prototype. Таким образом, конечный объект экземпляра может по-прежнему выполнять обычную цепочку прототипов с возвратом, возвращаясь ко всем методам прототипа исходной даты.

  • Таким образом, с помощью умной техники обмана достигается идеальное наследование Date. Но добавить,MDNупомянутый вышеСтарайтесь не изменять объект[[Prototype]], так как это может помешать оптимизации самого браузера.Если вы заботитесь о производительности, вам не следует изменять его [[Prototype]] в объекте.

ES6 Дафа

Конечно, помимо вышеописанной реализации ES5, ES6 также может напрямую наследовать (с собственной поддержкой наследованияDate), а еще проще:

class MyDate extends Date {
    constructor() {
        super();
        this.abc = 1;
    }
    getTest() {
        return this.getTime();
    }
}

let date = new MyDate();

// 正常输出,譬如1515638988725
console.log(date.getTest());

По сравнению с реализацией в ES5 это действительно просто, просто используйте синтаксис класса ES6 напрямую.

Также возможен нормальный вывод.

Уведомление:Обычная среда вывода здесь — работать напрямую с ES6 без упаковки babel, После упаковки он по существу преобразуется в ES5, поэтому эффект совершенно другой.

Написание ES6, а затем упаковка Babel

Хотя приведенный выше метод ES6 может напрямую наследовать Date, учитывая, что большинство производственных сред:ES6 + Babel

Использование ES6 + Babel напрямую, как это, вызовет проблемы

Если не верите, можете попробовать сами, код после упаковки Babel в ES5 примерно такой:

Затем, когда вы начнете использовать его с уверенностью, вы обнаружите:

Да, проблема повторилась, может в этот раз⊙?⊙

Поскольку в переведенном исходном коде ES5все еще черезMyDateстроить, а такжеMyDateне могут быть изменены в конструкцииDateВнутренний[[Class]]частные флаги, такие как Таким образом, построенный объект по-прежнему не позволяет вызыватьDateметод (при вызове он распознается базовым кодом движка как[[Class]]флаг не совпадает, вызов не разрешен, выдает ошибку)

Видно, что есть разница между внутренней реализацией наследования ES6 и реализацией, упакованной и скомпилированной Babel. (Хотя полифиллы Babel обычно реализуются в соответствии с определенными спецификациями, не будьте слишком суеверны).

Несколько тонких различий в наследовании

Хотя тремя упомянутыми выше методами можно добиться наследованияDateСтрого говоря, цель смешанного метода не считается наследованием, а просто альтернативной реализацией.

Таким образом, вся основная информация может быть выведена на печать, анализ нескольких унаследованных различий, сцена примерно такая:

Вы можете обратиться к: (Пожалуйста, войдите в режим отладки)proxyword.GitHub.io/share-inter-view…

Сверху донизу,1, 2, 3, 4Четыре реализации наследования: (исключая смешанный метод)

  • ES6 Класс Дафа

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

  • Хитрость в этой статье: Date создает экземпляр, а затем изменяет__proto__вид

  • ES6 Class Dafa, пакетная реализация Babel (не может быть вызвана нормально)

~~~~以下是MyDate们的prototype~~~~~~~~~
Date {constructor: ƒ, getTest: ƒ}
Date {constructor: ƒ, getTest: ƒ}
Date {getTest: ƒ, constructor: ƒ}
Date {constructor: ƒ, getTest: ƒ}

~~~~以下是new出的对象~~~~~~~~~
Sat Jan 13 2018 21:58:55 GMT+0800 (CST)
MyDate2 {abc: 1}
Sat Jan 13 2018 21:58:55 GMT+0800 (CST)
MyDate {abc: 1}

~~~~以下是new出的对象的Object.prototype.toString.call~~~~~~~~~
[object Date]
[object Object]
[object Date]
[object Object]

~~~~以下是MyDate们的__proto__~~~~~~~~~
ƒ Date() { [native code] }
ƒ () { [native code] }
ƒ () { [native code] }
ƒ Date() { [native code] }

~~~~以下是new出的对象的__proto__~~~~~~~~~
Date {constructor: ƒ, getTest: ƒ}
Date {constructor: ƒ, getTest: ƒ}
Date {getTest: ƒ, constructor: ƒ}
Date {constructor: ƒ, getTest: ƒ}

~~~~以下是对象的__proto__与MyDate们的prototype比较~~~~~~~~~
true
true
true
true

Как видно, основных отличий несколько:

  1. __proto__ MyDate указывает на разные

  2. Вывод Object.prototype.toString.call отличается

  3. Объект отличается по своей природе и может называться нормально1, 3обеDateпостроены, а остальныеMyDateпостроен

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

Но вот две переменные:Разница в том, что метод базового экземпляра конструкции отличается, а объектObject.prototype.toString.callвыход другой. (еще одинMyDate.__proto__можно исключить, т.к. возврат цепочки прототипов точно не при чем)

В случае, если его решение основано наObject.prototype.toString.callОткуда ты взялся? Не является ли этот вывод неверным?

Итак, согласно ES6,Symbol.toStringTag, используйте черную магию, изменяйте ее динамически и устраняйте помехи:

// 分别可以给date2,date3设置
Object.defineProperty(date2, Symbol.toStringTag, {
    get: function() {
        return "Date";
    }
});

Затем посмотрите на печать, она становится такой:

[object Date]
[object Date]
[object Date]
[object Object]

Видно, что второйMyDate2Сконструированный экземпляр, хотя и распечатанный,[object Date], но по-прежнему возникает ошибка при вызове метода Date

На данный момент мы можем более точно подтвердить:Вызов не разрешен, поскольку вызываемый объект не является экземпляром, созданным из Date

И мы можем видеть, что даже если оно изменено черной магиейObject.prototype.toString.call,Внутренний[[Class]]Флаги также неизменяемые. (Эта точка знаний, вероятно, заключается в том, что Object.prototype.toString.call может выводить внутренний [[Class]], но его нельзя изменить. Поскольку это не суть, я не буду повторяться здесь).

Разница между наследованием ES6 и наследованием ES5

Из вышеприведенного анализа видно одно: наследование записи классов в ES6 не представляет проблемы. Но перейти на запись ES5 не получится.

Итак, определенно есть разница между наследием Дафа ES6 и ES5, так в чем же разница? (в основном в сочетании с этой статьей, наследующей дату)

Разница: (сSubClass,SuperClass,instanceпример)

  • Суть наследования в ES5 такова: (разновидность классического метода наследования паразитной композиции)

    • подклассы (SubClass) создает объект экземпляра this

    • Затем в конструкторе дочернего класса поместите родительский класс (SuperClass) свойства добавляются кthisначальство,SuperClass.apply(this, arguments)

    • Прототип подкласса (SubClass.prototype) указывает на прототип родительского класса (SuperClass.prototype)

    • такinstanceявляется подклассом (SubClass) построен (так что нет родительского класса[[Class]]ключевой знак)

    • так,instanceимеютSubClassа такжеSuperClassВсе свойства экземпляра и могут быть прослежены через цепочку прототипов, получаютSubClassа такжеSuperClassметод на прототипе

  • Суть наследования в ES6 такова:

    • Сначала родительским классом (SuperClass) для создания объекта экземпляра this, поэтому сначала должен быть вызван родительский классsuper()Метод (подкласс не имеет собственного объекта this, он должен быть сначала создан родительским классом)

    • Затем в конструкторе подкласса модифицируйте this (для обработки), например, пусть он указывает на прототип подкласса (SubClass.prototype), этот шаг является критическим, иначе прототип подкласса не может быть найден (Примечание, фактическая практика обработки этого шага в конструкции подкласса выводится и выводится из окончательного эффекта)

    • Опять же, прототип подкласса (SubClass.prototype) указывает на прототип родительского класса (SuperClass.prototype)

    • такinstanceявляется родительским классом (SuperClass) сконструирован (поэтому у него есть родительский класс[[Class]]ключевой знак)

    • так,instanceимеютSubClassа такжеSuperClassВсе свойства экземпляра и могут быть прослежены через цепочку прототипов, получаютSubClassа такжеSuperClassметод на прототипе

В приведенном выше ⬆ перечислены некоторые важные сведения, а другие, такие как наследование статических методов, не повторяются. (Наследование статического метода по существу требует только измененияSubClass.__proto__прибытьSuperClassмогу)

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

Ты нашел это:Шаги в ES6 точно такие же, как метод наследования Date в этой статье, разница в том, что ES6 является нижним слоем языка и имеет свою базовую оптимизацию, а прямая модификация __proto__ в этой статье может легко повлиять на производительность

Преимущества создания этого в супер в ES6?

Поскольку ES6 позволяет нам наследовать встроенные классы, такие как Date, Array, Error и т. д. Если this создается первым и передается конструкторам системных встроенных классов, таких как Array, конструкторы этих встроенных классов не распознают this. Так что сейчас нужно встроить супер, чтобы у нас был ключ в супер[[Class]]флаг, чтобы разрешить вызов. (В противном случае, даже если они унаследованы, методы этих встроенных классов не могут быть вызваны)

Конструкторы и экземпляры объектов

Увидев это, я не знаю, является ли часто упоминаемое вышеКонструктор,экземпляр объектаЗапутался и запутался? Вот небольшое описание:

Чтобы это понять, нужно сначала узнатьnewЧто именно произошло с объектом? Сначала скажем образно:

Какая работа была проделана в новом MyClass()

function MyClass() {
    this.abc = 1;
}

MyClass.prototype.print = function() {
    console.log('this.abc:' + this.abc);
};

let instance = new MyClass();

Например, выше приведено стандартное создание экземпляра объекта, что произошло?

Шаги кратко описаны следующим образом: (Обратитесь к MDN, а некоторые описания нижележащих слоев опущены — например, флаги [[Class]] и т. д.)

  1. Внутри конструктора создайте новый объект, который наследуется отMyClass.prototype,let instance = Object.create(MyClass.prototype);

  2. вызвать конструктор с указанными аргументамиMyClassи привяжите его к вновь созданному объекту,MyClass.call(instance);, который имеет все свойства экземпляра после выполнения

  3. Если конструктор возвращает «объект», этот объект заменяет весьnewрезультаты. Если конструктор не возвращает объект, результатом new является объект, созданный на шаге 1. (В обычных обстоятельствах конструктор не возвращает никакого значения, но если пользователь хочет перезаписать возвращаемое значение, он может вернуть обычный объект для перезаписи. Конечно, возвращенный массив также будет перезаписан, потому что массив также объект.)

В сочетании с приведенным выше описанием его, вероятно, можно восстановить до следующего кода: (простое восстановление, без учета различных других логик)

let instance = Object.create(MyClass.prototype);
let innerConstructReturn = MyClass.call(instance);
let innerConstructReturnIsObj = typeof innerConstructReturn === 'object' || typeof innerConstructReturn === 'function';

return innerConstructReturnIsObj ? innerConstructReturn : instance;
  • Примечание ⚠️:

    • Построение обычной функции можно просто рассматривать как описанные выше шаги.

    • На самом деле, для некоторых встроенных классов (таких как Date и т.д.) это не так просто, и есть какая-то своя скрытая логика, например[[Class]]Некоторые важные частные атрибуты, такие как идентификационный бит.

      • Например, как вы можете видеть в MDN, вызов Date с помощью обычной функции (то есть без оператора new) вернет строку, а не объект даты, который будет недопустимым при симуляции таким образом.

Думаете, это выглядит сложно? Вы можете увидеть схему ниже:

Тогда оглянитесь назад.

Что такое конструктор?

как в приведенном вышеMyClassэто конструктор, который внутренне создаетinstanceобъект

Что такое экземпляр объекта?

instanceявляется экземпляром объекта, который передается черезnewот?

Связь между экземпляром и конструкцией

Иногда просто подумать, что конструктор xxx является экземпляром xxx. который:

let instance = new MyClass();

В этот момент мы можем рассмотретьinstanceдаMyClassэкземпляр , потому что его конструктор это он

Должен ли экземпляр создаваться соответствующим конструктором?

неуверенный, давайте использовать черную магию ES5 в качестве примера

function MyDate() {
    // bind属于Function.prototype,接收的参数是:object, param1, params2...
    var dateInst = new(Function.prototype.bind.apply(Date, [Date].concat(Array.prototype.slice.call(arguments))))();

    // 更改原型指向,否则无法调用MyDate原型上的方法
    // ES6方案中,这里就是[[prototype]]这个隐式原型对象,在没有标准以前就是__proto__
    Object.setPrototypeOf(dateInst, MyDate.prototype);

    dateInst.abc = 1;

    return dateInst;
}

Мы видим, чтоinstanceПрототип, который в конечном итоге указывает на этоMyDate.prototype,а такжеMyDate.prototypeКонструкторMyDate, Следовательно, можно считать, чтоinstanceдаMyDateпример.

но,Фактически,instanceэтоDateпостроен

мы можем продолжать использоватьES6серединаnew.targetпроверять.

Внимание ⚠️

оnew.target,MDNОпределение в:new.target возвращает ссылку на конструктор или функцию.

Ну, то есть return - это конструктор.

Мы можем протестировать печать в соответствующей конструкции:

class MyDate extends Date {
    constructor() {
        super();
        this.abc = 1;
        console.log('~~~new.target.name:MyDate~~~~');
        console.log(new.target.name);
    }
}

// new操作时的打印结果是:
// ~~~new.target.name:MyDate~~~~
// MyDate

Затем, как вы можете видеть в приведенном выше примере, даже с наследованием классов ES6,MyDateпечать во время строительстваnew.targetтакже показатьMyDate, но на самом деле этоDateпостроить (сDateКлючевой[[Class]]Флаг, потому что, если это не конструкция Date (например, без флага), методы Date не могут быть вызваны).

Итак, на самом делеиспользоватьnew.targetНевозможно судить, из какой структуры построен объект-экземпляр (здесь имеется в виду оценка лежащего в основе реального объекта).[[Class]]построение исходника логотипа)

Вы также можете увидеть определение на MDN,new.targetВозврат - это прямой конструктор (тот, на который воздействует new), поэтому, пожалуйста, не используйте прямой конструктор сфактическое строительствоиспортить

Вернемся к выводу:Экземплярный объект не обязательно создается конструктором на его прототипе, возможно, внутри конструктора присутствует паразитная логика, и для скрытого построения следующей функции используется другая функция., Конечно, в простых случаях не будет ошибкой сказать, что объект-экземпляр конструируется соответствующим конструктором (однако, когда дело доходит до такого рода Date-подобного анализа, нам все равно нужно разбираться).

[[Класс]] и внутренний слот

Эта часть является дополнительной.

Концепция была упомянута в предыдущей статье:Дата внутренняя[[Class]]логотип

На самом деле, строго говоря, его нельзя назвать в таком общем виде (понятие используется только в предыдущей статье для уменьшения сложности и облегчения понимания), его можно разделить на следующие две части:

  • В ES5 каждый встроенный объект определяет значение внутреннего свойства [[Class]], а значение внутреннего свойства [[Class]] используется для внутреннего различения типа объекта.

    • Object.prototype.toStringДоступ это [[Класс]]

    • В дополнение к прохождениюObject.prototype.toString, что не дает программам доступа к этому значению.

    • И вывод Object.prototype.toString не может быть изменен

  • В ES6 предыдущий [[Class]] больше не используется, а заменяется сериейinternal slot

    • Внутренние слоты соответствуют внутренним состояниям, связанным с объектами и используемыми различными алгоритмами спецификации ECMAScript, они не имеют свойств объекта и не могут быть унаследованы.

    • В зависимости от конкретной спецификации внутреннего слота это состояние может состоять из значения любого типа языка ECMAScript или значения определенного типа спецификации ECMAScript.

    • пройти черезObject.prototype.toString, вы все равно можете вывести значение внутреннего слота

    • Простое понимание (упрощенное понимание), процесс Object.prototype.toString таков: если это базовый тип данных (за исключением нескольких типов, отличных от Object), вернуть исходный слот, Если это тип объекта (включая встроенные объекты и объекты, написанные вами), вызовитеSymbol.toStringTag

    • Symbol.toStringTagРеализация метода по умолчанию заключается в возврате внутреннего слота объекта, этот методможно переписать

Эти две точки разные и их нужно различать (но можно просто понять, что внутри встроенного объекта есть специальный идентификатор для различения соответствующего типа - если он не соответствует типу, то вызываться не будет) .

Встроенные объекты JS:

"Arguments", "Array", "Boolean", "Date", "Error", "Function", "JSON", "Math", "Number", "Object", "RegExp", "String"

Некоторые из новых ES6, не упомянутые здесь: (например, объект Promise может выводить[object Promise])

И упоминалось выше:

Object.defineProperty(date, Symbol.toStringTag, {
    get: function() {
        return "Date";
    }
});

Его роль — переписать Symbol.toStringTag и перехватить дату (хоть это и встроенный объект, но все же принадлежит Object)Object.prototype.toStringвывод, пусть этот объект выводит свой модифицированный вывод[object Date].

Однако, когда это только вывод, он становится датой, на практике внутреннейinternal slotзначение не изменилось, поэтому до сих пор не считается свиданием

Как быстро определить, стоит ли наследовать?

На самом деле при оценке наследства умений не так уж и много, только ключевой момент:[[prototype]](__ptoto__) указывающие отношения

Например:

console.log(instance instanceof SubClass);
console.log(instance instanceof SuperClass);

По сути:

  • SubClass.prototypeэто появляется вinstanceв цепочке прототипов

  • SuperClass.prototypeэто появляется вinstanceв цепочке прототипов

Затем некоторые элементы управления ФИГ., приведенные здесь, можно сразу увидеть взаимосвязь. Иногда нет необходимости слишком усложнять.

последние слова

Так как введение наследования уже много в Интернете, эта статья не повторяет описание, а вызвана и расширена проблемой наследования даты. (Ключом является цепочка прототипов)

Я не знаю, видите ли вы это, все ли судьи уже поняли наследование в JS?

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

приложение

блог

Первый выпуск2018.01.15в моем личном блоге

Дай Личунь.com/2018/01/15/…

использованная литература