概述
本文主要讲解JavaScript中的三种相等运算:==,===和Object.is()。通过对比和例子,加深大家的印象,并就个别例子进行详细说明。
预备知识ECMAScript7规范中的ToPrimitive抽象操作
===运算符对于x === y,该运算符的比较步骤如下:
如果x的类型和y的类型不一样,返回false;
如果x的类型是数字,那么:
如果x是NaN,返回false;
如果y是NaN,返回false;
如果x和y是同一个数字值,返回true;
如果x是+0,y是-0,返回true;
如果x是-0,y是+0,返回true;
返回false;
返回SameValueNonNumber(x, y)的结果。
SameValueNonNumber(x, y)抽象操作比较两个非数字并且同类型的x和y是否相等,比较步骤如下:
如果x的类型是null或者undefined,返回true;
如果x是字符串类型,
如果x和y是完全相同的字符编码序列,返回true,否则返回false;
如果x是布尔类型,
如果x和y同为true或者false,返回true,否则返回false;
如果x是symbol类型,
如果x和y是相同的符号值,返回true,否则返回false;
如果x和y是同一个对象值,返回true,否则返回false。
需要注意的点是NaN,+0,-0:
NaN === NaN // false +0 === -0 // true -0 === +0 // true
这三个例子分别对应x === y比较步骤中的2.1,2.4和2.5。这三个例子的输出结果完全就是按照规范的定义得出的结果,没有为什么,规范就是这么定义的。至于说为什么规范要这样定义,可能就需要去问规范的制定者了,这个就不在本文的讨论范围之内了。
Object.is()对于Object.is(x, y),会使用抽象操作SameValue(x, y)进行比较,该抽象操作的步骤如下:
如果x的类型和y的类型不一样,返回false;
如果x的类型是数字,那么:
如果x和y都是NaN,返回true;
如果x是+0,y是-0,返回false;
如果x是-0,y是+0,返回false;
如果x和y是同一个数字值,返回true;
返回false;
返回SameValueNonNumber(x, y)的结果。
由此可见,===和Object.is()的区别在于对NaN和带符号的0的处理:
NaN === NaN // false +0 === -0 // true -0 === +0 // true Object.is(NaN, NaN) // true Object.is(+0, -0) // false Object.is(-0, +0) // false==运算符
对于x == y,该运算符的比较步骤如下:
如果x和y的类型相同,返回x === y的结果;
如果x是null,y是undefined,返回true;
如果x是undefined,y是null,返回true;
如果x的类型是数字,y的类型是字符串,返回x == ToNumber(y)的结果;
如果x的类型是字符串,y的类型是数字,返回ToNumber(x) == y的结果;
如果x的类型是布尔类型,返回ToNumber(x) == y的结果;
如果y的类型是布尔类型,返回x == ToNumber(y)的结果;
如果x的类型是字符串、数字或者Symbol中的一种,y的类型是对象,返回x == ToPrimitive(y)的结果;
如果x的类型是对象,y的类型是字符串、数字或者Symbol中的一种,返回ToPrimitive(x) == y的结果;
返回false。
上面用到了方法ToNumber,ToNumber(x)的步骤如下:
如果x的类型是Undefined,返回NaN;
如果x的类型是Null,返回+0;
如果x的类型是布尔类型,x为true返回1,false返回+0;
如果x的类型是数字,返回x;
如果x的类型是字符串,参考字符串转化为数字,本文不详细介绍这块内容;
如果x的类型是Symbol,返回NaN;
如果x的类型是对象,
让primValue的值是ToPrimitive(x, hint Number);
返回ToNumber(primValue)的结果;
[] == ![]上面讲了==运算符的比较步骤,下面我们讲一个例子加深下印象:
[] == ![]
首先左边的[]是一个空数组,类型是对象,右边是![],[]是一个真值,所以![]的结果是false:
[] == ![] // => [] == false
然后会走到x == y比较步骤的第7步,返回x == ToNumber(y)的结果,也就是:
[] == false // => [] == ToNumber(false)
由ToNumber(x)的第3步可知,ToNumber(false)返回+0:
[] == ToNumber(false) // => [] == +0
然后走到x == y比较步骤的第9步,返回ToPrimitive(x) == y的比较结果:
[] == +0 // => ToPrimitive([]) == +0
ToPrimitive([])的结果是空字符串"",原因请查看文章ECMAScript7规范中的ToPrimitive抽象操作。所以,上面等价于:
"" == +0
然后走到x == y比较步骤的第5步,返回ToNumber(x) == y的结果:
"" == +0 // => ToNumber("") == +0
由ToNumber操作的第5步可知,ToNumber("")的结果是+0,所以也就是:
+0 == +0 // true{} == !{}
首先,左边是{},类型是对象,右边是!{},{}是真值,所以!{}是false:
{} == !{} // => {} == false
然后同样会走到x == y比较步骤的第7步,返回x == ToNumber(y)的结果,也就是:
{} == false // => {} == ToNumber(false)
由ToNumber(x)的第3步可知,ToNumber(false)返回+0:
{} == ToNumber(false) // => {} == +0
然后走到x == y比较步骤的第9步,返回ToPrimitive(x) == y的比较结果:
{} == +0 // => ToPrimitive({}) == +0
ToPrimitive({})的结果是字符串"[object Object]",原因请查看文章ECMAScript7规范中的ToPrimitive抽象操作。所以,上面等价于:
"[object Object]" == +0
然后走到x == y比较步骤的第5步,返回ToNumber(x) == y的结果:
"[object Object]" == +0 // => ToNumber("[object Object]") == +0
由ToNumber操作的第5步可知,ToNumber("[object Object]")的结果是NaN,所以也就是:
NaN == +0 // false
所以,[] == ![]的结果是true,{} == !{}的结果是false。可能有人第一次看到[] == ![]的时候,觉得这个的比较结果怎么可能是true。我觉得有时候不要感性的去认识问题,按照规定的运算步骤走一遍,结果是什么就是什么。
如何证明x == y比较步骤的第4步和第5步?这个问题的详细描述是当==运算符两边分别是数字和字符串类型的时候,如何证明是如步骤4和5描述的那般把字符串转化成了数字,而不是把数字转化成了字符串?
答:思路是:是否存在这样一个例子,使得把数字转化为字符串后的比较结果是false,字符串转化为数字后的比较结果是true。是的,确实存在这样的例子:
+"3.0" // 3 => 3 == 3 => true "" + 3 // "3" => "3.0" == "3" // false "3.0" == 3 // true,证明是把字符串转化成了数字总结
本文讲解了JavaScript中的三种相等运算:==,===和Object.is(),希望对大家有所帮助。如果本文有什么错误或者不严谨的地方,欢迎在评论区留言。
参考资料es7规范
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/97834.html
摘要:如下所示在规范中,已经正式把属性添加到规范中也可以通过设置和获取对象的原型对象对象之间的关系可以用下图来表示但规范主要介绍了如何利用构造函数去构建原型关系。 前言 在软件工程中,代码重用的模式极为重要,因为他们可以显著地减少软件开发的成本。在那些主流的基于类的语言(比如Java,C++)中都是通过继承(extend)来实现代码复用,同时类继承引入了一套类型规范。而JavaScript是...
摘要:一些开发人员特别是新手们会认为这两个功能的工作方式是一样的,但其实并不是。的问题使用声明的对象仅能阻止其重新分配,但是并不能使其声明的对象具有不可变性能够阻止更改其属性。因此,当具有嵌套属性的对象时,并不能完全冻结对象。 原文:The differences between Object.freeze() vs Const in JavaScript 作者:Bolaji Ayodeji...
摘要:一可以用作对象的复制可以用作对象的合并注意目标对象自身也会改变。对象四返回一个数组,包括对象自身的不含继承的所有可枚举属性不含属性的键名。该方法返回被冻结的对象。方法判断一个对象是否被冻结。 JavaScript对Object对象的一些常用操作总结。 一、Object.assign() 1.可以用作对象的复制 var obj = { a: 1 }; var copy = Object....
摘要:表达式没有返回值,因此返回结果是。并不改变表达式的结果,只要让表达式不返回值按惯例我们用来获得这主要源自语言,当然使用或其他表达式也是可以的。不是数字的数字如果数学运算的操作数不是数字类型,就无法返回一个有效的数字,这种情况下返回值为。 这里的内容是读书笔记,仅供自己学习所用,有欠缺的地方欢迎留言提示。 第一部分 类型和语法 第1章 类型ECMAScript语言类型包括Undefin...
摘要:对象是中最常的内置对象之一。为了节省内存,使用一个共享的构造器使用更安全的引用如果不是或,抛出一个异常。使创建的一个新的对象为,就和通过表达式创建一个新对象一样,是标准内置的构造器名设置的内部属性为。方法返回一个该对象的字符串表示。 Object 对象是Javascript中最常的内置对象之一。除了null 和 undefined,其他的所有的都可以转换为对象。可以把对象看成含有键值一...
阅读 2937·2021-11-17 09:33
阅读 3086·2021-11-16 11:52
阅读 465·2021-09-26 09:55
阅读 2883·2019-08-30 15:52
阅读 1272·2019-08-30 15:44
阅读 1239·2019-08-30 13:59
阅读 752·2019-08-30 13:08
阅读 1093·2019-08-30 10:50