Java Script 中 ==(Equal) 和 === (Identity Equal) 的区别和比较算法逻辑
判断两个变量是否相等在任何编程语言中都是非常重要的功能。
JavaScript 提供了 == 和 === 两种判断两个变量是否相等的运算符,但我们开始学习的时候 JavaScript 的时候,就被一遍又一遍的告知:
- === 要求变量的类型和值均相等,才能返回true。
- 使用 === 来避免因JavaScript 类型转换带来的问题。
这样增加了 JavaScript 语法的灵活性但是也带来很多头疼的问题:
- 使用 ==/!=是 ===/!== 来判断两个变量是否相等?
- 为什么,JS 编码推荐使用 ===/!= 而不是 ==/!=,大部分的编程语言不都是使用==/!=么?
为了要回答这个问题,让我们看一下 JavaScript 所遵守的标准 ECMAScript 对于==和 === 是怎么描述的吧!
=== 详解
Identity Equal或 Strict Equal, 在 ECMAScript -- Java Script 所遵守的标准中,算法的定义为:The Strict Equality Comparison Algorithm, 规则如下:
- 如果 参数 x 的数据类型和 参数 y 的数据类型不一致,这返回 false
- 如果 参数 x 的数据类型为 undenfined, 则返回 true
- 如果 参数 x 的数据类型为 null, 则返回 true
- 如果 参数 x 的数据类型为 Number, 则:
- 如果 x 是 NaN 返回 false
- 如果 y 是 NaN 返回 false
- 如果 x 是 +0 并且 y 为 -0, 返回 true
- 如果 x 是 -0 并且 y 为 +0, 返回 true
- 如果 x 和 y 有着相同的数值,返回 true
- 返回 false
- 如果 x 的类型为 String, 且 x 与 y 有着相同的顺序排列的字符串, 返回 true
- 如果 x 的类型为 boolean, 且 x 与 y 拥有相同的布尔值,返回 true
- 如果 x 的类型为 Object, 且 x 与 y 指向相同的对象,返回 true
伪代码:
function strictEqual(x, y) {
// If Type(x) is different from Type(y), return false.
if (!valueEqual(typeof (x), typeof (y))) {
return false;
}
// If Type(x) is Undefined, return true.
// If Type(x) is Null, return true.
if (valueEqual(typeof (x), "undefined") || valueEqual(x, null)) {
return true;
}
if (valueEqual(typeof (x), "number")) {
// If x is NaN, return false.
if (isNaN(x)) {
return false;
}
// If y is NaN, return false.
if (isNaN(y)) {
return false;
}
// If x is +0 and y is −0, return true.
if (valueEqual(x, +0) && valueEqual(y, -0)) {
return true;
}
// If x is −0 and y is +0, return true.
if (valueEqual(y, +0) && valueEqual(x, -0)) {
return true;
}
// If x is the same Number value as y, return true.
if (valueEqual(x, y)) {
return true;
}
return false;
}
if (valueEqual(typeof (x), "string")) {
// If Type(x) is String, then return true if x and y are exactly
// the same sequence of characters
// (same length and same characters in corresponding positions); otherwise, return false.
return hasSameChar(x, y);
}
if (valueEqual(typeof (x), "boolean")) {
return valueEqual(x, y);
}
if (valueEqual(typeof (x), "object")) {
// Return true if x and y refer to the same object. Otherwise, return false.
return hasSameReference(x, y);
}
return false;
}
逻辑图:

== 详解
Equal, 在两个对比变量数据类型相同时, 和=== 有着一样的行为算法实现,但是当两个对比的变量数据类型不同时,ECMAScript/JavaScript 有着自定义的转换和比较逻辑:参考 The Abstract Equality Comparison Algorithm
- 如果 x 为 null, 且 y 为 undefined, 返回 true
- 如果 x 为 undefined, 且 y 为 null, 返回 true
- 如果 x 的数据类型为 Number, 且 y 的数据类型为 string, 则将 y 转换为 Number,然后进行比较
- 如果 x 的数据类型为 String, 且 y 的数据类型为 Number, 则将 x 转换为 Number,然后进行比较
- 如果 x 的数据类型为 Boolean, 将x 转换为数字类型,当 x 为 true 时转换为 1, 否则转换为 0 进行比较
- 如果 y 的数据类型为 Boolean, 将 y 转换为数字类型,当 y 为 true 时转换为 1, 否则转换为 0 进行比较
- 如果 x 的数据类型为 String 或者 Number, 且 y 为 Object, 则使用 valueOf 函数,将 y 转换为简单类型进行比较
- 如果 y 的数据类型为 String 或者 Number, 且 x 为 Object, 则使用 valueOf 函数,将 x 转换为简单类型进行比较
- 返回 false
从上述定义不难总结出以下几点:
- 该算法为递归算法,转换后,继续调用其自身直到能比较且返回为止
- 该算法依赖于 Strict Equal 的实现
- 进行转换时,具体转换依赖于数据类型的定义的方法,如Number() 函数
伪代码:
function abstractEqual(x, y) {
// if x and y has same type
if (valueEqual(typeof (x), typeof (y))) {
return strictEqual(x, y);
}
// If x is null and y is undefined, return true.
if (valueEqual(x, null) && valueEqual(y, undefined)) {
return true;
}
// If x is undefined and y is null, return true.
if (valueEqual(x, undefined) && valueEqual(y, null)) {
return true;
}
// Type(x) is Number and Type(y) is String,
if (valueEqual(typeof (x), "number") && valueEqual(typeof (y), "string")) {
var convertedY = Number(y);
// return the result of the comparison x == ToNumber(y)
return abstractEqual(x, convertedY);
}
// Type(x) is Number and Type(y) is String,
if (valueEqual(typeof (x), "string") && valueEqual(typeof (y), "number")) {
var convertedX = Number(x);
// return the result of the comparison x == ToNumber(y)
return abstractEqual(convertedX, y);
}
// Type(x) is Boolean, return the result of the comparison ToNumber(x) == y.
if (valueEqual(typeof (x), "boolean")) {
var convertedToIntX = Number(x);
return abstractEqual(convertedToIntX, y);
}
// Type(x) is Boolean
if (valueEqual(typeof (y), "boolean")) {
var convertedToIntY = Number(y);
// return the result of the comparison ToNumber(x) == y.
return abstractEqual(x, convertedToIntY);
}
// If Type(x) is either String or Number and Type(y) is Object,
if ((valueEqual(typeof (x), "string") || valueEqual(typeof (x), "number")) && valueEqual(typeof (y), "object")) {
var toPrimitiveY = y.valueOf();
// return the result of the comparison x == ToPrimitive(y).
return abstractEqual(x, toPrimitiveY);
}
// If Type(x) is either String or Number and Type(y) is Object,
if ((valueEqual(typeof (y), "string") || valueEqual(typeof (y), "number")) && valueEqual(typeof (x), "object")) {
var toPrimitiveX = x.valueOf();
// return the result of the comparison x == ToPrimitive(y).
return abstractEqual(toPrimitiveX, y);
}
return false;
}
逻辑图:

附加上本例使用的判断相等的函数的代码,直接使用了 JavaScript 的 == 来实现,为了 demo 么!呵呵,这是一个很号的接口,实际上,我也实现不出来 :).
function valueEqual(x, y) {
return x === y;
}
function hasSameChar(x, y) {
return x === y;
}
function hasSameReference(x, y) {
return x === y;
}
总结
现在,我们已经知道 == 和 === 在判断两个变量是否相等时所使用的算法的基本实现。帮助我们理解一些 JavaScript 中判断相等时一些"诡异“ 的行为。
把我们写的 Script 放在一个 HTML 文件里,用 Chrome 代开,按 F12, 开始我们的调试吧:
| 测试 JS 代码 | 运行结果 | JS 代码 | 运行结果 | 备注 |
| var x = 1, y = "1";console.log(strictEqual(x,y)); console.log(abstractEqual(x,y)) | false, true | var x = 1, y = "1";console.log(x === y); console.log(x == y) | false,true | == 时,y 先转换为数字类型1 |
| var x = 1, y = "not a number";console.log(strictEqual(x,y)); console.log(abstractEqual(x,y)) | false, falase | var x = 1, y = "not a number";console.log(x === y); console.log(x == y) | false, false | y 转换为数字类型失败,返回 NaN,NaN 不与任何值相等,包括 NaN 自身 |
| var x = undefined, y = null;console.log(strictEqual(x,y)); console.log(abstractEqual(x,y)) | false,true | var x = undefined, y = null;console.log(x===y); console.log(x == y) |
false,true |
=== 时, null != undefined == 时,规定了 null 与 undefined 的相等 |
| var x = true, y = 2;console.log(strictEqual(x,y)); console.log(abstractEqual(x,y)) | false,false | var x = true, y = 2;console.log(x === y); console.log(x == y) |
false,false |
true 转换为数字 1 |
| var x = false, y = 0;console.log(strictEqual(x,y)); console.log(abstractEqual(x,y)) | false,true | var x = false, y = 0;console.log(x === y); console.log(x == y) |
false,true |
false 转换为数字 0 |
| var x = {name:'test',valueOf:function(){return 1;}},y = 1; console.log(strictEqual(x,y));console.log(abstractEqual(x,y)); | false,true | var x = {name:'test',valueOf:function(){return 1;}},y = 1; console.log(x === y);console.log(x == y); |
false,true |
x.valueOf() 返回数字 1,与 y 相等 |
Java Script 中 ==(Equal) 和 === (Identity Equal) 的区别和比较算法逻辑的更多相关文章
- Java script 中的面向对象1
Java script 中的面向对象 对象 对象是Javascript的基本数据类型,对象是一种复合值,将很多的键值对聚合在一起使用.对象可看做是属性的无序集合,每个属性都是一个名/值对.属性名其实是 ...
- Java线程中yield与join方法的区别
长期以来,多线程问题颇为受到面试官的青睐.虽然我个人认为我们当中很少有人能真正获得机会开发复杂的多线程应用(在过去的七年中,我得到了一个机会),但是理解多线程对增加你的信心很有用.之前,我讨论了一个w ...
- Java线程中run和start方法的区别
http://bbs.csdn.net/topics/350206340 Thread类中run()和start()方法的区别如下:run()方法:在本线程内调用该Runnable对象的run()方法 ...
- Java Web 中 过滤器与拦截器的区别
过滤器,是在java web中,你传入的request,response提前过滤掉一些信息,或者提前设置一些参数,然后再传入servlet或者struts的 action进行业务逻辑,比如过滤掉非法u ...
- java Script 中的keyCode 和charCode
其实很长一段时间,我都没有完全弄明白keyCode 和charCode ,自己也认真看过,但是就是理解不透彻,为了防止以后再出现混乱,写篇博客记录一下吧! 首先 在不同的浏览器中,他们有不同的说法哦 ...
- Java Script中常见操作
字符串常见操作:obj.length 长度obj.trim() 移除空白obj.trimLeft()obj.trimRight)obj.charAt(n) 返回字符串中的第n个字符obj.concat ...
- Java -- Thread中start和run方法的区别
一.认识Thread的 start() 和 run() 1.start(): 我们先来看看API中对于该方法的介绍: 使该线程开始执行:Java 虚拟机调用该线程的 run 方法. 结果是两个线程并发 ...
- JAVA多线程中start方法与run方法区别
start()方法告诉jvm该线程准备运行,jvm通过调用任务的run()方法执行任务. 一个任务类必须实现Runnable接口,而任务必须从线程运行. 实现Runnable接口后必须重写run()方 ...
- Java框架中Struts和Struts2框架的区别
struts1 与 struts2 的区别:1.都是 MVC 的 WEB 框架,2 struts1的老牌框架,应用很广泛,有很好的群众基础,使用它开发风险很小,成本更低!struts2虽然基于这个框架 ...
随机推荐
- 处理事件的方式:两种类的覆盖处理(自己管理,覆盖专用事件函数;自己统一管理,覆盖QWidget::Event通用函数),一种对象的处理(父控件统一管理,即安装过滤器),两种全局处理(QCoreApplication安装过滤器;覆盖notify方法)
虽然只有一句话,但却是我自己的心得. 特别注意,bool QCoreApplication::notify(QObject *receiver, QEvent *event) 明确指明了要发送的对象, ...
- SQL Server索引语法 <第四篇>
从CREATE开始 通过显式的CREATE INDEX命令 在创建约束时作为隐含的对象 随约束创建的隐含索引 当向表中添加如下两种约束之一时,就会创建隐含索引. 主键约束(聚集索引) 唯一约束(唯一索 ...
- ubuntu下获取相应的内核源码
一直以为是apt-get install ,apt-get search 也搜索不到相关的包,结果不是. 其实是 apt-get source linux-image-$(uname -r) 必须要用 ...
- tc令牌桶限速心得
一.实验拓扑与实验现象 实验拓扑如图所示,在①号机上发送数据,③号机上接受数据,同时在④号机的eth1与eth2网口限制速率为115200kbps,命令如下 tc qdisc add dev eth1 ...
- 重大新闻:借贷宝不用绑卡了,借贷宝APP推出肖像识别新功能!
动动手指,20元人民币立即到手:http://www.cnblogs.com/mfryf/p/4754384.html 滴滴打车烧钱十几个亿,狂送打车券,很多人天天免费坐车! 去年年初百度钱包注册奖励 ...
- Extjs实现进度条
做Extjs开发中,往往后台程序可能要执行一段时间才能得到返回结果,加入进度条可以提高客户体验度,以下为两种便捷的方式: 1.提交数据前用Ext.Msg.wait('提示','正在处理数据,请稍候') ...
- HDOJ 题目分类
HDOJ 题目分类 /* * 一:简单题 */ 1000: 入门用:1001: 用高斯求和公式要防溢出1004:1012:1013: 对9取余好了1017:1021:1027: ...
- Lazy方式单列模式,一种线程安全模式的新选择
public class WeProxyClient { private static readonly Lazy<WeProxyClient> list = new ...
- Js apply 方法 具体解释
Js apply方法具体解释 我在一開始看到javascript的函数apply和call时,很的模糊,看也看不懂,近期在网上看到一些文章对apply方法和call的一些演示样例,总算是看的有点眉目了 ...
- 自学HTML的几个例子
此处不赘述HTML中不同标签的用法仅仅给出自己学习时写的一些自娱自乐的例子,具体标签用法请参考W3C(http://www.w3school.com.cn/),毕竟这个才是最靠谱的,请不要相信任何二道 ...