Javascript 中 == 和 === 区别是什么?

这个问题的答案网上随处可见,总结下答案无外乎就是: 双等号会造成类型转换,推荐一律使用三等号寻寻觅觅一直没有遇到能说服我放弃双等号的答案,还请大家赐教…
关注者
518
被浏览
390,141
登录后你可以
不限量看优质回答私信答主深度交流精彩内容一键收藏

经过这两天的狂看规范,本人算是略微了解了这些“怪异”行为背后的逻辑,下面把我所查到的东西分享给大家。这篇文章算是我看完这些规范后概括总结出的结果。对比规范,我主要是删除一些实现细节,并让步骤变得更加简明易懂。如果有错,敬请大家指出。

这个答案应该是能解释 @Belleve 大大中提到的代码了,也应该能回答你的疑问。

“==”操作符计算步骤

  1. 如果两个参数的类型相等,则使用“===”来求出结果
  2. 如果两个参数中一个为null,另一个为undefined,那么返回true
  3. 如果某个参数的类型为符号,返回false
  4. 如果某个参数的类型不是数字,那么就使用操作ToNumber将其转换成数字后再进行判断(详见下文“‘转化为数值’操作”一章)

“===”操作符计算步骤

  1. 如果两个参数的类型不相等,返回false
  2. 如果两个参数的类型为null或undefined,返回true
  3. 如果两个参数的类型为数字,那么:
    1. 如果有一个操作符的值为NaN,那么返回false
    2. 如果不为NaN,且两数值相等,那么返回true
    3. 在比较数值时不区分+0和-0,即-0===+0,+0===-0.
  4. 如果两个参数的类型为字符串,那么比较两个字符串的值是否相等
  5. 如果两个参数的类型为对象,那么判断这两个参数是否为同一个对象(指向同一引用)
  6. 如果两个参数的类型为符号(Symbol),那么判断这两个参数是否为同一个符号。

“转换为数值”操作

根据被转换值的类型,应用下表中相应的规则来完成转换。

“转换为原始类型”操作

如果被转换值本身就是原始类型(非对象类型),那么直接返回,否则进行如下操作:

  1. 如果对象定义了Symbol.toPrimitive函数,那么调用这个函数来获得原始类型的值
    1. 如果函数返回值为原始类型,那么结束操作并返回该值
    2. 否则抛出TypeError错误,并结束操作
  2. 如果对象没有定义Symbol.toPrimitive函数,那么执行以下操作:
    1. 如果Symbol.toPrimitive函数未定义,或其返回了一个对象,那么就调用对象valueOf函数来获得原始类型的值。
    2. 如果valueOf函数未定义,或其返回了一个对象,那么就调用对象的toString函数来获得原始类型的值
    3. 如果toString函数未定义,或其返回了一个对象,那么就抛出TypeError错误,并结束操作

:Symbol.toPrimitive函数即对象中属性名为“Symbol.toPrimitive”的函数,如obj[Symbol.toPrimitive]。Symbol.toPrimitive为Symbol对象中的一个属性,是一个系统内置的符号。

注:Object.prototype.valueOf默认返回对象自身,而Object.prototype.toString返回带有类型名称的字符串值。

注:如果你要编写自定义的valueOf或toString函数,那么要注意valueOf函数返回的应当是一个数值,toString函数应当返回一个字符串。(Object.prototype.valueOf返回对象是因为了默认的效果Orz…)

注:这个操作针对本文内容做了流程上的简化。在完整的过程中valueOf和toString的调用次序是可以改变的,也就是说,步骤2.2可以放在2.3后进行,这个次序由操作的第二个参数“preferType”决定。由于本文涉及的流程中“preferType”的值都是一样的,也就是说调用过程都一样,所以可以将第二种情况略去。

例子

例子一:计算Number({})的过程

根据上文,我们能够推出计算的步骤应当如下:

  1. 首先我们需要求出这个对象的原始值,这个过程计算如下:
    1. 寻找对象的Symbol.toPrimitive函数,显然我们并没有定义这个函数,所以略过。
    2. 寻找对象的valueOf函数,虽然对象自身没有,但是可以在Object.prototype上找到。根据规范,Object.prototype.valueOf函数返回调用该函数的对象自身,所以我们得到了一个对象。
    3. 由于我们需要一个原始类型,而valueOf返回的却是一个对象,所以我们忽略它,并寻找函数的toString函数
    4. 同样的,由于对象自身没有toString方法,所以最后我们调用了Object.prototype.toString方法。根据规范,此时函数的返回值应当是字符串"[Object object]"
    5. 所以{}的原始值为字符串"[Object object]"
  2. 然后我们将步骤一中得到的原始类型转换成数值类型。显然,"[Object object]"并不是一个数值的字面量,根据规定,在这种情况下的结果应为NaN
  3. 最终,我们可以得出:Number({})的结果为NaN

例子二:在Chrome上的操作示例

浏览器为Chrome 76


由于目前API中没有真正的toPrimitive操作,而Number(…)内部使用了这个操作,所以我使用这个函数演示效果。