JavaScript有两种比较方式:严格比较运算符和转换类型比较运算符。对于严格比较运算符(===
)来说,仅当两个操作数的类型相同且值相等为true
,而对于被广泛使用的比较运算符(==
)来说,会在进行比较之前,将两个操作数转换成相同的类型。
对于关系运算符(比如 <=
)来说,会先将操作数转为原始值,使它们类型相同,再进行比较运算。字符串比较则是使用基于标准字典的Unicode值来进行比较的, 数组也是这样比较的。
一、宽松相等(==)
比较操作符会为两个不同类型的操作数转换类型,然后进行严格比较。当两个操作数都是对象时,JavaScript会比较其内部引用,当且仅当他们的引用指向内存中的相同对象(区域)时才相等,即他们在栈内存中的引用地址相同。
- 类型相同的,执行
===
比较 null == undefiend // true(特殊定义的)
,undefiend
与null
与其他对象与基础类型的值均不相等;- A与数字B比较时,将A转换为数字
ToNumber
,若A为对象先转为原始值ToPrimitive
再转为数字ToNumber
; - A与布尔类型B比较,将A和B转换为数字
ToNumber
,若A为对象先转为原始值ToPrimitive
再转为数字ToNumber
; - A与字符串B比较,若A为数字和布尔类型,则将A和B转换为数字,若A为对象先转为原始值
ToPrimitive
,之后不转数字了;
ToNumber
ToNumber(A)
尝试在比较前将参数A
转换为数字,这与+A
(单目运算符+)的效果相同。
ToPrimitive
ToPrimitive(A)
通过尝试依次调用A
的A.toString()
参见Object.prototype.toString()和A.valueOf()
参见Object.prototype.valueOf()方法,将参数A
转换为原始值(Primitive
)。
其中,A.toString()
返回 “[object type]”, A.valueOf()
返回原始值。Javascript隐式装箱也是用ToPrimitive()
,在Javascript只有字符串和数字才能相加。参见What is {} + {} in JavaScript?
二、一致/严格相等(===)
一致运算符不会进行类型转换,仅当操作数严格相等时返回true
1 | var num = 0; |
对于除了数值之外的值,全等操作符使用明确的语义进行比较:一个值只与自身全等。对于数值,全等操作符使用略加修改的语义来处理两个特殊情况:第一个情况是,浮点数0
是不分正负的。区分+0
和-0
在解决一些特定的数学问题时是必要的,但是大部分境况下我们并不用关心。全等操作符认为这两个值是全等的。第二个情况是,浮点数包含了NaN
值,用来表示某些定义不明确的数学问题的解,例如:正无穷加负无穷。全等操作符认为NaN
与其他任何值都不全等,包括它自己。(等式 (x !== x
) 成立的唯一情况是 x
的值为NaN
)
三、Object.is (ECMAScript 2015/ ES6 新特性)
Object.is
的行为方式与三等式相同,但是对于NaN
和-0
和+0
进行特殊处理,所以最后两个不相同,而Object.is(NaN,NaN)
将为true
。(通常使用双等号或三等于将NaN
与NaN
进行比较结果为false
,因为IEEE 754如是说。)除了+0
和-0
,其他时候避免使用Object.is
,包括NaN
。
四、判等表
x | y | == | === | Object.is |
---|---|---|---|---|
undefined | undefined | true | true | true |
null | null | true | true | true |
true | true | true | true | true |
false | false | true | true | true |
“foo” | “foo” | true | true | true |
{ foo: “bar” } | x | true | true | true |
0 | 0 | true | true | true |
+0 | -0 | true | true | false |
0 | false | true | false | false |
“” | false | true | false | false |
“” | 0 | true | false | false |
“0” | 0 | true | false | false |
“17” | 17 | true | false | false |
[1,2] | “1,2” | true | false | false |
new String(“foo”) | “foo” | true | false | false |
null | undefined | true | false | false |
null | false | false | false | false |
undefined | false | false | false | false |
{ foo: “bar” } | { foo: “bar” } | false | false | false |
new String(“foo”) | new String(“foo”) | false | false | false |
0 | null | false | false | false |
0 | NaN | false | false | false |
“foo” | NaN | false | false | false |
NaN | NaN | false | false | true |
1 | // true 两个操作数均为String |