Автор оригинала: FreeCodeCamp Community Member.
Алексей Самошкин
Знать ваши двигатели
[Править 2/5/2018] : Этот пост сейчас Доступно на русском языке Отказ Хлопья к Серж Булавик за его усилия.
Тип принуждения Процесс преобразования значения из одного типа на другое (например, строку к номеру, объект к логическому регису и т. Д.). Любой тип, будь то примитив или объект, является действительным предметом для принуждения к типу. Чтобы вспомнить, примитивы: номер, строка, логический, нулевой, неопределенный + символ + (добавляется в ES6).
В качестве примера типового принуждения на практике посмотрите на Таблица сравнения JavaScript , что показывает, как свободное равенство == Оператор ведет себя для разных А и B Типы. Эта матрица выглядит страшно из-за неявного принуждения к тому, что == Оператор делает, и вряд ли можно вспомнить все эти комбинации. И вам не нужно это делать – просто выучите основные принципы принуждения к типу.
Эта статья идет углублена тем, как в JavaScript работает принуждение в JavaScript, и будет вооружать вас основным знанием, поэтому вы можете чувствовать себя уверенно, объяснив, что подчиняется следующим выражениям. К концу статьи я покажу ответы и объясни их.
true + false
12 / "6"
"number" + 15 + 3
15 + 3 + "number"
[1] > null
"foo" + + "bar"
'true' == true
false == 'false'
null == ''
!!"false" == !!"true"
['x'] == 'x'
[] + null + 1
[1,2,3] == [1,2,3]
{}+[]+{}+[1]
!+[]+[]+![]
new Date(0) - 0
new Date(0) + 0Да, этот список полон довольно глупых вещей, которые вы можете сделать в качестве разработчика. В 90% случаев использования лучше избегать неявного типа принуждения. Рассмотрим этот список в качестве учебного упражнения для проверки ваших знаний о том, как работает принуждение к приложению. Если вам скучно, вы можете найти больше примеров на wtfjs.com Отказ
Кстати, иногда вы можете столкнуться с такими вопросами в интервью для позиции разработчика JavaScript. Итак, продолжайте читать?
Неявность против явного принуждения
Тип принуждения может быть явным и неявным.
Когда разработчик выражает намерение преобразовать между типами, написав соответствующий код, как Номер (значение) это называется Явный тип принуждения (или тип литья).
Поскольку JavaScript – это слабо напечатанный язык, значения также могут быть автоматически преобразованы между различными типами, и он называется неявный тип принуждения Отказ Обычно это происходит, когда вы применяете операторы до значений различных типов, таких как 1 , 2/'5' , null + новая дата () или это может быть вызвано окружающим контекстом, как с Если (значение) {...} , где ценность принужден к логию.
Один оператор, который не вызывает неявный тип принуждения, является === , который называется строгим оператором равенства. Свободный оператор равенства == С другой стороны, как нужно как сравнение, так и тип принуждения.
Принуждение неявного типа – это двойной край Sword: это отличный источник разочарования и дефектов, но и полезный механизм, который позволяет нам писать меньше кода, не теряя читаемость.
Три типа конверсии
Первое правило, чтобы знать, есть лишь три типа преобразования в JavaScript:
- нанизывать
- до логии
- на номер
Во-вторых, конверсионная логика для примитивов и объектов работает по-разному, но как примитивы, так и объекты могут быть преобразованы только этими тремя способами.
Давайте сначала начнем с примитивов.
Конверсия строки
Чтобы явно преобразовать значения в строку Применить Строка () функция. Неявное принуждение запускается двоичным + Оператор, когда любой операнд является строкой:
String(123) // explicit 123 + '' // implicit
Все примитивные значения преобразуются в строки, естественно, как вы ожидаете:
String(123) // '123' String(-12.3) // '-12.3' String(null) // 'null' String(undefined) // 'undefined' String(true) // 'true' String(false) // 'false'
Конвертация символов немного сложно, потому что он может быть конвертирован только явно, но не касащенным неявно. Читать дальше на Символ Правила принуждения.
String(Symbol('my symbol')) // 'Symbol(my symbol)'
'' + Symbol('my symbol') // TypeError is thrownЛогическое преобразование
Явным образом преобразовать значение логичному мостику Применить Boolean () функция. Неявное преобразование происходит в логическом контексте или срабатывает логическими операторами ( && !
Boolean(2) // explicit
if (2) { ... } // implicit due to logical context
!!2 // implicit due to logical operator
2 || 'hello' // implicit due to logical operatorПримечание : Логические операторы, такие как и && логические преобразования внутри, но на самом деле вернуть значение оригинальных операндов
// returns number 123, instead of returning true // 'hello' and 123 are still coerced to boolean internally to calculate the expression let x = 'hello' && 123; // x === 123
Как только 2 возможных результатов булевой конверсии: правда или ложь Просто проще вспомнить список фальсифицированных ценностей.
Boolean('') // false
Boolean(0) // false
Boolean(-0) // false
Boolean(NaN) // false
Boolean(null) // false
Boolean(undefined) // false
Boolean(false) // falseЛюбое значение, которое не в списке, преобразуется в правда , в том числе объект, функция, Массив , Дата , определенный пользователем тип и так далее. Символы – это правдивые ценности. Пустой объект и массивы – это правдивые значения:
Boolean({}) // true
Boolean([]) // true
Boolean(Symbol()) // true
!!Symbol() // true
Boolean(function() {}) // trueЧисловое преобразование
Для явного преобразования просто примените Номер () Функция, такая же, как вы сделали с Boolean () и Строка () Отказ
Неявное преобразование сложно, потому что он срабатывает в большем количестве случаев:
- Операторы сравнения (
>,<,<=,> =) - Битовые операторы (
|&^~) - Арифметические операторы (
-+*/%). Обратите внимание, что двоичный+Не вызывает числовое преобразование, когда любой операнд является строкой. - Унарный
+оператор - Свободный оператор равенства
==(вкл.! =). Обратите внимание, что==Не вызывает числовое преобразование, когда обе операнды являются строками.
Number('123') // explicit
+'123' // implicit
123 != '456' // implicit
4 > '5' // implicit
5/null // implicit
true | 0 // implicitВот как примитивные значения преобразуются в цифры:
Number(null) // 0
Number(undefined) // NaN
Number(true) // 1
Number(false) // 0
Number(" 12 ") // 12
Number("-12.34") // -12.34
Number("\n") // 0
Number(" 12s ") // NaN
Number(123) // 123При преобразовании строки на номер двигателя первые отделки ведущих и конечных пробелов, \ N , \ T Персонажи, возвращение Нан Если обрезанная строка не представляет действительный номер. Если строка пуста, она возвращает 0 Отказ
null и undefined обрабатываются по-разному: null становится 0 , тогда как undefined становится Нан Отказ
Символы не могут быть преобразованы в число ни явно, ни безупречно. Более того, Типеррор брошен, а не молча преобразования в Нан , как будто это происходит для undefined Отказ Увидеть больше на правилах преобразования символов на MDN Отказ
Number(Symbol('my symbol')) // TypeError is thrown
+Symbol('123') // TypeError is thrownЕсть два Особые правила помнить:
- При применении
==кnullилиundefinedЧисловое преобразование не происходит.nullравняется толькоnullилиundefinedи не равно ничего еще.
null == 0 // false, null is not converted to 0 null == null // true undefined == undefined // true null == undefined // true
2. NAN не равен ничего себе:
if (value !== value) { console.log("we're dealing with NaN here") }Тип принуждения к объектам
До сих пор мы посмотрели принуждение к типу для примитивных ценностей. Это не очень захватывающе.
Когда дело доходит до объектов и двигателя, сталкивается с выражением, как [1] + [2,3] Сначала ему необходимо преобразовать объект в примитивное значение, которое затем преобразуется в конечный тип. И еще есть только три типа преобразования: числовая, струнная и логическая.
Самый простой случай – булево преобразование: любая не примитивная ценность всегда принуждено к правда Независимо от того, если объект или массив пусты или нет.
Объекты преобразуются в примитивы через внутреннюю [[Отправьте]] Метод, который несет ответственность за числовое и строковое преобразование.
Вот псевдо реализация [[Отправьте]] Метод:
[[Отправьте]] пропускается с входным значением и предпочтительным типом преобразования: Номер или Строка Отказ ПредпочтительТтип не является обязательным.
Как числовое и строковое преобразование использует два метода входного объекта: Ценность и TOSTRING Отказ Оба метода объявлены на Объект. Прототип и поэтому доступно для любых производных типов, таких как Дата , Массив , так далее.
В целом алгоритм выглядит следующим образом:
- Если вход уже является примитивным, ничего не сделайте и верните его.
2. Позвоните input.tostring () , если результат примитивно, верните его.
3. Позвоните input.valueof () , если результат примитивно, верните его.
4. Если ни input.tostring () ни input.valueof () Урожай примитив, бросок Типеррор Отказ
Числовое преобразование Первые звонки Ценность (3) С ответвлением к TOSTRING (2). Конверсия строки делает противоположное: TOSTRING (2) с последующим Ценность (3).
Самые встроенные типы не имеют Ценность или иметь Ценность Возвращение это Сам объект, так что он игнорируется, потому что это не примитивно. Вот почему числовое и строковое преобразование может работать то же самое – оба в конечном итоге вызывают TOSTRING () Отказ
Различные операторы могут вызвать либо числовое или строковое преобразование с помощью ПредпочтительТтип параметр. Но есть два исключения: свободное равенство == и бинарный + Операторы триггеруют режимы преобразования по умолчанию ( PreferredType не указан или равен по умолчанию ). В этом случае большинство встроенных типов предполагают числовое преобразование в виде по умолчанию, кроме Дата это строковое преобразование.
Вот пример Дата Поведение преобразования:
Вы можете переопределить по умолчанию TOSTRING () и ValueOf () Методы для подключения к объектно-примитивной конверсию.
Обратите внимание, как obj + '' Возвращает '101' как строка. + Оператор запускает режим преобразования по умолчанию, и как сказал до Объект Предполагается, что числовое преобразование в виде по умолчанию, таким образом, используя ValueOf () метод в первую очередь вместо TOSTRING () Отказ
ES6 Symbol. Топривисительный метод
В ES5 вы можете подключить к логике преобразования объекта к примитивным путем, переопределив TOSTRING и Ценность методы.
В ES6 вы можете пойти дальше и полностью заменить внутреннюю [[Отправьте]] рутина путем реализации [Symbol.toprimtive] Метод на объекте.
Примеры
Вооруженный теорией, теперь давайте вернемся к нашим примерам:
true + false // 1
12 / "6" // 2
"number" + 15 + 3 // 'number153'
15 + 3 + "number" // '18number'
[1] > null // true
"foo" + + "bar" // 'fooNaN'
'true' == true // false
false == 'false' // false
null == '' // false
!!"false" == !!"true" // true
['x'] == 'x' // true
[] + null + 1 // 'null1'
[1,2,3] == [1,2,3] // false
{}+[]+{}+[1] // '0[object Object]1'
!+[]+[]+![] // 'truefalse'
new Date(0) - 0 // 0
new Date(0) + 0 // 'Thu Jan 01 1970 02:00:00(EET)0'Ниже вы можете найти объяснение для каждого выражения.
Бинарный + Оператор вызывает числовое преобразование для правда и ложный
true + false ==> 1 + 0 ==> 1
Оператор арифметической дивизии / Триггеры числовое преобразование для строки '6' :
12 / '6' ==> 12 / 6 ==>> 2
Оператор + Увеличить ассоциативность, поэтому выражение «Номер» + 15 работает в первую очередь. Поскольку один операнд – это строка, + Оператор триггеры строки преобразования для числа 15 Отказ На втором шаге выражение "№15" + 3 оценивается аналогично.
"number" + 15 + 3 ==> "number15" + 3 ==> "number153"
Выражение 15 + 3 оценивается в первую очередь. Нет необходимости в принуждении вообще, так как обе операнды номера. На втором шаге выражение 18 + «Номер» Оценивается, и поскольку один операнд представляет собой строку, он вызывает преобразование строки.
15 + 3 + "number" ==> 18 + "number" ==> "18number"
Оператор сравнения & GT; Триггеры числовое преобразование f или [1] A ND N .
[1] > null ==> '1' > 0 ==> 1 > 0 ==> true
Унарный + Оператор имеет более высокий приоритет за двоичным + оператор. Итак, + 'Бар' Выражение оценивает первый. Унарный плюс триггеры числовое преобразование для строки «Бар» Отказ Поскольку строка не представляет действительный номер, результат является Нан Отказ На втором шаге выражение 'foo' + Nan оценивается.
"foo" + + "bar" ==> "foo" + (+"bar") ==> "foo" + NaN ==> "fooNaN"
== Оператор триггеры числовой преобразования, строка «Истина» преобразуется в Nan, Boolean правда преобразуется в 1.
'true' == true ==> NaN == 1 ==> false false == 'false' ==> 0 == NaN ==> false
== обычно вызывает числовое преобразование, но это не так с null Отказ null равна null или undefined Только только и не равно ничего.
null == '' ==> false
! Оператор преобразует оба «Истина» и «Ложь» Строки до логика правда , так как они не пустые строки. Тогда == Просто проверяет равенство двух логических правда без всякого принуждения.
!!"false" == !!"true" ==> true == true ==> true
== Оператор вызывает числовое преобразование для массива. Массив ValueOf () Метод возвращает сам массив и игнорируется, потому что это не примитивно. Массив TOSTRING () преобразует ['x'] просто 'x' нить.
['x'] == 'x' ==> 'x' == 'x' ==> true
+ Оператор вызывает числовое преобразование для [] Отказ Массив ValueOf () Метод игнорируется, потому что он возвращает сам массив, который не примитивна. Массив TOSTRING Возвращает пустую строку.
На втором этапе выражение '' + NULL + 1 оценивается.
[] + null + 1 ==> '' + null + 1 ==> 'null' + 1 ==> 'null1'
Логично и && Операторы принуждают операнды к логии, но вернуть оригинальные операнды (не логические). 0 Является ли язвой, тогда как ‘0’ это правда, потому что это не пустая строка. {}
0 || "0" && {}
==> (0 || "0") && {}
==> (false || true) && true // internally
==> "0" && {}
==> true && true // internally
==> {}Никакое принуждение не требуется, потому что обе операнды имеют тот же тип. С == Проверяет идентификацию объекта (а не для равенства объекта), и два массива являются двумя разными экземплярами, результатом является ложь Отказ
[1,2,3] == [1,2,3] ==> false
Все операнды не примитивные значения, поэтому + начинается с левого запускающего числового преобразования. Оба Объект и Массив Ценность Метод возвращает сам объект, поэтому он игнорируется. TOSTRING () используется в качестве спада. Хитрость вот что первая {} не считается литералом объекта, а скорее как заявление об объявлении Блока, поэтому он игнорируется. Оценка начинается с следующего + [] выражение, которое преобразуется в пустую строку через TOSTRING () метод, а затем к 0 Отказ
{}+[]+{}+[1]
==> +[]+{}+[1]
==> 0 + {} + [1]
==> 0 + '[object Object]' + [1]
==> '0[object Object]' + [1]
==> '0[object Object]' + '1'
==> '0[object Object]1'Это лучше объяснить шаг за шагом в соответствии с приоритетом оператора.
!+[]+[]+![] ==> (!+[]) + [] + (![]) ==> !0 + [] + false ==> true + [] + false ==> true + '' + false ==> 'truefalse'
- Оператор вызывает числовое преобразование для Дата Отказ Date.valueof () Возвращает количество миллисекунд с эпохи Unix.
new Date(0) - 0 ==> 0 - 0 ==> 0
+ Оператор вызывает преобразование по умолчанию. Дата предполагает преобразование строки как один, поэтому TOSTRING () Метод используется, а не ValueOf () Отказ
new Date(0) + 0 ==> 'Thu Jan 01 1970 02:00:00 GMT+0200 (EET)' + 0 ==> 'Thu Jan 01 1970 02:00:00 GMT+0200 (EET)0'
Ресурсы
Я действительно хочу порекомендовать отличную книгу « Понимание ES6 », написанные Николас С. Закас Отказ Это отличный ресурс обучения ES6, не слишком высокий уровень, и не слишком много копает в внутренних органах.
А вот хорошая книга только на ES5 – Meadjs написано Axel Rauschmayer Отказ
( Русский ) Современный Учебник JavaScript – https://learn.javascript.ru/ Отказ Особенно эти Два Страницы на принуждении к типу.
Таблица сравнения JavaScript – https://dorey.github.io/javascript-equality-table/
WTFJS – маленький код блога о том языке, который мы любим, несмотря на то, что нам так много, чтобы ненавидеть – https://wtfjs.com/