为什么 [1] == 1 的结果会是true
就是如果在console中写入
[1] == 1
他返回的竟然是
true
让我很不解,一个数组和数字比较,他是怎么转化的呢?直接将数组转换成什么呢?
对此,我又进行了如下的测试
[2] == 1
false
[2] == 2
true
[1,3] == 1
false
[1,3] == 2
false
[1,3] == 3
false
['a'] == 'a'
true
['a','b'] == 'a'
false
['a','b'] == 'ab'
false
总的来看,似乎是如果只有一个元素,那么就和这一个元素判断是否相等,如果多个就进行正常的判断,请问是这样么?
另外对于这种情况的转化规则是什么?
ecmascript5 ecmascript JavaScript
Answers
不是,是
toString()
是先调用
valueOf()
但是默认的
valueOf()
返回的是对象,所以再去调用
toString()
[1] == 1
true
[1,2] == "1,2"
true
Array.prototype.toString = function(){return this.join("")}
[1,2] == "1,2"
false
[1,2] == "12"
true
[1,2] == 12
true
Array.prototype.toString = function(){return 10}
[1,2] == 10
true
[1,2,2] == 10
true
[9] == 9
false
Array.prototype.valueOf = function(){return 11}
[1,2] == 11
true
[1,2] == 10
false
这是因为
[1]
在和
1
进行比较的时候,首先会被转成数字类型,而转成数字类型在实现上又是通过
new Number()
来完成的。
所以,
[1] == 1
会变成
(new Number([1])).valueOf() == 1
。
同时,
Number
的构造函数其实是接受一个字符串作为参数的,加上
[1].toString() === "1"
,所以
new Number([1])
相当于写
new Number("1")
,于是很显然
(new Number([1])).valueOf()
就是
1
了。
当然啦,上面那些都是从浏览器实现的角度来说明问题,其实之前就有人问过和答过几乎一样的问题,重复这个论调太 low。接下来,我还是从 ECMAScript 262 的标准角度来解答,看看为什么浏览器会实现成这样,没耐心的同学可以略过了。
要通过
==
比较值,且运算符两边的变量类型一个是 Number 另一个是对象,javascript
标准的 11.9.3 节
规定应该将对象使用
ToPrimitive
这个抽象操作(abstract operation)将对象转换成基本类型(primitive)再进行比较。
原文是:
- If Type(x) is either String or Number and Type(y) is Object, return the result of the comparison x == ToPrimitive(y).
那么接下来问题就是,
Array
如何转换成基本类型?这在
标准的 9.1 节
有规定,其具体实现细节则写在
8.12.8 节
。
在 hint 为
Number
时,标准规定:
-
调用对象的
valueOf
方法,如果返回值为基本类型,这个值就是最终值; -
否则,调用
toString
方法,如果返回值为基本类型(字符串是基本类型之一),这个值就是最终值; - 以上皆不可,抛出异常。
对于
Array
来说:
-
valueOf
返回是Array
本身,所以不符合第 1 条; -
toString
返回的时"1"
,符合第 2 条,因此ToPrimitive
的结果就是"1"
。
那么,比较
[1] == 1
就变成比较
"1" == 1
了。对于这种比较,
标准的 11.9.3 节
规定的很明白,就是
ToNumber("1") == 1
,注意这里的
ToNumber
也是个抽象操作,并非实际函数,具体规则在
标准的 9.3.1 节
定义,简单说就是通过类似
new Number()
的算法将字符串变成数字,毫无疑问
"1"
应该变成数字
1
。
因此,
[1] == 1
是 true,而
[1, 2] == 1
相当于
"1,2" == 1
结果就是 false 了。
补充说明一下,为什么 @zonxin 的答案从现象上来说差不多正确,但实际上没那么简单。
从我的答案可看出,
ToPrimitive
最终是调用了
toString
来获得字符串值并和数字进行比较,所以一般来说把
[1] == 1
理解成
[1].toString() == 1
是没问题的。
可问题是,假如
toString
返回的不是基础类型怎么办呢?
javascript
// 在 Chrome/42.0.2311.90 验证通过 Array.prototype.toString = function() { return {} } [1] == 1 // 抛出 TypeError 异常
这说明至少 Chrome 的实现是非常符合标准的,也同时说明,简单将
[1] == 1
理解成
[1].toString() == 1
并不是十分精确。
当然,以上都是吹毛求疵,了解一下就好啦。
简单说是 比较运算符 == 会在比较之前判断操作数的类型是否相等,如果不相等,就想转换成合适的类型,再比较。
举例:
'1' == 1 // true
因为比较运算符会先将左操作数由一个字符字面量衍生出一个数学的值,简单说就是字符串‘1’变数字1
再进行比较的时候 1 == 1
标准相等操作符(== and !=) 使用 Abstract Equality Comparison Algorithm 去比较两个操作数。当两个操作数类型不相等时,会在比较前尝试将其转换为相同类型。 e.g., 对于表达式 5 == '5', 在比较前会先将左边字符串类型的操作数 5 转换为数字。
严格相等操作符 (=== and !==) 使用 Strict Equality Comparison Algorithm 并尝试对两个相同操作数进行相等比较,如果它们的类型不相等,那么永远会返回false 所以 5 !== '5'。
-
当需要明确操作数的类型和值的时候,或者操作数的确切类型非常重要时,应使用严格相等操作符。否则,当你允许操作数在比较前进行类型转换时,可以使用标准相等操作符来比较。
-
当比较运算设计类型转换时 (i.e., non–strict comparison), JavaScript 会按以下规则对字符串,数字,布尔或对象类型的操作数进行操作:
-
当比较数字和字符串时,字符串会转换成数字值。 JavaScript 尝试将数字字面量转换为数字类型的值。 首先, 一个数学上的值会从数字字面量中衍生出来,然后得到被四舍五入后的数字类型的值。
如果其中一个操作数为布尔类型,那么布尔操作数如果为true,那么会转换为1,如果为false,会转换为整数0,即0。 -
如果是一个对象与数字或字符串向比较,JavaScript会尝试返回对象的默认值。操作符会尝试将对象转换为其原始值(一个字符串或数字值)通过方法valueOf和toString。如果尝试转换失败,会产生一个运行时错误。
-
注意:当且仅当与原始值比较时,对象会被转换为原始值。当两个操作数均为对象时,它们作为对象进行比较,仅当它们引用相同对象是返回true。