1. 引言

这是在 JavaScript 中用来进行数值和对象对比时常用的操作符,从定义上来看:

  • == :抽象相等,比较时会先进性类型转换,然后再比较值

  • === :严格相等,会比较两个值的类型和值

测试例子:

console.log('10'==10);  // true
console.log('10'===10); // false

2. ECMA 规范

上面的例子只是从最直观的角度展示两个操作符的差别,想要从底层原理上来剖析两者的区别,还需要回归到 ECMA 的规范,这里以 ECMAScript 2016/ECMA-262 7th 文档(即 ES 6 版本)中的内容作为参考依据:

Type(x) 标识 x 的类型,Type(y) 标识 y 的类型

2.1 Strict 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 different from Type(y), return false.

  2. If Type(x) is Number, then

    • a. If x is NaN, return false.

    • b. If y is NaN, return false.

    • c. If x is the same Number value as y, return true.

    • d. If x is +0 and y is ‐0,return true.

    • e. If x is ‐0 and y is +0, return true.

    • f. Return false.

  3. Return SameValueNonNumber(x, y).

NOTE This algorithm differs from the SameValue Algorithm in its treatment of signed zeroes and NaNs.

第 1、2 点比较容易理解,直接字面翻译即可:

  1. 如果 Type(x) 和 Type(y) 不同,返回 false;

  2. 如果 Type(x) 是 Number :

    • 假如 x 是 NaN ,返回 false

    • 假如 y 是 NaN ,返回 false

    • 假如 x 的数值与 y 相等,返回 true

    • 假如 x 是 +0 ,y 是 -0 ,返回 true

    • 假如 x 是 -0 ,y 是 +0 ,返回 true

    • 其他情况,返回 false

  3. 返回 SameValueNonNumber(x,y) 的结果

SameValueNonNumber

这是计算非 Number 类型 x, y 是否相同的方法,详细定义如下:

The internal comparison abstract operation SameValueNonNumber(x, y), where neither x nor y are Number values, producestrue or false. Such a comparison is performed as follows:

  1. Assert: Type(x) is not Number.

  2. Assert: Type(x) is the same as Type(y).

  3. If Type(x) is Undefined, return true.

  4. If Type(x) is Null, return true.

  5. If Type(x) is String, then

    • a. If x and y are exactly the same sequence of code units (same length and same code units at corresponding indices), return true; otherwise, return false.

  6. If Type(x) is Boolean, then

    • a. If x and y are both true or both false, return true; otherwise, return false.

  7. If Type(x) is Symbol, then

    • a. If x and y are both the same Symbol value, return true; otherwise, return false.

  8. Return true if x and y are the same Object value. Otherwise, return false.

  1. 断言 :Type(x) 不是 Number

  2. 断言 :Type(x) 和 Type(y) 相同

  3. 假如 Type(x) 是 Undefined ,返回 true

  4. 假如 Type(x) 是 Null ,返回 true

  5. 假如 Type(x) 是 String ,则

    当且仅当 x, y 字符序列相同(长度相同且每个位置上的字符也相同),返回 true ,否则,返回 false

  6. 假如 Type(x) 是 Boolean ,则

    x, y 都为 true 或都为 false ,返回 true ,否则,返回 false

  7. 假如 Type(x) 是 Symbol ,则

    当 x, y 具有相同 Symbol 值,返回 true,否则,返回 false

  8. 假如 x 和 y 是同一个对象值,返回 true,否则,返回 false

2.2 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), thena. 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.

  1. 假如 Type(x) 和 Type(y) 相同,则

    返回严格对比 x===y 的结果

  2. 假如 x 是 Null 而 y 是 Undefined ,返回 true

  3. 假如 x 是 Undefined 而 y 是 Null ,返回 true

  4. 假如 Type(x) 是 Number 而 Type(y) 是 String ,返回 x==ToNumber(y) 的结果

  5. 假如 Type(x) 是 String 而 Type(y) 是 Number ,返回 ToNumber(x)==y 的结果

  6. 假如 Type(x) 是 Boolean ,返回 ToNumber(x)==y 的结果

  7. 假如 Type(y) 是 Boolean ,返回 x==ToNumber(y) 的结果

  1. 假如 Type(x) 是 String 、Number 或 Symbol 其中之一,而 Type(y) 是 Object,则返回 x == ToPrimitive(y) 的结果

  2. 假如 Type(x) 是 Object 而 Type(y) 是 String 、Number 或 Symbol 其中之一,则返回 ToPrimitive(x)==y 的结果

  3. 其他情况,返回 false

ToPrimitive

用于将复杂数据类型转化为简单数据类型,或者说转换为原始类型(Null, Undefined, Number, String, Boolean 等),详细定义如下:

The abstract operation ToPrimitive takes an input argument and an optional argument PreferredType. The abstract operation ToPrimitive 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. Conversion occurs according to Table 9 :

Input Type Result
Undefined Return input.
Null Return input.
Boolean Return input.
Number Return input.
String Return input.
Symbol Return input.
Object Perform the steps following this table.

When Type(input) is Object, the following steps are taken:

  1. If PreferredType was not passed, let hint be “default”.

  2. Else if PreferredType is hint String, let hint be “string”.

  3. Else PreferredType is hint Number, let hint be “number”.

  4. Let exoticToPrim be ? GetMethod(input, @@toPrimitive).

  5. If exoticToPrim is not undefined, then

    • a. Let result be ? Call(exoticToPrim, input, « hint »).

    • b. If Type(result) is not Object, return result.

    • c. Throw a TypeError exception.

  6. If hint is “default”, let hint be “number”.

  7. Return ? OrdinaryToPrimitive(input, hint).When the abstract operation OrdinaryToPrimitive is called with arguments O and hint, the following steps are taken:

  8. Assert: Type(O) is Object.

  9. Assert: Type(hint) is String and its value is either “string” or “number”.

  10. If hint is “string”, then

    • a. Let methodNames be « “toString”, “valueOf” ».

  11. Else,

    • a. Let methodNames be « “valueOf”, “toString” ».

  12. For each name in methodNames in List order, do

    • a. Let method be ? Get(O, name).

    • b. If IsCallable(method) is true, then

      • i. Let result be ? Call(method, O).

      • ii. If Type(result) is not Object, return result.

  13. Throw a TypeError exception.NOTE When ToPrimitive is called with no hint, then it generally behaves as if the hint were Number. However, objects may over‐ride this behaviour by de ining a @@toPrimitive method. Of the objects de ined in this speci ication only Date objects (see 20.3.4.45) and Symbol objects (see 19.4.3.4) over‐ride the default ToPrimitive behaviour. Date objects treat no hint as if the hint were String.

ToPrimitive 接口的定义其实是 ToPrimitive(input[, PreferredType])

  • 第一个参数input 是必选参数,传入需要进行转换的数据

  • 第二个参数 PreferredType 是可选参数,传入期望的转换后的数据类型,后面称之为 hint

当 input 的类型为 Null, Undefined, Number, String, Boolean 和 Symbol 这些数据类型时,不需要进行转换,直接返回。

当 input 是 Object 类型时,则

  1. 假如没有传入 PreferredType ,令 hint 为 “default”

  2. 假如 PreferredType 是 String,令 hint 为 "string"

  3. 假如 PreferredType 是 Number,令 hint 为 "number"

  4. 令 exoticToPrim 为 GetMethod(input, @@toPrimitive) 的返回值,其中 @@toPrimitive 是一个用于将对象转换成原始值的方法。

  5. 假如 exoticToPrim 不是 undefined ,则:

    • 令 result 为 Call(exoticToPrim, input, « hint »)

    • 假如 result 类型不是 Object ,直接返回 result

    • 抛出 TypeError 异常

  6. 假如 hint 是 “default” ,令 hint 为 “number”

  7. 返回 OrdinaryToPrimitive(input, hint) 的值

抽象操作 OrdinaryToPrimitive(O, hint) 执行的顺序如下:

  • 断言:O 的类型是 Object

  • 断言:hint 的类型必须是 String ,且字符串内容只能是 “number” 和 "string"

  • 假如 hint = “string” ,则

    令 methodNames 为 « “toString”, “valueOf” »

  • 假如 hint = “number” ,则

    令 methodNames 为 « “valueOf”, “toString” »

  • 对于 methodNames 每一个 name 依次执行如下操作:

    • 令 method 为 Get(O, name)

    • 假如 IsCallable(method) 返回 true,则继续执行:

      • 令 result 为 Call(method, O)

      • 如果 result 的类型不是 Object ,则返回 result

  • 返回 TypeError 异常

看起来上面的过程很复杂,但实际上有用的关键信息如下:

  • 假如传入的 hint 是String ,先判断 toString 能否调用,再判断 toString() 的结果,是基本类型才返回,再判断 valueOf 能否调用,再判断 valueOf() 的结果,是基本类型才返回,否则报错。

  • 假如传入的 hint 是 Number(或者没有 hint ,默认是 Number ),先判断 valueOf ,再判断 toString

  • 对于普通 Object,默认用 hint 为 Number 的方式来转换,对于 Date 类型的 Object ,用 hint 为 String 的方式来转换

3. 其他

上面提到的几个核心的接口 ToNumberToStringToPrimitive ,也是 JavaScript 隐式装箱 (隐式类型转换)操作的核心接口。

在分析如下的题目中:

[]+[]、{}+{}、[]+{}、{}+[]

这其实是 JavaScript 中加法运算相关的逻辑 12.8 Additive Operators

AdditiveExpression : AdditiveExpression + MultiplicativeExpression

  1. 把AdditiveExpression的result赋值给lref

  2. 把GetValue(lref)的结果赋值给lval

  3. 把MultiplicativeExpression的result赋值给rref

  4. 把GetValue(rref)的结果赋值给rval

  5. 把ToPrimitive(lval)的结果赋值给lprim

  6. 把ToPrimitive(lval)的结果赋值给rprim

  7. 如果Type(lprim)和Type(rprim)中有一个是String,则a.把ToString(lprim)的结果赋给lstrb.把ToString(rprim)的结果赋给rstrc.返回lstr和rstr拼接的字符串

  8. 把ToNumber(lprim)的结果赋给lnum

  9. 把ToNumber(rprim)的结果赋给rnum

  10. 返回lnum和rnum相加的数值

4. 参考

  • ecma-262 7.0

  • js隐式装箱-ToPrimitive

  • Javascript原始值


微信公众号「何乐不为呢」,一个码农的技术笔记和唠叨。

JavaScript 中 == 和 === 的区别相关推荐

  1. Javascript中 != 和 !==的区别

    Javascript中 != 和 !==的区别 开发工具与关键技术:javascript 作者:沈金凤 年级:18级(5)班 撰写日期:2019年1月18日 1.!= 会转换成相同类型 进行比较,!= ...

  2. JavaScript学习总结(五)——Javascript中==和===的区别

    一.JavaScript"=="的作用 当==两边的内容是字符串时,则比较字符串的内容是否相等. 当==两边的内容是数字时,则比较数字的大小是否相等. 当==两边的内容是对象或者是 ...

  3. Javascript中“==”与“===”的区别

    在Javascript中有"=="和"==="两种比较运行符,那么他们有什么区别呢? 一.对于string,number等基础类型,==和===是有区别的 1) ...

  4. javascript中==和===的区别

    我们在使用javascript时总要使用判断两个变量是否相等来执行之后的一些操作,我一开始学习javascript的时候,还没有===这个概念,只知道使用==来判断两个变量是否相等,然而在学习过程中发 ...

  5. javascript中==与===的区别

    1.对于基础类型字符串和数字类型来说. a.同类型 ==与===没有区别,都是进行'值'比较 b.不同类型 ==会转换成同一类型的值,然后进行值比较,===会因为类型不同而返回false 2.对于高级 ...

  6. js(小程序或JavaScript)中[]、{}、()区别

    一.{ } 大括号,表示定义一个对象,大部分情况下要有成对的属性和值,或是函数体 {}表示对象.[]表示对象的属性.方法,()如果用在方法名后面,代表调用 如:var LangShen = {&quo ...

  7. 在javascript中==和===的区别

    ==用于一般比较,===用于严格比较,==在比较的时候可以转换数据类型,===严格比较,只要类型不匹配就返回flase.举例说明:"1" == true类型不同,"==& ...

  8. JS中 ?? 与 || 的区别

    JavaScript 中 ?? 与 || 的区别 相同点 用法相同,都是前后是值,中间用符号连接.根据前面的值来判断最终返回前面的值还是后面的值. 值1 ?? 值2 值1 || 值2 不同点 判断方式 ...

  9. JavaScript中的数组与伪数组的区别

    在JavaScript中,除了5种原始数据类型之外,其他所有的都是对象,包括函数(Function). 基本数据类型:String,boolean,Number,Undefined, Null 引用数 ...

最新文章

  1. 深圳杯---无线回传拓扑规划
  2. xshell进行ssh链接报错“所选的用户密钥未在远程主机上注册”处理
  3. 基于算法的建模---分形几何方法
  4. 关于android工程添加support-v7包的问题
  5. 【资源】媲美搜索神器everything,超过1秒出结果算我输!
  6. SpringMVC的数据响应-回写数据-直接回写字符串(应用)
  7. OSPF 邻接关系建立
  8. kaggle数据集_Kaggle上有170万份ArXiv文章的数据集
  9. python读取配置文件获取所有键值对_python ConfigParser模块读写配置文件
  10. 电子产品设计emc风险评估_书籍介绍:EMC设计方法与风险评估技术
  11. go语言 panic
  12. php网站iis7.5 session,IIS 7.5 asp Session超时时间设置方法
  13. ffmpeg 截图太模糊了_通过ffmpeg实现视频流截图
  14. 保姆级的接口自动化教程,不会写代码也能2小时学会
  15. matplotlib堆积图
  16. Neo4j【有与无】【N6】Graph数据库内部
  17. Latex: 表格内换行
  18. 惠普HPE服务器升级iLO4固件版本
  19. HSI、HSV、RGB、CMY、CMYK、HSL、HSB、Ycc、XYZ、Lab、YUV颜色模型
  20. OpenCV学习心得:Scalar()的几种使用方法

热门文章

  1. 出现Only the original thread that created a view hierarchy can touch its views.的错误
  2. 【日拱一卒行而不辍20220921】自制操作系统
  3. ambarella misc
  4. 学习python-day01-13---转自Python分布式爬虫打造搜索引擎Scrapy精讲
  5. 数据中台的数据仓库和商业智能BI的数据仓库有什么区别?
  6. 图论最短路 之 弗洛伊德Floyd(详细分析)
  7. 数位DP(期末机测题)
  8. 2. 表的操作:创建表、修改表、列约束和表约束、数据操作、删除表
  9. 浏览器字体和html字体,如何正确设置兼容浏览器的中文字体
  10. 超高精度时间频率同步及其应用