предисловие
Ты с нетерпением ждешь мира, надеюсь, у тебя нетbug. Всем привет! Я Лин Дуань!
Тринити-босса я смотрел несколько месяцев назад."(Рекомендуемая подборка) Вопрос души нативного JS, со сколькими вы справитесь? 》Серия, в то время, я использовал время поездки на автобусе, чтобы почистить. Расскажу, что я чувствовал в то время. Я действительно не знаю некоторых пунктов знаний, и я чувствую себя хулиганом. Действительно есть ощущение, что меня истязает душа. Самое страшное, что в то время я еще не понимал, что моя база настолько плоха, я просто запоминал какие-то разрозненные знания, которые я мог забыть через несколько дней.
В какой-то момент, возможно, под влиянием определенной статьи, мое мышление внезапно сильно изменилось. Как бы мне описать это чувство... Как будто я думал, что знаю все это, но я не знал, что не знал, а потом я с нетерпением ждал лучшего завтра. Я чувствую себя немного как лягушка в колодце. Я не узнаю, пока действительно не узнаю себя沉淀важность слова. когда я принесу'为什么会这样?'、'还可以怎样?'、'如果这样会怎样?'Чтобы просмотреть некоторые из предыдущих знаний, я обнаружил, что мне действительно есть что добавить...
Первоначально эта статья была некоторыми заметками, которые я сделал, когда недавно читал «Вопросы души Sanyuan-JS», но я обнаружил, что чем больше я записывал, тем больше я писал... Это привело к серии статей, которые я написал, например,Серия преобразований типов JS,Серия наследования JS,thisи т.п. Вот несколько дополнительных пояснений к некоторым вопросам, упомянутым Саньюанем, чтобы они были более подходящими для чтения младшими и средними партнерами, а также для закрепления их собственного обучения на данном этапе.
The Nuggets не ломались после написания в течение года или двух.Lv4, кажется, я собираюсь увеличить масштаб. Стой здесьflag,РостLv4поствзрывная женская одежда"呆妹",Пучок"她"Поместите это в соседнюю статью, чтобы устроить волну. Это так скромно...
(в соответствии с первоначальным авторомБог ТроицаНаписано с благодарностью19177Не поймите меня неправильно, я тоже его маленькая поклонница...)
Система знаний Лин
Все статьи включены в gitHub"niubility-кодирование-js"середина.
Первое дополнение: основы типов JS
1. Почему '1'.toString() можно вызвать, а 1.toString() нельзя?
Мы знаем, что если мы используем в коде:
'1'.toString()
// 或者是
true.toString()
можно назвать нормально, это потому, чтоtoStringэтоObject.prototypeметод выше, любой доступ кObjectЭлементы прототипа могут вызывать его.
И вот, для'1'.toString()Это эквивалентно выполнению слоя преобразования, превращению его в"对象", так что вы можете позвонитьtoString()метод.
Вот и все:
var s = new Object('1');
s.toString();
s = null;
- Создайте
Objectэкземпляр, будетsсталString{"1"}объект - передача
Object.prototypeметод экземпляра наtoString() - Уничтожить экземпляр сразу после использования
Этой части тернарного анализа было достаточно много, в основном хочу добавить1.toString()Почему бы нет.
Когда мы пытаемся использовать в коде1.toString(), и обнаружили, что редактор сообщил об ошибке, которая не позволяет нам это сделать.
Странная идея сначала возникла из-за того, что мы все упустили из виду одну вещь, а именно.Это тоже часть чисел 😂.
Например1.2,1.3. поэтому, когда вы хотите использовать1.toString()когда,JavaScriptИнтерпретатор будет рассматривать его как часть числа, что эквивалентно(1.)toString, очевидно, это плохой кусок кода.
В этом случае, если я верну код.Это нормально, поэтому я попытался:
console.log(1.1.toString())
Получается, что печатает нормально:
"1.1"
Это еще раз доказывает1.toString()буду.приписывается1принадлежать, а неtoString().
Конечно, это также возможно, если вы используете переменную для переноса числа:
var num = 1;
console.log(num.toString()) // "1"
// 或者
console.log((1).toString()) // "1"
2. Почему я могу использовать новый номер, но не новый символ?
var num = new Number(1) // Number{1}
var str = new String('1') // String{'1'}
var bol = new Boolean(true) // Boolean{true}
var symbol = new Symbol(1) // TypeError
используйте, как указано вышеnew Number、new StringОсновные типы данных, созданные и т. д., называются:Создайте явный объект-оболочку вокруг примитивного типа данных..
С точки зрения непрофессионала: использоватьnewдля создания классов-оболочек для примитивных типов.
И этот подходES6Он больше не поддерживается, так какnew Symbol(1)Видно, что ошибка сообщается, и теперь она используется безnewПуть:var symbol = Symbol(1), поэтому он неполный как конструктор.
Однако в силу исторических причинnew NumberЕго все еще можно использовать таким образом, но не рекомендуется.
Если вы действительно хотите создать объект-оболочку Symbol (Symbol wrapper object),вы можете использоватьObject()функция:
var sym = Symbol(1)
console.log(typeof sym) // "symbol"
var symObj = Object(sym)
console.log(typeof symObj) // "object"
Второе дополнение: определение типа JS
1. Может ли instanceof определить базовый тип данных?
Что ты имеешь в виду 🤔️?
Как обычно,instanceofОн используется для определения того, можно ли найти объект-прототип конструктора в цепочке прототипов объекта.
Давайте взглянем на краткое введение:
a instanceof B
实例对象a instanceof 构造函数B
обнаружитьaцепь прототипов(__proto__)есть лиB.prototype, иначе возвратtrue, иначе возвратfalse.
Таким образом, его можно использовать для оценки основного типа данных?
То есть я определяюvar num = 1,Я могу использоватьinstanceofзаключается в том, чтобы определитьnumberпеременная типа?
Если вы попытаетесь написать:
var num = 1;
console.log(num instanceof Number) // false
обнаружил, что результатfalse.
В этот момент вы можете использоватьSymbol.hasInstanceреализовать пользовательскийinstanceofповедение.
Прежде всего, какую функцию мы пытаемся достичь?
a instanceof B
осталосьaявляется переменной, а правая частьBявляется конструктором иclassСущность также является конструктором.
Таким образом, в этом требовании мы можем определитьMyNumberКласс инкапсулирует в нем слой и предоставляет статический метод для определения типа данных:
class MyNumber {
static [Symbol.hasInstance](instance) {
return typeof instance === 'number'
}
}
var num = 1;
console.log(num instanceof MyNumber) // true
- в классе
MyNumberопределяет именованныйSymbol.hasInstanceстатический метод - Этот метод получает экземпляр объекта
instance - Возвращаемое значение
typeofопределитьnumberТипы
Вот что сложнее понять, так это то, чтоSymbol.hasInstanceНу, когда я впервые столкнулся с этим, я не знал, что это такое 😂. нашел волнуMDNВведение в него:
用于判断某对象是否为某构造器的实例。
Затем я попытался написать несколько случаев и обнаружил, что это не должно быть так сложно, вы можете просто понять, когда мы используемinstanceofКогда вы можете настроить правильный конструктор (класс), егоinstanceofДостаточно метода проверки.
Как и в случае выше 👆, я вMyNumberпереопределенный статический методSymbol.hasInstance, и его метод проверки становитсяtype instance === 'number'.
Так почему же это статический метод (то есть добавление перед методомstatic), чтением«[Почему не Sanlian] Вопросы о наследовании JS, которые проще, чем наследование семейного бизнеса — инкапсуляция (тест коровьего ножа)»Мы знаем, что статические методы монтируются вMyNumberМетоды на этом классе, поэтому мы можем даже поместить следующий контент в другую формулировку:
console.log(num instanceof MyNumber) // true
// 换成:
console.log(MyNumber[Symbol.hasInstance](num)) // true
Видите ли, на самом деле это просто имя метода, и поскольку этот метод является статическим, вMyNumber, поэтому мы можем использоватьMyNumber[Symbol.hasInstance]Позвонил так.
Подумай, если нетstaticА как насчет этого ключевого слова?
нетstatic, метод, определенный в классе, эквивалентен подключению к объекту-прототипу класса, поэтому, если мы хотим его использовать, нужно напрямую использоватьMyNumber.prototypeпозвонить, а другой использоватьnew MyNumber()Создайте экземпляр для вызова:
class MyNumber {
[Symbol.hasInstance](instance) { // 没有 static
return typeof instance === 'number'
}
}
var num = 1
console.log(num instanceof new MyNumber()) // true
console.log(num instanceof MyNumber.prototype) // true
// 转化为:
console.log(MyNumber.prototype[Symbol.hasInstance](num)) // true
console.log(new MyNumber()[Symbol.hasInstance](num)) // true
Итак, оглядываясь назад:
class MyNumber {
static [Symbol.hasInstance](instance) {
return typeof instance === 'number'
}
}
var num = 1;
console.log(num instanceof MyNumber) // true
Это легче понять?
Итак, если я хочу, чтобы вы реализовалиinstanceofОпределите, является ли это классом массиваMyArray, как это написать?
Думал 🤔...
Хм... ответ:
class MyArray {
static [Symbol.hasInstance](instance) {
return Array.isArray(instance);
}
}
console.log([] instanceof MyArray); // true
2. Маршрут поиска instanceof?
Мы сказали вышеinstanceofОн используется для определения того, можно ли найти объект-прототип конструктора в цепочке прототипов объекта.
И он будет искать слой за слоем по цепочке прототипов, пока не достигнет конца цепочки прототипов.
Так на что же похож этот процесс? Давайте посмотрим на пример 🌰:
function Parent () {
this.name = 'parent'
}
function Child () {
this.sex = 'boy'
}
Child.prototype = new Parent()
var child1 = new Child()
console.log(child1 instanceof Child)
console.log(child1 instanceof Parent)
console.log(child1 instanceof Object)
Результат:
true
true
true
Фактически здесь используется наследование по цепочке прототипов.Chindунаследовано отParent, а объекты-прототипы трех конструкторов существуют вchild1в цепочке прототипов.
То есть слеваchild1Он будет продолжать искать свою цепочку прототипов, чтобы увидеть, есть ли объект-прототип для конструктора справа.
Напримерchild1 instanceof ChildПорядок поиска:
child1 -> child1.__proto__ -> Child.prototype
child1 instanceof ParentПорядок поиска:
child1 -> child1.__proto__ -> Child.prototype
-> Child.prototype.__proto__ -> Parent.prototype
Все еще не понимаете?
Не беда, у меня еще есть большая хитрость:
Я добавил три поисковых маршрута к карте 👆 наследования цепочки прототипов выше.
Отмечен ⭕️1、2、3соответственно представляютChild、Parent、Objectобъект-прототип.
Хорошо, это четкая картина. увидимсяinstanceofТакого рода вещи, просто следуйте маршруту поиска на моей карте, чтобы найти его 😁 ~
3. Что делает isPropertypeOf()?
Так как было сказаноinstanceofТогда вы должны упомянуть это.isPrototypeOfЭтот метод.
это принадлежитObject.prototypeметод выше, вы можете использоватьObject.prototypeНапечатай в консоли и посмотри.
isPrototypeOf()использование иinstanceofНапротив.
Используется для определения указанного объектаobject1существует в другом объектеobject2В цепочке прототипов он возвращаетtrue, иначе возвратfalse.
Например, в вопросе выше 👆 мы изменим содержимое для печати:
function Parent () {
this.name = 'parent'
}
function Child () {
this.sex = 'boy'
}
Child.prototype = new Parent()
var child1 = new Child()
console.log(Child.prototype.isPrototypeOf(child1))
console.log(Parent.prototype.isPrototypeOf(child1))
console.log(Object.prototype.isPrototypeOf(child1))
Выход здесь по-прежнему триtrue:
true
true
true
Единственный способ судитьЭкземпляр наследования прототипа цепочки поиска разумной картыЭто изображение можно найти в обратном порядке.
больше оinstanceOfСодержание можно кликнуть здесь👇:
💦 [Почему бы не три раза подряд] Ответьте на эти 48 вопросов и полностью поймите наследование JS.
4. В чем разница между Object.is() и ===?
- для
+0а также-0разные суждения - для
NaNа такжеNaNразные суждения
console.log(+0 === -0) // true
console.log(Object.is(+0, -0)) // false
console.log(NaN === NaN) // false
console.log(Object.is(NaN, NaN)) // true
На самом деле, его не нужно запоминать, нужно просто понять,Object.is()существует===Эти особые ошибки исправляются на основе .
То есть:+0а также-0не должно быть равным, это из1 / +0дляInfinity,1 / -0для-Infinityможно увидеть выше.
(но+0а также0нет никакой разницы)
а такжеNaNВсе представления не являются числами, поэтому они должны быть равны.
следовательноObject.is()Внутренности на самом деле делают некоторые исправления:
function is (x, y) {
if (x === y) {
return x !== 0 || 1 / x === 1 / y;
} else {
return x !== x && y !== y;
}
}
Как мы можем понять это здесь 🤔️?
- когда
x === yиспользуется для обработки+0, -0, 0особые обстоятельства.
В этом приговоре первыйx和yЭто все+0, -0, 0один из трех (потому что мы знаем, что любой из трех и0Конгруэнтное сравнение, результат будетtrue,такx !== 0было быfalse), и согласно||Принцип короткого замыкания , прежний терминfalse, то от последнего пункта зависит конечный результат.
такx !== 0эквивалентноx, yдля+0, -0, 0ситуация подтолкнула к1 / x === 1 / y, используйте его результат, чтобы определить окончательный результат.
(В оригинальном тексте Саньюаня это суждение написано такreturn x !== 0 || y !== 0 || 1 /x === 1 / y, на один больше, чем у меня здесь|| y !== 0. Друзья в комментарияхmatteokjhПодскажите, думаю можно поставить|| y !== 0Решение опущено, а другойMDNв темеpolyfillТакого шага нет, я подумал: ведь я могу войти вx === y, то еслиx !== 0Если установлено, тоy !== 0Он должен быть установлен, и кажется, что его можно опустить. Кроме того, я выполнил пропущенный код, и результат выполнения совпадает с результатом выполнения, когда он не пропущен, поэтому я считаю, что это возможно. )
И мы знаем1 / +0дляInfinity,1 / -0для-Infinity, так что еслиx, yдля+0或者-0неравны, когдаObject.is(+0, -0)的结果为false.
- когда
x !== yиспользуется для обработкиNaN, NaNособые обстоятельства.
потому что мы знаемNaN !== NaN, то еслиxа такжеyЕсли ни один из них не равен себе, это означает, что обаNaN, а если обаNaNесли,Object.is(NaN, NaN)Результатtrue.
Третье дополнение: магия метода toString()
1. Где существует toString()?
До этого я много читал оtoString()Большая часть информации о нем описывает его использование, но где он существует на самом деле?
Возможно, более распространено высказывание о том, что оно существует вObjectв прототипе объекта , то естьObject.prototype, то для основных типов данныхNumber、String、Boolean、 Symbol、BigIntШерстяная ткань? У них самих есть этот метод? Или у них это есть на их объекте-прототипе?
Чтобы докопаться до сути, я распечаталNumberа такжеNumber.prototype:
console.log(Number)
console.log(Number.prototype)
Затем я нашел несколько вещей:
-
NumberПросто конструктор, исходный код будет отображаться при печати -
Number.prototypeдействительно естьtoString() -
Number.prototype.__proto__то естьObject.prototypeТакже естьtoString()
Затем я попытался сноваString、Boolean、SymbolРезультаты оказались такими же, как и выше.
Нетрудно понять, см."💦[Почему не три подряд] После ответа на эти 48 вопросов я досконально разобрался в наследовании JS (1.7w слова содержат сочувствие - вернитесь к оригиналу)"Все наши друзья знают, что цепочка прототипов всех объектов будет указывать на конецObject.prototype, это все "по наследству"Objectэкземпляр объекта, поэтому можно использоватьtoString()метод, но для других встроенных объектов, для достижения более подходящих функциональных требований, этот метод будет переписан, так что вы можете видетьNumber.prototypeБудет и этот метод.
Итак, мы можем сначала сделать первый вывод:
- Кроме
null、undefinedДругие типы данных (базовые типы данных + ссылочные типы данных), отличные от объектов-прототипов их конструкторов, имеютtoString()метод - Конструктор примитивного типа данных на объекте-прототипе
toString()будет охватыватьObjectна объекте-прототипеtoString()метод
(Конечно, когда вы оглянетесь назад, вы увидите, что это утверждение не очень точное, но большую часть времени нас волнует только то, кто может его использовать, а не то, где оно существует)
2. Кто может вызывать toString()?
На этот вопрос, собственно, дан ответ выше👆, все объекты, кромеnull、undefinedможет вызываться с любым значением, кромеtoString()метод, обычно его возвращаемый результат иStringТакой же.
На самом деле, здесь мы, скорее всего, запутаемсяStringа такжеtoString.
Эти два свойства всегда использовались случайным образом для преобразования определенного типа в строку.
-
StringаналогичныйFunctionТакой объект можно использовать либо как объект, применяя к нему статические методы, либо как конструктор для созданияStringобъект - а также
toStringэто помимоnull、undefinedметодов, отличных от типов данных, обычно его возвращаемый результат совпадает сStringТакой же.
3. Как использовать toString() для определения конкретного типа данных?
Одно из наиболее распространенных применений, которое вы можете увидеть, заключается в следующем:
Object.prototype.toString.call({ name: 'obj' }) // '[object Object]'
Сначала получите твердые знания.Object.prototype.toStringЭтот метод будет основан на объекте[[class]]Внутреннее имущество, возвращенное"[object " 和 class 和 "]"Строка из трех частей.
Что ты имеешь в виду?[[class]]Что такое внутреннее свойство 🤔️?
Не думайте слишком много об этом здесь, просто поймите это буквально, подумайте об этом,classЗначение английского слова ->类.
Ну, я просто думаю, что это представляет класс вещей.
подобно
- Массив — это класс, чей
[[class]]даArray - String — это класс, его
[[class]]даString -
argumentsэто класс, его[[class]]даArguments
Кроме того, о[[class]]Типов очень много, и вам не нужно запоминать их все, вам просто нужно знать некоторые часто используемые, основные и простые для понимания.
так что вернемся кObject.prototype.toString.call()Этот способ вызова, теперь вы можете понять, что он делает,Это может помочь нам точно определить определенный тип данных, то есть различать, является ли это массивом, числом, функцией илиNaN. 😊
Также учитывая, что его возвращаемый результат"[object Object]"такая строка и предыдущая"[object ]"Эти восемь строк являются фиксированными (включая"t"пробел после него), поэтому мы можем инкапсулировать метод, чтобы получить только"Object"А как быть с такой строкой?
Очень просто, код выше:
function getClass (obj) {
let typeString = Object.prototype.toString.call(obj); // "[object Object]"
return typeString.slice(8, -1);
}
Как видите, я назвал эту функцию какgetClass, что также повторяет его первоначальную функцию, заключающуюся в получении объекта[[class]]внутренние свойства.
Кроме того, после получения"[object Object]"После строки a.slice(8, -1)Функция перехвата строки удаляет первые восемь символов"[object ]"и последнее"]".
Теперь давайте рассмотрим некоторые распространенные типы данных:
function getClass(obj) {
let typeString = Object.prototype.toString.call(obj); // "[object Array]"
return typeString.slice(8, -1);
}
console.log(getClass(new Date)) // Date
console.log(getClass(new Map)) // Map
console.log(getClass(new Set)) // Set
console.log(getClass(new String)) // String
console.log(getClass(new Number)) // Number
console.log(getClass(true)) // Boolean
console.log(getClass(NaN)) // Number
console.log(getClass(null)) // Null
console.log(getClass(undefined)) // Undefined
console.log(getClass(Symbol(42))) // Symbol
console.log(getClass({})) // Object
console.log(getClass([])) // Array
console.log(getClass(function() {})) // Function
console.log(getClass(document.getElementsByTagName('p'))) // HTMLCollection
console.log(getClass(arguments)) // Arguments
"霖呆呆,这么多,这是人干的事吗?"
"心平气和,记住一些常用的就行了..."
"啪!"
4. В чем разница между toString.call() и typeof?
Хороший дроп👌, только что пройдя исследование, мы узнали, что,toString.callЭтот способ состоит в том, чтобы получить более конкретный тип данных переменной.
Эй~ Когда дело доходит до типов данных, у нас их нет.typeof? это иtoString.call()Какая разница?
Сначала позвольте мне рассмотретьtypeofПравила его отображения:
- Для примитивных типов (т.
number、stringэто), кромеnullоба отображают правильный тип -
nullИз-за исторической версии его ошибочно приняли за"object" - Для ссылочных типов (т. е.
object、arrayэто), за исключением функций, будет отображаться как"object" - Функция будет отображаться как
function
Так что да,typeofНедостаток очевиден, у меня сейчас есть объект и массив, или объект даты, я хочу его внимательно различать, использоватьtypeofЭто определенно невозможно, потому что все, что они получают, это"object".
Поэтому, используя наши упакованныеgetClass()Очевидно, хороший выбор.
5. Что происходит при вызове toString() с разными типами данных?
вызов различных типов данныхtoString()В чем будет разница?
Здесь я в основном делю его на две части:
- вызов базового типа данных
- вызов ссылочного типа
5.1 Базовые типы данных вызывают toString
Для базовых типов данных вызвать его очень просто, вы просто думаете о замене его исходного значения строкой:
console.log('1'.toString()) // '1'
console.log(1.1.toString()) // '1.1'
console.log(true.toString()) // 'true'
console.log(Symbol(1).toString()) // 'Symbol(1)'
console.log(10n.toString()) // '10'
Итак, для примитивных типов данных:
- Когда вызывается примитивный тип данных, его примитивное значение заменяется строкой
5.2 Вызов toString для ссылочного типа
Сложная часть - это вызов ссылочного типаtoString(), и мы знаем, что ссылочные типы основаны на[[class]]Разница разделена на множество категорий, таких какObject,Array,Dateи т.п.
Потом разные классыtoString()Тоже отличается 🤔️?
да разные версииtoStringВ основном делятся на:
- массив
toStringСпособ состоит в том, чтобы преобразовать каждый элемент в строку, а затем использовать","соединять - Обычные предметы (например,
{name: 'obj'}this) в строку станет"[object Object]" -
函数(class)、正则будет преобразован в строку исходного кода -
日期Строка даты, которая будет преобразована в местный часовой пояс. - Вызов объекта-оболочки для примитивного значения
toStringвернет строку с исходным значением - имеют
Symbol.toStringTagОбъект встроенного свойства станет соответствующей меткой при вызове"[object Map]"
(Symbol.toStringTagсвойства будут заданы в следующем вопросе)
Например 🌰:
console.log([].toString()) // ""
console.log([1, 2].toString()) // "1,2"
console.log({}.toString()) // "[object Object]"
console.log({name: 'obj'}.toString()) // "[object Object]"
console.log(class A {}.toString()) // "class A {}"
console.log(function () {}.toString()) // "function () {}"
console.log(/(\[|\])/g.toString()) // "/(\[|\])/g1"
console.log(new Date().toString()) // "Fri Mar 27 2020 12:33:16 GMT+0800 (中国标准时间)"
console.log(new Object(true).toString()) // "true"
console.log(new Object(1).toString()) // "1"
console.log(new Object(BigInt(10)).toString()) // "10"
console.log(new Map().toString()) // "[object Map]"
console.log(new Set().toString()) // "[object Set]"
6. Знаете ли вы о Symbol.toStringTag?
"Символ.toStringTag"Выше описано так:
ДолженSymbol.toStringTagОбщеизвестной нотацией является строковое свойство, используемое при создании строкового описания объекта по умолчанию. это состоит изObject.prototype.toString()Доступ к методу осуществляется изнутри.
Неважно, если вы этого не понимаете, вы можете понять это так, это фактически определяет типы данных, которые мы только что упомянули.[[class]]Что это за внутреннее свойство.
Как и числа, мы получили[[class]]даNumber, то я могу понять его как класс чисел и егоSymbol.toStringTagвозвратNumber.
Просто мы использовалиNumber、String、Booleanне вSymbol.toStringTagЭто встроенное свойство, которое используется, когда мыtoString.call()Он идентифицируется и возвращается только при вызове.
И только что мы только что увидели в пятом вопросеnew Map(), давайте распечатаем и посмотрим.
console.log(new Map())
можно увидетьSymbol.toStringTagон существует вMap.prototypeна, то есть этоMap、Setвстроенное свойство, поэтому, когда мы вызываем напрямуюtoString(), он вернется"[object Map]".
Ну разве мы можем это понять?
- нет
Symbol.toStringTagТип встроенного свойства называетсяtoString()эквивалентноString(obj)Этот вызов преобразуется в соответствующую строку - имеют
Symbol.toStringTagТип встроенного свойства называетсяtoString()Когда возвращается соответствующая метка (т. е."[object Map]"такая строчка)
Наш обычно используется сSymbol.toStringTagОбъекты со встроенными свойствами:
console.log(new Map().toString()) // "[object Map]"
console.log(new Set().toString()) // "[object Set]"
console.log(Promise.resolve().toString()) // "[object Promise]"
Его основная функция состоит в том, чтобыSymbol.hasInstanceНапример, может позволить нам настроить этикетку.
(Symbol.hasInsanceРоль состоит в том, чтобы настроитьinstanceofВозвращаемое значение)
Что такое пользовательский тег 🤔️?
То есть, если мы сейчас создадим класс и воспользуемсяtoString.call()Вызов его объекта экземпляра будет иметь следующие результаты:
class Super {}
console.log(Object.prototype.toString.call(new Super())) // "[object Object]"
хорошо понятно, потому что в результатеnew Super()является объектом, поэтому то, что будет напечатано, будет"[object Object]".
Но теперь естьSymbol.toStringTagПосле этого мы можем изменить последний"Object".
Например, если я перепишу это:
class Super {
get [Symbol.toStringTag] () {
return 'Validator'
}
}
console.log(Object.prototype.toString.call(new Super())) // "[object Validator]"
ЭтоSymbol.toStringTagСамое замечательное в этом то, что он позволяет нам настраивать метки.
Но есть одно замечание,Symbol.toStringTagпереписаноnew Super()метка для этого экземпляра объекта, а не переопределениеSuperМетка этого класса, то есть здесь есть разница:
class Super {
get [Symbol.toStringTag] () {
return 'Validator'
}
}
console.log(Object.prototype.toString.call(Super)) // "[object Function]"
console.log(Object.prototype.toString.call(new Super())) // "[object Validator]"
потому чтоSuperЭто все еще сама функция, толькоSuperСгенерированный объект экземпляра будет использовать нашу пользовательскую метку.
В заключениеSymbol.toStringTag:
- Это встроенное свойство определенного типа, например
Map、Set、Promise - Основная функция состоит в том, чтобы позволить нам настраивать метки, изменять
Object.prototype.toString.call()вернуть результат
больше оtoStringа такжеSymbol.toStringTagСодержание можно кликнуть здесь👇:
[Хорошо] Полностью понять прошлое и настоящее преобразования типов данных из 206 console.log() (1)
Четвертое дополнение: преобразование типов JS
Поскольку преобразование типов — это немного головная боль, я также написал серию статей об этом, в основном охватывающих вопросы, которые могут быть заданы в интервью на портале:
[Хорошо] Полностью понять прошлое и настоящее преобразования типов данных из 206 console.log() (1)
[Точно] Полностью понять прошлое и настоящее преобразования типов данных из 206 console.log() (2)
(следующая часть написана хорошо и никому не неудобно 😣)
1. Расскажите об основном использовании valueOf()
- Вызов базового типа данных, возврат исходного значения вызывающей стороны
- Другие вызовы ссылочного типа для объектов, не относящихся к дате
valueOf()По умолчанию возвращается сам - в то время как объект даты вернет
1970 年 1 月 1 日以来的毫秒数(похожий на1585370128307).
Пример 🌰:
console.log('1'.valueOf()) // '1'
console.log(1.1.valueOf()) // 1.1
console.log([].valueOf()) // []
console.log({}.valueOf()) // {}
console.log(['1'].valueOf()) // ['1']
console.log(function () {}.valueOf()) // ƒ () {}
console.log(/(\[|\])/g.valueOf()) // /(\[|\])/g
console.log(new Date().valueOf()) // 1585370128307
2. Каков конкретный процесс преобразования ToPrimitive?
когда мыобъект преобразуется в примитивный типилисделать == сравнение, встроенныйToPrimitiveфункция.
Например:
console.log(String({})) // 对象转字符串,结果为 "[object Object]"
console.log(Number([1, 2])) // 对象转数字,结果为 NaN
console.log([] == ![]) // true
Истоки вышеуказанных результатов былиToPrimitiveфункция.
Давайте сначала посмотрим на синтаксис его функции:
ToPrimitive(input, PreferredType?)
параметр:
- Параметр один:
input, представляющее входное значение для обработки - Второй параметр:
PerferredType, тип ожидаемого преобразования, вы можете видеть, что после грамматики стоит вопросительный знак, указывающий, что это не требуется. Он имеет только два необязательных значения,Numberа такжеString.
А обработка входящих параметров у него сложнее, давайте посмотрим на блок-схему:
Согласно блок-схеме мы получили следующую информацию:
- Когда PreferredType не передается, если ввод является типом даты, это эквивалентно передаче String, в противном случае это эквивалентно передаче Number.
- Если это ToPrimitive(obj, Number), шаги обработки следующие:
- Если obj является примитивным типом, вернитесь напрямую
- В противном случае вызывается метод valueOf, и если он возвращает примитивное значение, JavaScript возвращает его.
- В противном случае вызывается метод toString, и если он возвращает примитивное значение, JavaScript возвращает его.
- В противном случае JavaScript выдает исключение TypeError.
- Если это ToPrimitive(obj, String), шаги обработки следующие:
- Если obj является примитивным типом, вернитесь напрямую
- В противном случае вызывается метод toString, и если он возвращает примитивное значение, JavaScript возвращает его.
- В противном случае вызывается метод valueOf, и если он возвращает примитивное значение, JavaScript возвращает его.
- В противном случае JavaScript выдает исключение TypeError.
(краткий источник«Преобразование типа Сай Ю глубокой головной боли JavaScript (часть 1)»)
Картинка выше 👆 на самом деле выглядит очень сложной, внимательные друзья могут обнаружить, что там, где красная рамка на картинке обрамлена, толькоtoString()а такжеvalueOf()Порядок выполнения методов разный.
Если PreferredType имеет значение String, выполнить сначалаtoString()метод
Если PreferredType имеет значение Number, выполнить сначалаvalueOf()метод
(Лин ДианДай предлагает вам сначала нарисовать эту блок-схему на бумаге для заметок)
2.1 Объект в строку
Вот несколько примеров для закрепления:
console.log(String({})) // "[object Object]"
Для этого простого преобразования мы можем заменить его наtoPrimitiveВзгляните на псевдокод:
toPrimitive({}, 'string')
Хорошо👌, давайте прямо сейчас рассмотрим правила конвертации:
-
inputда{}, является ссылочным типом,PerferredTypeдляstring - так позвони
toString()метод, то есть{}.toString() -
{}.toString()Результат"[object Object]", это строка, которая является базовым типом данных, а затем возвращается, и это конец.
вау~
Все ли понятно? Вроде ничего сложного 😁.
Да, при использованииString()метод,JSПорядок выполнения внутри движка действительно такой, но одна вещь отличается от только что упомянутых шагов, а именно, когда результат наконец возвращается, последний базовый тип данных фактически преобразуется в строку для возврата.
То есть третий шаг из вышеперечисленного 👆 должен быть разделен на два этапа:
-
{}.toString()Результат"[object Object]", это строка, базовый тип данных - положи это
"[object Object]"String снова выполняет преобразование строки и возвращается. (потому что"[object Object]"Это уже строка, поэтому верните ее как есть, я не вижу здесь никакой разницы)
Преобразование конечного результата в строку и возврат к этому шагу на самом деле очень просты для понимания. Подумай об этом, я звонюStringМетод состоит в том, чтобы получить строку, если вы вернете мнеnumber、nullАх, или что-то в этом роде, разве это не то, что сделал старый король по соседству~
2.2 Объект к номеру
Только что мы сказали, что объект строкиtoPrimitive(object, 'string')Случай,
Тогда объект для нумерацииtoPrimitive(object, 'number').
Разница в том, что при повороте номера сначала будет звонитьvalueOf()звонить послеtoString().
Пример 🌰:
console.log(Number({}))
console.log(Number([]))
console.log(Number([0]))
console.log(Number([1, 2]))
console.log(Number(new Date()))
дляNumber({}):
- переданный объект
{}, так что звонитеvalueOf()метод, метод в названии7.1Как уже упоминалось в , он возвращает себя, за исключением других вызовов ссылочного типа объекта даты, поэтому объект по-прежнему возвращается здесь.{} -
valueOf()Возвращаемое значение по-прежнему является объектом, поэтому продолжайте вызыватьtoString()метод, в то время как{}передачаtoString()результат - строка"[object Object]", это примитивный тип данных - Получите базовый тип данных, пора возвращать, но перед этим его надо преобразовать в число перед возвратом, затем
"[object Object]"Преобразовать в числаNaN, так что результатNaN
дляNumber([]):
- Массив передается в
[], так что звонитеvalueOf()метод, который возвращает себя[] -
[]Продолжай звонитьtoString()метод, а преобразование пустого массива в строку для"" - Наконец, поместите пустую строку
""преобразовать в число0вернуть
дляNumber([0]):
- потому что
[0]преобразуется в строку"0", и, наконец, преобразовать в число0вернуть
дляNumber([1, 2]):
- Массив передается в
[1, 2], так что звонитеvalueOf()Метод возвращает сам массив[1,2] - Так что продолжайте звонить
toString()метод, который теперь преобразуется в"1,2"нить -
"1,2"Строка, наконец, преобразуется в число какNaN, так что результатNaN
дляNumber(new Date()):
- Входящий объект типа date
new Date(), так что звонитеvalueOf(), в тему7.2Как было сказано в вызове типа датыvalueOf()вернет количество миллисекунд - Количество миллисекунд - это цифровой тип, то есть базовый тип данных, затем возврат напрямую (на самом деле есть шаг для преобразования в цифровой тип), поэтому результат
1585413652137
результат:
console.log(Number({})) // NaN
console.log(Number([])) // 0
console.log(Number([0])) // 0
console.log(Number([1, 2])) // NaN
console.log(Number(new Date())) // 1585413652137
3. Почему массив преобразуется в строку, связанную знаком ","?
Все мы знаем, что когда массив преобразуется в строку, каждый элемент в нем будет преобразован в строку, а затем","Склеить обратно.
Так почему же","А как насчет шага сшивания? ЭтоtoString()также будет вызываться при вызовеjoin()метод?
Чтобы проверить свою идею 💡, я провел эксперимент и переписал массивjoin()метод:
var arr = [1, 2]
arr['join'] = function () {
let target = [...this]
return target.join('~')
}
console.log(String(arr))
переписанныйjoinв функции,thisПредставляет массив, который вызываетсяarr.
Затем измените возвращаемое значение на"~"По сплайсингу получился такой ответ:
"1~2"
То есть вString(arr)процесс, он действительно неявно называетсяjoinметод.
Но когда мы переписываемtoString()После этого вы не будете заботиться об этом переписыванииjoinсейчас:
var arr = [1, 2]
arr['toString'] = function () {
let target = [...this]
return target.join('*')
}
arr['join'] = function () {
let target = [...this]
return target.join('~')
}
console.log(String(arr)) // "1*2"
Как можно заметитьtoString()приоритет все же выше, чемjoin()Высоко.
Теперь мы можем сделать еще один вывод:
Если объект представляет собой массив, когда мы не переписываем егоtoString()метод, при преобразовании в строковый тип реализация по умолчанию должна вызыватьjoin()Возвращаемое значение метода равноtoString()Возвращаемое значение.
4. Знакомы ли вы с Symbol.toPrimitive? Для чего это?
Когда мы преобразуем объект в исходное значение, он будет неявно вызывать внутреннийToPrimitiveметод, преобразуйте его так же, как указано выше.
а такжеSymbol.toPrimitiveАтрибуты помогают нам переопределитьtoPrimitive, который изменяет результат преобразования.
Давайте посмотрим на его резюме:
- Если вы переопределяете объект или конструктор в
toString、valueOf、Symbol.toPrimitiveметод,Symbol.toPrimitiveприоритет является наивысшим - если
Symbol.toPrimitiveЕсли значение, возвращаемое функцией, не является базовым типом данных (то есть исходным значением), будет сообщено об ошибке. -
Symbol.toPrimitiveПолучает строковый параметрhint, который представляет ожидаемый тип примитивного значения для преобразования, в общей сложности'number'、'string'、'default'три варианта - использовать
String()Когда звонили,hintдля'string';использоватьNumber()час,hintдля'number' -
hintЗначение параметра определено с начала вызова
Давайте посмотрим на пример 🌰:
var b = {
toString () {
console.log('toString')
return '1'
},
valueOf () {
console.log('valueOf')
return [1, 2]
},
[Symbol.toPrimitive] (hint) {
console.log('symbol')
if (hint === 'string') {
console.log('string')
return '1'
}
if (hint === 'number') {
console.log('number')
return 1
}
if (hint === 'default') {
console.log('default')
return 'default'
}
}
}
console.log(String(b))
console.log(Number(b))
Этот вопрос был переписанtoString、valueOf、Symbol.toPrimitiveТри атрибута, которые мы уже знаем из приведенного выше 👆 названия, пока естьSymbol.toPrimitiveТеперь первые два свойства игнорируются, поэтому мы не заботимся о них.
И дляSymbol.toPrimitive, ставлю триhintСитуация написана, если по моему предположению при звонкеString(b)должен распечататьstringда, позвониNumber(b)распечаткаnumber, результат такой, как я и ожидал:
'string'
'1'
'number'
1
5. Преобразование типа == в сравнении?
На самом деле, на практике мы, скорее всего, будем тестироваться с помощью==чтобы сравнить, равны ли две переменные разных типов.
в то время как конгруэнтный===Ситуация относительно проста, и в целом не очень хороша в тестировании, потому что условие конгруэнтности таково: если типы равны и значения равны, считается конгруэнтным, и преобразование типов не задействовано.
но==Ситуация относительно сложная.Позвольте мне сначала задать вам несколько знакомых вопросов:
console.log([] == ![]) // true
console.log({} == true) // false
console.log({} == "[object Object]") // true
как? Часто ли вы видите эти вопросы 😁, давайте рассмотрим их по порядку.
Прежде всего, нам все еще нужно прояснить несколько понятий.Это жесткое и быстрое правило.Если мы его не прочитаем, мы не сможем продолжать.
когда используешь==При сравнении будут действовать следующие правила преобразования (правила суждения):
- Если типы обеих сторон одинаковы, значения равны, например
2 == 3для уверенностиfalseда - Обе стороны сравнения являются примитивными типами данных:
- Если одна сторона
null、undefined, другая сторона должна бытьnull或者undefinedтолько дляtrue, то есть,null == undefinedдляtrueилиnull == nullдляtrue,потому чтоundefinedполученный изnull - один из них является
String, если да, поставьStringПеревести вNumberсравните еще раз - один из них является
Boolean, если да, тоBooleanПеревести вNumberсравните еще раз
- Одна сторона сравнения имеет ссылочный тип:
- следуйте ссылочному типу
ToNumberпреобразованную форму для сравнения (на самом деле этоhintдаdefault, то есть,toPrimitive(obj, 'default'),ноdefaultправила преобразования иnumberаналогичный) - Обе стороны являются ссылочными типами, затем оцените, указывают ли они на один и тот же объект
В некоторых статьях будет сказано:
Если один из них является объектом, а другой — строкой, числом или символом, объект будет преобразован в строку, а затем сравнен
(взято из"Shen Sanyuan - (рекомендуемая подборка) Вопрос души нативного JS, сколько вы справитесь? (начальство)"середина3. == 和 ===有什么区别?)
Это нормально так думать, потому что подумайте об этомtoPrimitive(obj, 'number')процесс:
- Если входное значение является ссылочным типом данных, сначала вызовите
valueOf()метод - если
valueOf()Если возвращаемое значение метода является базовым типом данных, он вернется напрямую, если нет, продолжит вызовtoString() - Если звонить
toString()Если возвращаемое значение является базовым типом данных, оно будет возвращено, в противном случае будет сообщено об ошибке.
Как видите, первое — выполнитьvalueOf(), но ссылочные типыvalueOf()Метод, за исключением типа даты, возвращает себя в других случаях, то есть после выполненияvalueOf()После этого он по-прежнему является ссылочным типом и является самим собой. Тогда мы можемvalueOf()Этот шаг опущен и считается выполненным непосредственноtoString()Так что начните иметь проблемы намного быстрее.
6. Преобразование типов нескольких унарных операторов?
Преобразование типов для нескольких распространенных операторов:
-
-、*、/、%Эти четыре преобразуют обе стороны символа в числа для работы. -
+Поскольку это не только числовой оператор, но и конкатенатор строк, он делится на два случая:
- Если оба конца являются числами, выполняется цифровое вычисление (унарный положительный знак
+bЭтот случай эквивалентен преобразованию в число) - Один конец представляет собой строку, а другой конец также преобразуется в строку для подключения
объект+Преобразование типа номера:
- объект в процессе
+Когда числовая строка объединяется,toPrimitiveпараметрыhintдаdefault,ноdefaultпорядок исполнения иnumberТо же самое — сначала определить, есть лиvalueOf, если есть, выполнитьvalueOf, а потом судитьvalueofПосле возвращаемого значения, если это ссылочный тип, продолжить выполнениеtoString. (похожий на4.5а также4.6) - дата в процессе
+Когда числовая строка объединяется, она вызывается первойtoString()метод. (похожий на4.7) - Унарный знак плюс — самый быстрый способ преобразовать другие объекты в числа., также является наиболее рекомендуемой практикой, потому чтоОн не выполняет никаких избыточных операций с числовыми значениями..
7. Сколькими способами можно сделать условие if(a == 1 && a == 2 && a == 3) истинным?
Полагаю, этот вопрос не пропустят все, кроме переписыванияvalueOf()Какие еще решения у вас есть?
Решение 1. ПерепишитеvalueOf()
Это решение состоит в том, чтобы воспользоваться: когда объект находится в процессе==При сравнении он фактически будет выполняться первымvalueOf(),еслиvalueOf()Если возвращаемое значение является базовым типом данных, оно будет возвращено, в противном случае оно будет продолжать вызываться, если это ссылочный тип.toString()возвращайся, а потом судиtoString()Возвращаемое значение , если возвращаемое значение является базовым типом данных, оно будет возвращено, в противном случае будет сообщено об ошибке.
СейчасvalueOf()Каждый раз, когда он возвращает числовой тип, он будет возвращен напрямую.
// 1
var a = {
value: 0,
valueOf () {
return ++this.value
}
}
if (a == 1 && a == 2 && a == 3) {
console.log('成立')
}
Решение 2. ПерепишитеvalueOf()а такжеtoString()
var a = {
value: 0,
valueOf () {
return {}
},
toString () {
return ++this.value
}
}
if (a == 1 && a == 2 && a == 3) {
console.log('成立')
}
Принцип является принципом решения один, но он используется, когдаvalueOf()Когда возвращаемое значение является ссылочным типом, он будет продолжать вызыватьtoString().
Здесь даже не нужно переписыватьvalueOf(), потому что объекты, отличные от объекта даты, вызываютvalueOf()всегда возвращает себя.
Тем не менее, вы также можете сделать это:
var a = {
value: 0,
toString () {
return ++this.value
}
}
if (a == 1 && a == 2 && a == 3) {
console.log('成立')
}
Решение 3. ПерепишитеSymbol.toPrimitive
Подумайте, можно ли его еще использоватьSymbol.toPrimitiveрешить это?
Комбинированные вопросы3.10Мы знаем, что когда объект==При сравнении,Symbol.toPrimitiveполученные параметрыhintда"defalut", то нам просто нужно переписать его так:
var a = {
value: 0,
[Symbol.toPrimitive] (hint) {
if (hint === 'default') {
return ++this.value
}
}
}
if (a == 1 && a == 2 && a == 3) {
console.log('成立')
}
Такой результат тоже возможен.
Решение 4. Определите класс и перепишите valueOf()
Конечно, вы также можете использоватьclassнаписать:
class A {
constructor () {
this.value = 0
}
valueOf () {
return ++this.value
}
}
var a = new A()
if (a == 1 && a == 2 && a == 3) {
console.log('成立')
}
Решение 5. Преобразование массива в строку вызовет неявный вызовjoin()
Какой ? Есть ли другое решение? И я не понимаю вопроса Решения 5.
Вернемся и посмотрим на вопрос4.3, где упоминается, что при преобразовании массива в строку вызовtoString()Результат на самом деле вызываетjoinрезультат.
Какое это имеет отношение к этому вопросу? Посмотрим ответ:
let a = [1, 2, 3]
a['join'] = function () {
return this.shift()
}
if (a == 1 && a == 2 && a == 3) {
console.log('成立')
}
Потому что мы знаем, если объект представляет собой массив, когда мы не переписываем его.toString()метод, при преобразовании в строковый тип реализация по умолчанию должна вызыватьjoin()Возвращаемое значение метода равноtoString()Возвращаемое значение.
Итак, мы переписываемaизjoinметод, и это переопределение делает две вещи:
- массив
aвоплощать в жизньa.shift()метод, мы знаем, что это влияет на исходный массивa, удалить первый элемент - Вернуть первый только что удаленный элемент
Итак, когда мы выполняемa == 1На этом этапе, поскольку неявный вызовa['join']метод, поэтому две вещи, упомянутые выше, будут выполнены, последняяa == 2а такжеa == 3То же самое справедливо.
Решение 6. Определите класс для наследования Array и перепишите функцию join()
Для пятого решения мы также можем использоватьclassреализовать
class A extends Array {
join = this.shift
}
var a = new A(1, 2, 3)
if (a == 1 && a == 2 && a == 3) {
console.log('成立')
}
Такой способ написания классный 🆒, но может быть непонятен с первого взгляда.
- первый
AЭтот класс проходитextendsунаследовано отArray, так что поnew Aсоздает массив - потом
Aпереписанныйjoinметод,join = this.shiftэквивалентноjoin = function () { return this.shift() } - Таким образом, когда каждый вызов
a == xxxКогда придет время, мы неявно вызовем наш пользовательскийjoinметод, выполните те же операции, что и в решении 5.
8. Сделать если (a === 1 && a === 2 && a === 3) условие верно?
Этот вопрос немного похож на вопрос выше, но условия для суждения здесь совпадают.
Мы знаем условие соответствия:
- Типы левой и правой сторон должны быть равны, а если типы не равны, возвратить напрямую
false, это и==разные,==Происходит неявное преобразование типов - Значение повторной оценки не равно
И для решения вышеуказанной 👆 проблемы мы использовали его==Произойдет неявное преобразование типов, очевидно, если его использовать для решения этой задачи, добиться невозможно.
думать, когда мыa === xxxПри судействе на самом деле звонитaЭти данные только, то есть мы должны что-то сделать перед вызовом этих данных для достижения нашей цели.
Я не знаю, если эти слова заставят вас задуматься о чем-нибудь 🤔️? Может быть, вы с Дуаном подумаете об этом.VueЗнаменитый захват данных 😁.
подумай оVue2.xне используетсяObject.defineProperty()переопределение методаdataВсе свойства в , то мы также можем использовать его здесь для захватаa,ИсправлятьaПеременнаяgetАтрибуты.
var value = 1;
Object.defineProperty(window, "a", {
get () {
return this.value++;
}
})
if (a === 1 && a === 2 && a === 3) {
console.log('成立')
}
Вот что происходит на самом деле:
- использовать
Object.defineProperty()метод перехвата глобальной переменнойwindowсвойства наa - когда каждый звонок
aбудетvalueавтоинкремент и вернуть автоинкрементное значение
(На самом деле, я думал об использованииProxyДля захвата данных проксиwindow, используй этоnew Proxy()справиться с этим, но дляwindowОбъект, кажется, не имеет никакого эффекта...)
Решение второе
Что я должен делать
var arr = [1, 2, 3];
Object.defineProperty(window, "a", {
get () {
return this.arr.shift()
}
})
if (a === 1 && a === 2 && a === 3) {
console.log('成立')
}
бингоshift()Яд... Конечно, это тоже достижимо.
Решение третье
И естьEnoYaoШоу, в котором я появился в большом человеке:
Оригинальная ссылка:nuggets.capable/post/684490…
var aᅠ = 1;
var a = 2;
var ᅠa = 3;
if (aᅠ == 1 && a == 2 && ᅠa == 3) {
console.log("成立");
}
стыдно сказать...aНе могу набрать скрытые символы до и после 😂...
9. Что происходит с консольным вводом {}+[]?
Этот интересный вопрос отLINGLONGодин из"[js мало знаний] []+ {} =? /{} +[] =? (Неявное преобразование плюса)"Там выглядит.
(PS: Берите волну Linglong, статьи этой дамы все очень хорошо написаны, но популярность невысокая, поддержать ее может каждый 😁)
Хорошо👌, давайте посмотрим, что это за заголовок:
В консоли (например, в консоли браузера) введите:
{}+[]
Какой будет результат 🤔️?
Эй ~ этот вопрос должен быть очень простым, в соответствии с предыдущим принципом преобразования типов,+Обе стороны преобразуются в строки,{}Перевести в"[object Object]",[]Перевести в"", результат склеивания:
console.log({}+[]) // "[object Object]"
Но обратите внимание на заголовок здесь, это для вывода на консоль.
В этот момент я поместил этот код в вывод консоли и обнаружил, что ответ не такой, как ожидалось:
{}+[]
0
то есть{}игнорируется, выполняется напрямую+[], результат0.
Зная причину, у меня слезы потекли, оказывается, это то же самое, что и упомянутое ранее1.toString()Вроде как, еще и потому, чтоJSПо причинам синтаксического анализа кода в консоли или терминалеJSдумаю брекеты{}В начале находится пустой блок кода, поэтому он будет проигнорирован, если в нем нет содержимого.
Так что просто выполните+[], преобразовать его в0.
Если мы изменим порядок, такой проблемы не будет:
(Источник изображения:nuggets.capable/post/684490…)
Доказать это, мы можем поставить{}Как пустой объект вызывать какие-то методы объекта, посмотрим, какой будет эффект:
(консоль или терминал)
{}.toString()
настоящее время{}по-прежнему считается блоком кода, а не объектом, поэтому будет сообщено об ошибке:
Uncaught SyntaxError: Unexpected token '.'
Решение заключается в использовании()Разверните его:
({}).toString
Но на практике эта штука мало используется, единственное, о чем я могу думать, это проект (например, тот, который я используюvue), затем определитеprops, если вы хотите определить значение по умолчанию одного из свойств как пустой объект, вы будете использовать:
props: {
target: {
type: Object,
default: () => ({})
}
}
Пятое дополнение: восьмое наследование JS
дляJSНаследование Я тоже писал серию "инкапсуляция | наследование | полиморфизм", вот портал:
В статье четко указаны конкретные примеры и темы, здесь я лишь перечислю преимущества и недостатки каждого наследования и псевдокода.
1. Наследование цепочки прототипов
伪代码:
Child.prototype = new Parent()
思维导图:
преимущество:
- Наследовать шаблон родительского класса и унаследовать объект-прототип родительского класса
недостаток:
- Если вы хотите добавить свойства и методы к прототипу подкласса, вы должны поместить его в
Child.prototype = new Parent()После такого заявления - Множественное наследование невозможно (поскольку был указан объект-прототип)
- Все свойства объекта-прототипа являются общими, поэтому, если вы случайно измените свойства ссылочного типа в объекте-прототипе, это повлияет на все объекты-экземпляры, созданные подклассами.
- При создании подкласса нельзя передавать параметры конструктору родительского класса
2. Конструктивное наследование
伪代码:
function Child () {
Parent.call(this, ...arguments)
}
преимущество:
- Решена проблема, связанная с тем, что экземпляры подкласса совместно используют объект ссылки родительского класса в наследовании цепочки прототипов, реализуют множественное наследование и могут передавать параметры родительскому классу при создании экземпляра подкласса.
недостаток:
- Конструктивное наследованиеТолькоНаследовать свойства экземпляра и методы родительского класса,не можемНаследовать свойства и методы прототипа родительского класса
- Экземпляр не является экземпляром родительского класса, а является экземпляром дочернего класса.
- Не удалось добиться повторного использования функции, каждый подкласс имеет копию функции экземпляра родительского класса, что влияет на производительность.
3. Композиционное наследование
伪代码:
// 构造继承
function Child () {
Parent.call(this, ...arguments)
}
// 原型链继承
Child.prototype = new Parent()
// 修正constructor
Child.prototype.constructor = Child
思维导图:
Метод реализации:
- использоватьНаследование цепочки прототиповЧтобы подклассы могли наследовать свойства и методы в прототипе родительского класса
- использоватьКонструктивное наследованиеЧтобы гарантировать, что подкласс может наследовать свойства экземпляра и методы суперкласса
преимущество:
- Может наследовать свойства и методы экземпляра родительского класса, а также свойства и методы прототипа родительского класса.
- Решите проблему совместного использования ссылочных свойств в наследовании цепочки прототипов.
- Можно передавать параметры, можно использовать повторно
недостаток:
- При использовании составного наследования дважды вызывается конструктор родительского класса.
- Создаются два экземпляра.Свойства и методы экземпляра подкласса переопределяют свойства и методы прототипа подкласса (экземпляр родительского класса), поэтому добавляется ненужная память.
4. Наследование паразитарного состава
伪代码:
// 构造继承
function Child () {
Parent.call(this, ...arguments)
}
// 原型式继承
Child.prototype = Object.create(Parent.prototype)
// 修正constructor
Child.prototype.constructor = Child
思维导图:
Наследование паразитарного составасчитатьES6Более совершенный способ наследования.
он избегаетнаследование композицииНедостаток двойного вызова конструктора родительского класса и двойной инициализации свойств экземпляра.
Таким образом, он имеет преимущества всех вышеперечисленных методов наследования:
- Конструктор родительского класса вызывается только один раз, и создается только один атрибут родительского класса.
- Подклассы могут использовать свойства и методы в цепочке прототипов родительского класса.
- можно использовать нормально
instanceOfа такжеisPrototypeOfметод
5. Прототипное наследование
伪代码:
var child = Object.create(parent)
Метод реализации:
Принцип этого метода заключается в создании конструктора, прототип которого указывает на объект, затем вызове оператора new для создания экземпляра и возврате экземпляра, который по сути является поверхностной копией.
существуетES5можно использовать сразу послеObject.create()метод для реализации, а до этого вручную можно реализовать только один (например, заголовок6.2).
преимущество:
- В случае отсутствия необходимости в создании конструктора реализуется наследование по цепочке прототипов, а объем кода уменьшается на часть.
недостаток:
- Некоторые операции со ссылочными данными вызовут проблемы, и два экземпляра будут совместно использовать класс ссылочных данных, который наследует экземпляр.
- Тщательное определение методов, чтобы не определять методы, которые также наследуют одно и то же имя методов от прототипа объекта.
- Невозможно использовать параметры непосредственно в родительском конструкторе
6. Паразитическое наследование
伪代码:
function createAnother (original) {
var clone = Object.create(original);; // 通过调用 Object.create() 函数创建一个新对象
clone.fn = function () {}; // 以某种方式来增强对象
return clone; // 返回这个对象
}
Метод реализации:
- существуетпрототипное наследованиеНа основе слоя инкапсуляции улучшить объект, а затем вернуть объект.
преимущество:
- В случае отсутствия необходимости в создании конструктора реализуется наследование по цепочке прототипов, а объем кода уменьшается на часть.
недостаток:
- Некоторые операции со ссылочными данными вызовут проблемы, и два экземпляра будут совместно использовать класс ссылочных данных, который наследует экземпляр.
- Тщательное определение методов, чтобы не определять методы, которые также наследуют одно и то же имя методов от прототипа объекта.
- Невозможно использовать параметры непосредственно в родительском конструкторе
7. Смешанное наследование
伪代码:
function Child () {
Parent.call(this)
OtherParent.call(this)
}
Child.prototype = Object.create(Parent.prototype)
Object.assign(Child.prototype, OtherParent.prototype)
Child.prototype.constructor = Child
思维导图:
8. Наследование в классе
伪代码:
class Child extends Parent {
constructor (...args) {
super(...args)
}
}
Наследование в ES6:
- в основном зависят от
extendsключевое слово для реализации наследования, и эффект наследования подобенНаследование паразитарного состава - использовал
extendsНаследование реализации не обязательноconstructorа такжеsuper, потому что в противном случае они генерируются и вызываются по умолчанию -
extendsСледующая цель не обязательноclass, пока естьprototypeфункция собственности
Разница между наследованием ES5 и наследованием ES6:
- существует
ES5наследование в (напр.Конструктивное наследование, паразитарное композиционное наследование), по сути, сначала создавая объект экземпляра подклассаthis, а затем добавьте свойства и методы родительского класса вthisна (используяParent.call(this)). - пока в
ES6Это не такСначала создайте экземпляр объекта родительского классаthis(то есть использоватьsuper()), а затем используйте конструктор подкласса для измененияthis.
Справочная статья
Знания бесценны и поддерживают оригинальность.
Справочная статья:
- Понимание JavaScript ES6 Symbol.hasInstance. 》
- "(Рекомендуемая подборка) Вопрос души нативного JS, со сколькими вы справитесь? (начальство)"
послесловие
Ты с нетерпением ждешь мира, надеюсь, у тебя нетbug. Эта статья представлена здесь.
Поскольку слишком много было сказано в начале, я не буду говорить это здесь 🙊.
подобноЛин ДуанТакже парень надеется обратить внимание на паблик Лина.LinDaiDaiИли отсканируйте QR-код ниже👇👇👇.
Время от времени я буду обновлять некоторый внешний контент знаний и свои собственные оригинальные статьи🎉
Ваша поддержка - главная мотивация для меня продолжать творить 😊.
связанное предложение:
"Самый подробный учебник по bpmn.js во всей сети"
«[Предлагаемое изменение] Прочитав это, вы все еще не понимаете Вавилон, я пришлю вам маску»