Как 1 == [1] в javascript?
Недавно мне задали этот вопрос в интервью.
var a = 1;
var b = [1];
Что вернет a == b;
.
Когда я проверил, что на моей консоли браузера Chrome я получил это.
var a = 1;
var b = [1];
a == b;
true
Я также проверил
var a = 1;
var b =(1);
a == b;
true
Я знаю, что b в массиве размером 1. Означает ли это, что размер массива присваивается b. Я действительно смущен. Может ли кто-нибудь объяснить мне логику?
Ответы
Ответ 1
Если объект сравнивается с числом или строкой, JavaScript пытается вернуть значение по умолчанию для объекта. Операторы пытаются преобразовать объект в примитивное значение, значение String
или Number
, используя valueOf
и toString
методы объектов. Если эта попытка конвертировать объект не выполняется, генерируется ошибка времени выполнения. Ref]
var a = 1;
var b = [1];
//What is happening when `(a==b)`
//typeof a; ==> number
//typeof b; ==>object
//Object is converted to Primitive using `valueOf` and `toString` methods of the objects
var val = b.valueOf().toString();
console.log('Values after conversion is: ' + val + ' And typeof converted value is: ' + typeof val);
//typeof val; ==> string
//a == b; will be evaluated as `true` because `'1' == 1` hence..
console.log(a == b); //'1'==1 ==> true
Ответ 2
Я не понял из Района ответа, как valueOf
и toString
вступают в игру при преобразовании объекта в примитивное значение; поэтому я вник в спецификации ECMAScript 2015.
Предупреждение: длинный ответ.
Мы хотим проверить выражение 1 == [1]
.
Начиная с 12.10 операторов равенства, мы видим, что после извлечения значений выражений последний шаг
- Возвращает результат выполнения абстрактного сравнения равенства rval == lval
Абстрактное сравнение равенства определено в главе 7.2.12 Абстрактное сравнение равенства.
7.2.12 Абстрактное сравнение равенства Сравнение x == y, где x и y - значения, создает true или false. Такое сравнение выполняется следующим образом:
- ReturnIfAbrupt (х).
- ReturnIfAbrupt (у).
- Если тип (x) совпадает с типом (y), то а. Верните результат выполнения строгого сравнения равенств x === y.
- Если x равно null, а y - undefined, верните true.
- Если x равно undefined, а y - null, верните true.
- Если Type (x) - Number и Type (y) - String, верните результат сравнения x == ToNumber (y).
- Если Type (x) - String и Type (y) - Number, верните результат сравнения ToNumber (x) == y.
- Если тип (x) является логическим, верните результат сравнения ToNumber (x) == y.
- Если тип (y) булев, верните результат сравнения x == ToNumber (y).
- Если Type (x) является либо строкой, либо числом, либо символом, и типом (y) является Object, тогда вернуть результат сравнения x == ToPrimitive (y).
- Если Type (x) - это Object и Type (y) - либо String, Number, либо Symbol, то верните результат сравнения ToPrimitive (x) == y.
- Возвращает false.
Выражение 1 == [1]
относится к случаю 10.
Поэтому в основном, как и ожидалось, массив [1]
преобразуется в значение примитивного типа.
ToPrimitive определяет в 7.1.1 ToPrimitive (input [, PreferredType])
Абстрактная операция ToPrimitive принимает входной аргумент и необязательный аргумент PreferredType. абстрактная операция ToPrimitive преобразует свой входной аргумент в не-объектный тип.
Я не включил полную цитату, так как только интересный для этого примера:
- Аргумент PreferredType (на самом деле hint var) преобразуется из "default" (поскольку он не передается) в "число".
-
OrdinaryToPrimitive
вызывается с теми же аргументами.
Теперь интересная часть OrdinaryToPrimitive сделает следующее:
- Assert: Type (O) - Object
- Assert: Тип (подсказка) - это String, а его значение - "строка" или "число".
- Если подсказка "строка", то а. Пусть methodNames - "toString", "valueOf" ".
- Else,
а. Пусть methodNames будут" "valueOf", "toString" ". - Для каждого имени в именах методов в списке, выполните а. Пусть метод Get (O, name).
б. ReturnIfAbrupt (метод).
с. Если IsCallable (метод) истинно, тогда... i. Пусть результат будет Call (метод, O).
... ii. ReturnIfAbrupt (результат).
... iii. ** Если Тип (результат) не Объект, верните результат. ** - Выбросить исключение TypeError
Итак, чтобы преобразовать [1]
в примитивное значение, среда выполнения сначала попросит позвонить valueOf
. Этот метод возвращает сам массив, который является объектом, поэтому по 5.c.iii следующий метод toString
.
Этот метод возвращает элементы массива как список, разделенный запятыми, поэтому он просто возвращает строку "1"
.
Итак, мы сокращаем сравнение 1 == "1"
, которое по правилам абстрактного равенства, пункт 6, означает преобразование "1"
в число 1
и выполнение тривиального сравнения 1 = 1
.
С сомнительным читателем предлагается проверить, как Сравнение строгого равенства действительно определено в стандарте.
Вы можете играть с этими преобразованиями, чтобы лучше понять их, вот пример HTML файла игровой площадки
<html>
<head><title>title</title></head>
<body>
<script>
var old_valueOf = Array.prototype.valueOf;
var old_toString = Array.prototype.toString;
Array.prototype.valueOf = function(){ console.log("Array::valueOf"); return old_valueOf.apply(this); };
Array.prototype.toString = function(){ console.log("Array::toString"); return old_toString.apply(this); };
console.log(1 == [1]); //Array::valueOf, Array::toString, true
Array.prototype.valueOf = function(){ console.log("Array::valueOf"); return 2; };
console.log(1 == [1]); //Array::valueOf, false
Array.prototype.valueOf = function(){ console.log("Array::valueOf"); return {}; };
Array.prototype.toString = function(){ console.log("Array::toString"); return {} };
console.log(1 == [1]); //Array::valueOf, Array::toString, Uncaught TypeError: Cannot convert object to primitive value
</script>
</body>
</html>
Ответ 3
Это связано с типом сравнения.
В javascript для сравнения можно использовать либо ==
, либо ===
. В случае тройного равенства это то, что известно как равенство без принуждения типа, другими словами, это строгое сравнение.
Равенство с типом принуждения
И наоборот, это означает, что использование двойного эквивалентного операнда - это равенство с типом принуждения.
Что это значит?
Проще говоря, это означает, что javascript будет использовать встроенные методы, чтобы преобразовать значение в примитивный тип, готовый для сравнения. В частности, эти методы .valueOf()
и .toString()
.
Вот несколько примеров:
0 == false // true, auto type coercion
0 === false // false, because they are of a different type
1 == "1" // true, auto type coercion
1 === "1" // false, because they are of a different type
Ergo:
1 == [1] // true
1 === [1] // false, because they are of a different type