由一道小题引发的对ECMA规范的浏览

434 阅读3分钟

由夕姐一道题引发的对ECMA规范的浏览

有幸参加前端小姐姐Step-By-Setp的活动,因一道题发现自己的基础相当薄弱,之前都是看大佬们的文章,百度,mdn,畏惧去读一些规范相关的东西,难免出现不理解和不全面的情况。因此就着当前的问题,读了一下下ECMA的规范,本文仅作学习笔记。

题目:如何让 (a == 1 && a == 2 && a == 3) 的值为true?
来源:github.com/YvetteLau/S…

本题主要考察的知识点有三个:

  • == 操作运算符的下数据的隐式类型转换
  • 数据的隐式类型转换做了什么
  • 数据劫持
  1. 首先 == 操作运算符的数据隐式类型转换,以下是ECMA2016原文

    可以看到是先转换为原始数据类型再进行比较。如果a本就是原始数据类型,则不能(也许有我不知道的方法)同时等于1,2,3,因1此a是Object类型,则按8,9条规则作ToPrimitive处理。

  2. ToPrimitive

    可以看到原始数据类型是不变,Object类型有以下规则

  3. Object => Primitive规则

  • 如果有@@toPrimitive方法,则使extoicToPrim方法指向该方法,即Symbol.toPrimitive
  • 如果没有,则使用默认的方法,下文可得知是valueOf和toString
  • 默认方法会根据hint值判断使用valueOf还是toString,其中根据第6条,默认是number,即使用valueOf,但是有例外是Date(下面说)
  • 根据下面方框第5条,会将可执行的方法排成列表依次执行,如果获得原始类型则返回,否则返回result,继续执行。都没成功,则报错。
  • Note提示显示Symbol和Date重写了该行为使用@@toPrimitive方法,此处即是Symbol.toPrimitive
  1. 特殊对象,Date
    从Date的标准中可以看出,Date的默认hint是String而不是Number,即默认调用toString. 这样是为什么使用 + new Date()返回时间戳的原因,使用+运算符改变hint为number。 而Date的Symbol.toPrimitive在不传hint,默认的情况下则会报错。

测试如下:

  1. valueOf和toString的优先级
var a = [1,2,3]
a.valueOf = function(){
    console.log('调用valueOf');
    
    return 3;
}
a.toString = function(){
    console.log('调用toString');
    return this;
}

a == 3  //“调用valueOf” “调用toString”
var b = [1,2]
b[a] //“调用toString” “调用valueOf”

  1. Symbol.toPrimitive优先级
//Symbol.toPrimitive优先级最高
let a = {
    [Symbol.toPrimitive]: () => 2,
    valueOf :function(){
        console.log('调用valueOf');
        return 3
    },
    toString :function(){
        console.log('调用toString');
        return 4;
    }
};
a == 2 //true
  1. Date
var date = new Date();
d //Fri May 24 2019 06:41:02 GMT+0800 (中国标准时间)
+d //1558651262982
date[Symbol.toPrimitive]('number');//1558651262982
date[Symbol.toPrimitive]();//error

而关于本道题的答案,大佬们的回答很好且全面,直接看github的issue即可。此处不写。

参考文献

  1. [ECMAScript® 2016 Language Specification]
    www.ecma-international.org/ecma-262/7.…
  2. [ECMAScript7规范中的ToPrimitive抽象操作] blog.csdn.net/weixin_3409…
  3. [前端小姐姐对于问题的解答]
    github.com/YvetteLau/S…