我不知道的ECMAScript2021——‘==’比较

结论

简单总结一下==比较的算法规则:

  1. 首先==两边的操作数是否是同一类型,如果是,则同===比较。
  2. 两个操作数都是基本数据类型(Number,String,Boolean,BigInt,Symbol):
    • 一个 操作数是Numeric(Number、BigInt),则将另外一个操作数转成对应的数值类型来比较。
    • 一个操作数是Boolean,则将Boolean转化成Number,再进行比较。
  3. 一个操作数是对象,则通过ToPrimitive ( input [ , PreferredType ] ),将对象转换成基本数据类型再比较。

经过转换,很多最后都会变成数值之间的比较,如果mathematical value的值相等,那返回true,否则为false。需要注意的是,NaN, +∞, -∞的出现,就代表了整个比较的结果为false

结论来源:

引自 ‘Draft ECMA-262 / June 23, 2020 ECMAScript® 2021 Language Specification’

7.2.15 Abstract Equality Comparison

The comparison x == y, where x and y are values, produces true or false. Such a comparison is performed as follows:

  1. If Type(x) is the same as Type(y), then

    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 BigInt and Type(y) is String, then

    • Let n be ! StringToBigInt(y).
    • If n is NaN, return false.
    • Return the result of the comparison x == n.
  7. If Type(x) is String and Type(y) is BigInt, return the result of the comparison y == x.

  8. If Type(x) is Boolean, return the result of the comparison ! ToNumber(x) == y.

  9. If Type(y) is Boolean, return the result of the comparison x == ! ToNumber(y).

  10. If Type(x) is either String, Number, BigInt, or Symbol and Type(y) is Object, return the result of the comparison x == ? ToPrimitive(y).

  11. If Type(x) is Object and Type(y) is either String, Number, BigInt, or Symbol, return the result of the comparison ? ToPrimitive(x) == y.

  12. If Type(x) is BigInt and Type(y) is Number, or if Type(x) is Number and Type(y) is BigInt, then

  13. Return false.

注意事项

需要注意上文中的!与?,并不是JS的操作符,而是规范中运行时语义(Runtime Semantic)——ReturnIfAbrupt的shorthand。其中!与?的区别主要在调用语义处返回的Completion Record的类型([[Type]])上,在这里讨论的==比较中应该没什么影响,详情见规范的第五章第二节和第七章第二节:

Invocations of abstract operations and syntax-directed operations that are prefixed by ? indicate that ReturnIfAbrupt should be applied to the resulting Completion Record.

Similarly, prefix ! is used to indicate that the following invocation of an abstract or syntax-directed operation will never return an abrupt completion and that the resulting Completion Record‘s [[Value]] field should be used in place of the return value of the operation.

ToPrimitive ( input [ , PreferredType ] )的转化规则如下,仅需要注意,其中用到的toString等函数可能是被内置覆盖了的,如[].toString(),其他的就不做更多的解读了:

7.1.1 ToPrimitive ( input [ , PreferredType ] )

The abstract operation ToPrimitive takes argument input and optional argument PreferredType. It converts its input argument to a non-Object type. If an object is capable of converting to more than one primitive type, it may use the optional hint PreferredType to favour that type. It performs the following steps when called:

  1. Assert: input is an ECMAScript language value.
  2. If Type(input) is Object, then
    1. If PreferredType is not present, let hint be “default“.
    2. Else if PreferredType is hint String, let hint be “string“.
    3. Else,
      1. Assert: PreferredType is hint Number.
      2. Let hint be “number“.
    4. Let exoticToPrim be ? GetMethod(input, @@toPrimitive).
    5. 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.
    6. If hint is “default“, set hint to “number“.
    7. Return ? OrdinaryToPrimitive(input, hint).
  3. Return input.

7.1.1.1 OrdinaryToPrimitive ( O, hint )

The abstract operation OrdinaryToPrimitive takes arguments O and hint. It performs the following steps when called:

  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

    • Let methodNames be « “toString“, “valueOf“ ».
  4. Else,

    • Let methodNames be « “valueOf“, “toString“ ».
  5. For each name in methodNames in List order, do
  6. Let method be ? Get(O, name).
    1. If IsCallable(method) is true, then
    2. Let result be ? Call(method, O).
      1. If Type(result) is not Object, return result.
  7. Throw a TypeError exception.

你喜欢独处,却又担心寂寞,于是你爱上一阵又一阵迎面吹来的风