[ECMAScript] == 运算符与隐式类型转换

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)如果xy是相同类型的,则调用x===y
(2)如果xy分别是nullundefined,则返回true
(3)如果xy的类型分别是NumberString,则调用ToNumber将String转为Number再进行==比较,
(4)如果xy有一个是Boolean,则调用ToNumber将Boolean转为Number再进行==比较,
(5)如果xy有一个是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类型中有两个值,为truefalse
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只能是numberstring

如果是number,则按顺序调用OvalueOftoString方法,
直到有一个方法返回原始值。
如果都没有返回原始值,则抛TypeError异常。

如果是string,则按顺序调用OtoStringvalueOf方法,
直到有一个方法返回原始值。
如果都没有返回原始值,则抛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


参考

ECMAScript 2017 Language Specification

你可能感兴趣的:([ECMAScript] == 运算符与隐式类型转换)