Javascript 中 == 和 === 区别是什么?
Javascript 中 == 和 === 区别是什么?
链接:https://www.zhihu.com/question/31442029/answer/77772323
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
var x = 1;
var obj = {valueOf: function(){ x = 2; return 0 }}
console.log(obj == 0, x) // true, 2
甚至还会产生异常呢
var x = 1;
var obj = {valueOf: function(){ return {} }, toString: function(){ return {}}}
console.log(obj == 0) // Error: Cannot convert object to primitive value
————————————————————————————————————————————
这是 ==<img src="https://pic1.zhimg.com/50/41b28b0c6def1909e0ad1da86093e620_hd.png" data-rawwidth="1424" data-rawheight="1417" class="origin_image zh-lightbox-thumb" width="1424" data-original="https://pic1.zhimg.com/41b28b0c6def1909e0ad1da86093e620_r.png">
这是 ===<img src="https://pic4.zhimg.com/50/eb4c38443744d02015ddbe9bfd775eb3_hd.png" data-rawwidth="1415" data-rawheight="1410" class="origin_image zh-lightbox-thumb" width="1415" data-original="https://pic4.zhimg.com/eb4c38443744d02015ddbe9bfd775eb3_r.png">
完整比较图:
- 红色:===
- 橙色:==
- 黄色:<= 和 >= 同时成立,== 不成立
- 蓝色:只有 >=
- 绿色:只有 <=
&amp;amp;amp;lt;img src="https://pic3.zhimg.com/50/b922270259dece707ef6c6a50259a406_hd.png" data-rawwidth="1420" data-rawheight="1418" class="origin_image zh-lightbox-thumb" width="1420" data-original="https://pic3.zhimg.com/b922270259dece707ef6c6a50259a406_r.png"&amp;amp;amp;gt;
(参:JS Comparison Table, Edit fiddle - JSFiddle)
顺便,有人说这图像现代艺术,感觉旋转完更像,删减重排换成 S1 配色之后做成头像了。
不免费帮人做头像(也就是换个顺序的事情)
&amp;amp;amp;lt;img src="https://pic3.zhimg.com/50/27a6f55c41ace25f058625b3073ad492_hd.png" data-rawwidth="1600" data-rawheight="1600" class="origin_image zh-lightbox-thumb" width="1600" data-original="https://pic3.zhimg.com/27a6f55c41ace25f058625b3073ad492_r.png"&amp;amp;amp;gt;
链接:https://www.zhihu.com/question/20348948/answer/19601270
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
例如 Google v8 就是开源的,只要看看里面的代码,就能知道到底是怎么一回事了。但是我相信绝大多数人都是没有这个耐心的——我知道大家都在忙着挣钱养家——好吧,那咱就不看 Google v8 了,看看 ECMA-262 吧,里面已经把实现算法描述得很清楚了,只要看懂了下面的内容,以后就再也不会有此类疑问了。
=== 被称为 Strict Equals Operator,假设有表达式 a === b,则它的实际运算过程如下
- 计算出表达式 a 的结果,并存入 lref 变量
- 将 GetValue(lref) 的结果存入 lval 变量
- 计算出表达式 b 的结果,并存入 rref 变量
- 将 GetValue(rref) 的结果存入 rval 变量
- 执行 Strict Equality Comparison 算法判断 rval === lval 并将结果直接返回
这里的 Strict Equality Comparison 算法很关键,假设要计算的是 x === y,则过程如下
1. 如果 Type(x) 和 Type(y) 不同,返回 false
2. 如果 Type(x) 为 Undefined,返回 true
3. 如果 Type(x) 为 Null,返回 true
4. 如果 Type(x) 为 Number,则进入下面的判断逻辑
4.1. 如果 x 为 NaN,返回 false
4.2. 如果 y 为 NaN,返回 false
4.3. 如果 x 的数字值和 y 相等,返回 true
4.4. 如果 x 是 +0 且 y 是 -0,返回 true
4.5. 如果 x 是 -0 且 y 是 +0,返回 ture
4.6. 返回 false
5. 如果 Type(x) 为 String,则当且仅当 x 与 y 的字符序列完全相同(长度相等,每个位置上的字符相同)时返回 true,否则返回 false
6. 如果 Type(x) 为 Boolean,则若 x 与 y 同为 true 或同为 false 时返回 true,否则返回 false
7. 如果 x 和 y 引用的是同一个对象,返回 true,否则返回 false
二、相等运算符 == 的实现
好了,当你明白了 === 的实现之后,我们再来看看 == 的实现,比较一下他们有何不同?
== 被称为 Equals Operator (注意看没有 Strict 了),假设有表达式 a == b,则它的实际运算过程如下
- 计算出表达式 a 的结果,并存入 lref 变量
- 将 GetValue(lref) 的结果存入 lval 变量
- 计算出表达式 b 的结果,并存入 rref 变量
- 将 GetValue(rref) 的结果存入 rval 变量
- 执行 Abstract Equality Comparison 算法判断 rval == lval 并将结果直接返回
注意,其中的前 4 个步骤是和 === 完全相同的。唯独 5 不同。对于 === 来说,调用的是 Strict Equality Comparison 算法,但是 == 则调用的是 Abstract Equality Comparison 算法。虽然仅一词之差,但是却有质的不同,我们下面就来看看到底它是怎么实现的
假设要计算的是 x == y,Abstract Equality Comparison 计算的过程如下(很冗长,但是每个步骤都很简单)
1. 如果 Type(x) 和 Type(y) 相同,则
1.1. 如果 Type(x) 为 Undefined,返回 true
1.2. 如果 Type(x) 为 Null,返回 true
1.3. 如果 Type(x) 为 Number,则
1.3.1. 如果 x 是 NaN,返回 false
1.3.2. 如果 y 是 NaN,返回 false
1.3.3. 如果 x 的数值与 y 相同,返回 true
1.3.4. 如果 x 是 +0 且 y 是 -0,返回 true
1.3.5. 如果 x 是 -0 且 y 是 +0,返回 true
1.3.6. 返回 false
1.4. 如果 Type(x) 为 String,则当且仅当 x 与 y 的字符序列完全相同(长度相等,每个位置上的字符相同)时返回 true,否则返回 false
1.5. 如果 Type(x) 为 Boolean,则若 x 与 y 同为 true 或同为 false 时返回 true,否则返回 false
1.6. 如果 x 和 y 引用的是同一个对象,返回 true,否则返回 false
2. 如果 x 是 null 且 y 是 undefined,返回 true
3. 如果 x 是 undefined 且 y 是 null,返回 ture
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) 的比较结果作为返回值
8. 如果 Type(x) 为 String 或 Number 且 Type(y) 为 Object,以 x == ToPrimitive(y) 的比较结果作为返回值
9. 如果 Type(x) 为 Object 且 Type(y) 为 String 或 Number,以 ToPrimitive(x) == y 的比较结果作为返回值
10. 返回 false
三、总结
从上面的算法流程可以看出,a === b 是最简单的。如果 a 和 b 的类型不同,那么一定会返回 false。而 a == b 则要灵活得多。JavaScript 会尝试调整 a 和 b 的类型,例如若 a 为字符串 b 为数字,则将字符串先转化为数字再与 b 比较,等等。这在很多时候简化了程序员的代码量。
一些程序员害怕 == 而提倡使用 === 的根本原因是,他们不知道在 == 的内部具体发生了什么。而这就导致误用和出错。
很多人认为,当我们不了解一个 feature 的时候,我们就不用它就行了。这也许是一种习惯性逻辑。但尴尬的是你不用这个 feature 没问题,别人却可能还是会用。为了能读懂别人的代码,我们又必须实际上搞清楚这内在的逻辑。所以最终还是得付出这方面的学习成本。
"==="叫做严格运算符,"=="叫做相等运算符。
严格运算符的运算规则如下,
(1)不同类型值
如果两个值的类型不同,直接返回false。
(2)同一类的原始类型值
同一类型的原始类型的值(数值、字符串、布尔值)比较时,值相同就返回true,值不同就返回false。
(3)同一类的复合类型值
两个复合类型(对象、数组、函数)的数据比较时,不是比较它们的值是否相等,而是比较它们是否指向同一个对象。
(4)undefined和null
undefined 和 null 与自身严格相等。
null === null //true
undefined === undefined //true
相等运算符在比较相同类型的数据时,与严格相等运算符完全一样。
在比较不同类型的数据时,相等运算符会先将数据进行类型转换,然后再用严格相等运算符比较。类型转换规则如下:
(1)原始类型的值
原始类型的数据会转换成数值类型再进行比较。字符串和布尔值都会转换成数值,所以题主的问题中会有第二个string输出。
(2)对象与原始类型值比较
对象(这里指广义的对象,包括数值和函数)与原始类型的值比较时,对象转化成原始类型的值,再进行比较。
(3)undefined和null
undefined和null与其他类型的值比较时,结果都为false,它们互相比较时结果为true。
(4)相等运算符的缺点
相等运算符隐藏的类型转换,会带来一些违反直觉的结果。
'' == '0' // false
0 == '' // true
0 == '0' // true
false == 'false' // false
false == '0' // true
false == undefined // false
false == null // false
null == undefined // true
' \t\r\n ' == 0 // true
这就是为什么建议尽量不要使用相等运算符。
至于使用相等运算符会不会对后续代码造成意外影响,答案是有可能会。
var a = undefined;
if(!a){
console.log("1"); //1
}
var a = undefined;
if(a == null){
console.log("1"); //1
}
var a = undefined;
if(a === null){
console.log("1"); //无输出
}
也就是说当a为undefined时,输出的值会有变化,而在编程中对象变成undefined实在是太常见了。
<canvas id="drawCanvas" width="1600" height="1600" style="height:800px;width:800px"/>
var cmp = function(v1, v2) { return v1 == v2; };
var vals = [
["null", function() { return null; }],
["undefined", function() { return undefined; }],
["false", function() { return false; }],
['"false"', function() { return "false"; }],
["Boolean(false)", function() { return new Boolean(false); }],
["[]", function() { return []; }],
["[[]]", function() { return [[]]; }],
['""', function() { return ""; }],
['String("")',function(){return new String('')}],
["0", function() { return 0; }],
["Number(0)",function(){return new Number(0)}],
['"0"', function() { return "0"; }],
['String("0")',function(){return new String('0')}],
["[0]", function() { return [0]; }],
["true", function() { return true; }],
['"true"', function() { return "true"; }],
["Boolean(true)", function() { return new Boolean(true); }],
["1",function() { return 1; }],
["Number(1)",function(){return new Number(1)}],
['"1"', function() { return "1"; }],
['String("1")',function(){return new String('1')}],
["[1]", function() { return [1]; }],
["-1", function() { return -1; }],
["Number(-1)",function(){return new Number(-1)}],
['"-1"', function() { return "-1"; }],
['String("-1")',function(){return new String('-1')}],
["[-1]", function() { return [-1]; }],
["Infinity", function() { return Infinity; }],
["-Infinity", function() { return -Infinity; }],
["{}", function() { return {}; }],
["NaN", function() { return NaN; }]
];
var canvas = document.getElementById("drawCanvas");
var ctx = canvas.getContext("2d");
var n = vals.length;
var r = 40; // diameter of grid squares
var p = 160; // padding space for labels
// color grid cells
for (var i = 0; i < n; i++) {
var v1 = vals[i][1]();
for (var j = 0; j < n; j++) {
var v2 = vals[j][1]();
var eq = cmp(v1, v2);
ctx.fillStyle = eq ? "orange" : "white";
ctx.fillRect(p+i*r,p+j*r,r,r);
}
}
// draw labels
ctx.fillStyle = "black";
var f = 24;
ctx.font = f + "px Helvetica";
for (var i = 0; i < n; i++) {
var s = vals[i][0];
var w = ctx.measureText(s).width;
ctx.save();
ctx.translate(p+i*r+r/2-f*0.4,p-w-2);
ctx.rotate(3.14159/2);
ctx.fillText(s, 0, 0);
ctx.restore();
}
for (var i = 0; i < n; i++) {
var s = vals[i][0];
var w = ctx.measureText(s).width;
ctx.fillText(s, p-w-2, p+i*r+r/2+f*0.4);
}
// draw grid lines
ctx.beginPath();
ctx.strokeStyle = "black";
for (var i = 0; i <= n; i++) {
ctx.moveTo(p+r*i, p);
ctx.lineTo(p+r*i, p+r*n);
ctx.moveTo(p, p+r*i);
ctx.lineTo(p+r*n, p+r*i);
}
ctx.stroke();
Javascript 中 == 和 === 区别是什么?的更多相关文章
- javascript中的Array对象 —— 数组的合并、转换、迭代、排序、堆栈
Array 是javascript中经常用到的数据类型.javascript 的数组其他语言中数组的最大的区别是其每个数组项都可以保存任何类型的数据.本文主要讨论javascript中数组的声明.转换 ...
- JavaScript 中的数据类型
Javascript中的数据类型有以下几种情况: 基本类型:string,number,boolean 特殊类型:undefined,null 引用类型:Object,Function,Date,Ar ...
- 掌握javascript中的最基础数据结构-----数组
这是一篇<数据结构与算法javascript描述>的读书笔记.主要梳理了关于数组的知识.部分内容及源码来自原作. 书中第一章介绍了如何配置javascript运行环境:javascript ...
- Javascript中的valueOf与toString
基本上,javascript中所有数据类型都拥有valueOf和toString这两个方法,null除外.它们俩解决javascript值运算与显示的问题,本文将详细介绍,有需要的朋友可以参考下. t ...
- JavaScript中数据类型转换总结
JavaScript中数据类型转换总结 在js中,数据类型转换分为显式数据类型转换和隐式数据类型转换. 1, 显式数据类型转换 a:转数字: 1)Number转换: 代码: var a = " ...
- JavaScript中‘this’关键词的优雅解释
本文转载自:众成翻译 译者:MinweiShen 链接:http://www.zcfy.cc/article/901 原文:https://rainsoft.io/gentle-explanation ...
- javascript中的原型和继承
javascript一直是初学者口中的难点,甚至一些有些许工作经验的人也不太明白其中的原理,而我就是那个初学者,前段时间在阮一峰老师的博客上看了一篇文章<javascript继承机制的设计思想& ...
- 深入浅出 妙用Javascript中apply、call、bind
apply.call 在 javascript 中,call 和 apply 都是为了改变某个函数运行时的上下文(context)而存在的,换句话说,就是为了改变函数体内部 this 的指向. Jav ...
- 【优雅代码】深入浅出 妙用Javascript中apply、call、bind
这篇文章实在是很难下笔,因为网上相关文章不胜枚举. 巧合的是前些天看到阮老师的一篇文章的一句话: “对我来说,博客首先是一种知识管理工具,其次才是传播工具.我的技术文章,主要用来整理我还不懂的知识.我 ...
随机推荐
- 成都Uber优步司机奖励政策(1月14日)
滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/不用抢单:http://www.cnblogs.com/mfry ...
- (转)Html邮件CSS指南
转载地址:http://www.maildesign.cn/archives/937 分享来自Campaignmonitor非常实用的Html邮件中CSS的支持文档! 他们总结的Html邮件的CSS指 ...
- 几个常用的轻量级web服务
Node.js 安装:npm install http-server 使用:hs命令,可启动以当前目前为webroot的8080端口web服务,也可指定端口 Python 安装:内置 使用:pytho ...
- 理解Python的装饰器
看Flask文档时候看到关于cache的装饰器,有这么一段代码: def cached(timeout=5 * 60, key=’view/%s’): def decorator(f): @wraps ...
- 多台服务器下同步文件夹数据(rsync+inotify)
网上有很多讲解rsync+inotify的教程,我就先贴出一个来大家去看吧,基本都是类似的. http://www.jb51.net/article/57011.htm 我就强调几点,按照上面的方法配 ...
- wpf基础使用_修改窗体图标
废话不多说,直接开始修改图标步骤: 当然直接使用绝对路径添加图标也是可以的,这种方式不可取,一旦图标移动位置或被删除,就会导致找不到图标文件报错,这里我们介绍的是另一个方式,使用资源文件的方式添加 1 ...
- 关于excle导数据的一些代码笔记
package com.bonc.util; import java.io.File; import java.io.FileInputStream; import java.io.FileOutpu ...
- [2017 - 2018 ACL] 对话系统论文研究点整理
(论文编号及摘要见 [2017 ACL] 对话系统. [2018 ACL Long] 对话系统. 论文标题[]中最后的数字表示截止2019.1.21 google被引次数) 1. Domain Ada ...
- 吴恩达深度学习 反向传播(Back Propagation)公式推导技巧
由于之前看的深度学习的知识都比较零散,补一下吴老师的课程希望能对这块有一个比较完整的认识.课程分为5个部分(粗体部分为已经看过的): 神经网络和深度学习 改善深层神经网络:超参数调试.正则化以及优化 ...
- CryptoZombies学习笔记——Lesson1
CryptoZombies是一个学习以太坊开发的平台,我将在这里记录学习过程中的一些笔记. 课程网址:cryptozombies.io 首先是第一课——Lesson1:Making the Zombi ...