JavaScript在进行==
运算时,会进行隐式类型转换,
因此,当==
运算符两边的类型不同时,会产生很多令人困惑的结果。
下面从规范入手,理清==
运算符的判断过程。
1. == 判断
ECMAScript 2017规范,7.2.13 Abstract Equality Comparison
给出了==
运算符的判断过程,
1. If Type(x) is the same as Type(y), then
a. Return the result of performing Strict Equality Comparison x === y.
2. If x is null and y is undefined, return true.
3. If x is undefined and y is null, return true.
4. If Type(x) is Number and Type(y) is String, return the result of the comparison x == ToNumber(y).
5. If Type(x) is String and Type(y) is Number, return the result of the comparison ToNumber(x) == y.
6. If Type(x) is Boolean, return the result of the comparison ToNumber(x) == y.
7. If Type(y) is Boolean, return the result of the comparison x == ToNumber(y).
8. If Type(x) is either String, Number, or Symbol and Type(y) is Object, return the result of the comparison x == ToPrimitive(y).
9. If Type(x) is Object and Type(y) is either String, Number, or Symbol, return the result of the comparison ToPrimitive(x) == y.
10. Return false.
整个过程可以分为6个逻辑部分,
(1)如果x
和y
是相同类型的,则调用x===y
,
(2)如果x
和y
分别是null
和undefined
,则返回true
,
(3)如果x
和y
的类型分别是Number
和String
,则调用ToNumber将String
转为Number
再进行==
比较,
(4)如果x
和y
有一个是Boolean
,则调用ToNumber将Boolean
转为Number
再进行==
比较,
(5)如果x
和y
有一个是Object
,另一个是String / Number / Symbol
,则调用ToPrimitive将Object
转为原始值再进行比较。
(6)否则,返回false
。
2. 关于类型
6.1 ECMAScript Language Types指出,
ECMAScript包括以下7种类型,
Undefined
类型,Null
类型,Boolean
类型,String
类型,
Symbol
类型,Number
类型,Object
类型。
Undefined
类型中只要一个值,那就是undefined
,
Null
类型中也只有一个值,那就是null
,
Boolean
类型中有两个值,为true
和false
。
Symbol
类型是ES 2016新引入的,使用Symbol(...)
或者Symbol.for(...)
来创建,
Number
类型和String
类型指的是,数字和字符串字面量。
Object
类型包含了剩余的其他值,除Object
类型之外的其他类型值称为原始值(primitive value)。
3. ToNumber ( argument )
ToNumber遵循以下转换规则,
类型 | 结果 |
---|---|
Undefined | NaN |
Null | +0 |
Boolean | true -> 1, false -> +0 |
Number | 原样返回 |
String | 参考ToNumber Applied to the String Type |
Symbol | 抛TypeError异常 |
Object | Let primValue be ? ToPrimitive(argument, hint Number). Return ? ToNumber(primValue). |
4. ToPrimitive ( input [ , PreferredType ] )
ToPrimitive会将input
转换成一个原始值,
如果input
有多种转换方式,还需要提供PreferredType
用来指定具体转换为哪种值,
以下是转换步骤,
1. Assert: input is an ECMAScript language value.
2. If Type(input) is Object, then
a. If PreferredType was not passed, let hint be "default".
b. Else if PreferredType is hint String, let hint be "string".
c. Else PreferredType is hint Number, let hint be "number".
d. Let exoticToPrim be ? GetMethod(input, @@toPrimitive).
e. If exoticToPrim is not undefined, then
1) Let result be ? Call(exoticToPrim, input, « hint »).
2) If Type(result) is not Object, return result.
3) Throw a TypeError exception.
f. If hint is "default", set hint to "number".
g. Return ? OrdinaryToPrimitive(input, hint).
3. Return input.
ToPrimitive
只可以用来转Object
类型的值,
如果input
是原始值,则直接返回。
如果PreferredType
没有提供,则置为default
。
执行过程中,首先判断input
有没有@@toPrimitive
这个方法,
如果有就调用该方法,并将PreferredType
作为hint
传入。
a = {
[Symbol.toPrimitive](){
return 1;
}
};
1 == a; // true
a == 1; // true
否则,如果input
没有@@toPrimitive
(即Symbol.toPrimitive)这个内置方法,
就调用OrdinaryToPrimitive,
并将PreferredType
作为hint
传入(如果是default
,则也传入number
)。
5. OrdinaryToPrimitive ( O, hint )
OrdinaryToPrimitive执行过程如下,
1. Assert: Type(O) is Object.
2. Assert: Type(hint) is String and its value is either "string" or "number".
3. If hint is "string", then
a. Let methodNames be « "toString", "valueOf" ».
4. Else,
a. Let methodNames be « "valueOf", "toString" ».
5. For each name in methodNames in List order, do
a. Let method be ? Get(O, name).
b. If IsCallable(method) is true, then
1) Let result be ? Call(method, O).
2) If Type(result) is not Object, return result.
6. Throw a TypeError exception.
此时传入的hint
只能是number
或string
。
如果是number
,则按顺序调用O
的valueOf
和toString
方法,
直到有一个方法返回原始值。
如果都没有返回原始值,则抛TypeError
异常。
如果是string
,则按顺序调用O
的toString
和valueOf
方法,
直到有一个方法返回原始值。
如果都没有返回原始值,则抛TypeError
异常。
b = {
valueOf(){
return 1;
},
toString(){
return 2;
}
};
1 == b; // true
b == 1; // true
c = {
[Symbol.toPrimitive](){
return 0;
},
valueOf(){
return 1;
},
toString(){
return 2;
}
};
0 == c; // true
c == 0; // true