我对 JS 中相等和全等操做符转化过程一直很迷惑,直到有了这份算法

做者:Dmitri Pavlutinjavascript

译者:前端小智html

来源:dmitripavlutin前端


阿里云最近在作活动,低至2折,有兴趣能够看看: promotion.aliyun.com/ntms/yunpar…java


在平常的 JS 编码过程当中,可能很难看到相等运算符(==)是如何工做的。特别是当操做数具备不一样类型时。这有时会在条件语句中产生一些难以识别的 bug。很容易理解为何 0 == 8flase 的或者 '' == falsetrue。可是为何{} == truefalse 的就看不出来了。接下将会讲这是肿么肥事。git

在这以前,先说几个术语:github

  • 操做符(Operator) 表示操做的符号。例如,相等运算符==比较两个值,三等运算符 === 比较两个值及其类型,加法运算符+两个数字和或链接两个字符串。正则表达式

  • 操做数(Operand) 是运算的主体,是执行运算的数量。例如,在表达式 0 == {} 中,0 是第一个操做数,{} 是第二个操做数。算法

  • JS 中的基本数据类型(原始类型)有 numberstring, booleannullundefinedsymbol数组

全等运算符 ===

全等和不全等操做符遵循如下基本规则(IEA规则):浏览器

  1. 若是两个操做数有不一样的类型,它们不是严格相等的
  2. 若是两个操做数都为 null,则它们是严格相等的
  3. 若是两个操做数都为 undefined,它们是严格相等的
  4. 若是一个或两个操做数都是 NaN,它们就不是严格相等的
  5. 若是两个操做数都为 true 或都为 false,它们是严格相等的
  6. 若是两个操做数都是 number 类型而且具备相同的值,则它们是严格相等的
  7. 若是两个操做数都是 string 类型而且具备相同的值,则它们是严格相等的
  8. 若是两个操做数都引用相同的对象或函数,则它们是严格相等的
  9. 以上全部其余状况下操做数都不是严格相等的。

规则很简单。

值得一提的是,在全等运算中,NaN 与其余任何值相比,结果都是 false。 来看看考虑些例子,这是学习这些规则的好方式。

例 1

1 === "1" // false, 规则 1
复制代码

操做数是不一样的类型(数字和字符串),基于 IEA 规则1,它们是不等的。

例 2

0 === 0 // true, 规则 6
复制代码

操做数具备相同的类型和相同的值,所以根据IEA规则6,它们是严格相等的。

例 3

undefined === undefined // true, 规则 3
复制代码

两个操做数都是 undefined 的,应用 IEA 规则3,它们是相等的。

例 4

undefined === null // false, 规则 1
复制代码

由于操做数是不一样的类型,根据IEA规则1,它们并不相同。

例 5

NaN === NaN // false, IEA 规则 5
复制代码

操做数是相同的类型,可是IEA 规则4 代表任何与 NaN 比较都是不相等的。

例 6

var firstObject = {},
  secondObject = firstObject;
secondObject['name'] = 'Neo';
secondObject === firstObject // true, IEA 规则 8
复制代码

两个变量 firstObjectsecondObject 都是对同一对象的引用,根据 IEA 规则8,它们相等。

例 7

[] === [] //false, IEA 规则 9
复制代码

字面量 [] 建立了一个新的数组引用。这两个操做数是相同的类型(对象),可是它们引用不一样的对象。根据 IEA 规则 9 ,它们不相等。

对象转换为原始值的规则

对象到布尔值

对象到布尔值的转换很是简单:全部的对象(包括数字和函数)都转换为 true。对于包装对象亦是如此:new Boolean(false) 是一个对象而不是原始值,它将转换为 true

对象到字符串

对象到字符串对象到数字 的转换都是经过调用待转换对象的一个方法来完成的。一个麻烦的事实是,JS 对象有两个不一样的方法来执行转换,接下来要讨论的一些特殊场景更加复杂。值得注意的是,这里提到的字符串和对象的转换规则只适用于原生对象(native object)。宿主对象(例若有Web浏览器定义的对象)根据各自的算法能够转换成字符串和数字。

全部的对象继承了两个转换方法。第一个是toString(),它的做用是返回一个反映这个对象的字符串。默认的 toString() 方法并不会返回一个有趣的值:

({x:1,y:2}).toString()  //=>"[object object]"
复制代码

不少类定义了更多特定版本的toString()方法。例如,数组的 toString() 方法是将每一个数组元素转换为一个字符串,并在元素之间添加逗号后合并成结果字符串。

函数的 toString() 方法返回了这个函数的实现定义。实际上,这里的实现是一般是将用户定义的函数转换为 JS 源代码字符串。

日期 DatetoString() 方法返回了一个可读的日期和时间字符串。

RegExptoString() 方法将RegExp对象转换为表示正则表达式直接量的字符串:

来几个例子:

[1,2,3].toString() //=> "1,2,3"
(function(x){ f(x); }).toString() // => "function(x){ f(x); }"
/\d+/g.toString()   // => "/\d+/g"
new Date(2019,9,16).toString()  //=> "Wed Oct 16 2019 00:00:00 GMT+0800 (中国标准时间)"
复制代码

另外一个转换对象的函数是 valueOf()。若是存在任意原始值,它就默认将对象转换为表示它的原始值。对象是复合值,并且大多数对象没法真正表示为一个原始值,所以默认的 valueOf() 方法简单地返回对象自己,而不是返回一个原始值。数组、函数和正则表达式简单地继承了这个方法,调用这些类型的实例的valueOf() 方法只是简单返回对象自己。日期 DatevalueOf() 方法会返回它的一个内部表示:1970年1月1日以来的毫秒数。

new Date(2019,9,16).valueOf() // 1571155200000
复制代码

经过使用 toString()valueOf() 方法,就能够作到对象到字符串和对象到数字的转换了。但须要注意的是,在某些特殊的场景中,JS 执行了彻底不一样的对象到原始值的转换。

JS 中对象到字符串的转换通过以下这些步骤,我们简称 OPCA 算法。

  1. 若是方法 valueOf() 存在,则调用它。若是 valueOf() 返回一个原始值,JS 将这个值转换为字符串(若是自己不是字符串的话),并返回这个字符串结果。

  2. 若是方法 toString() 存在,则调用它。若是 toString() 返回一个原始值,JS 将这个值转换为字符串(若是自己不是字符串的话),并返回这个字符串结果。须要注意,原始值到字符串的转换。

  3. 不然,JS 没法从 toString()valueOf() 得到一个原始值,它将抛出一个 TypeError:不能将对象转换为原始值 异常

当调用 valueOf() 方法时,大多数原生对象都会返回对象自己。所以 toString() 方法使用得更频繁。

关于 Date 对象的注意事项:在转换为原始值时,对象当即使用 toString() 方法转换为字符串。这样,规则1就被跳过了。普通的 JS 对象,{}new object(),一般被转换成 "[object Object]"

数组经过将它的元素与“,”分隔符链接转换为。例如 [1,3,"four"] 被转换成" 1,3,four"

相等运算符 ==

相等运算符 “==” 若是两个操做数不是同一类型,那么相等运算符会尝试一些类型转换,而后进行比较。

相等运算符算法(EEA)

  1. 若是操做数具备相同的类型,请使用上面的 IEA 测试它们是否严格相等。 若是它们不严格相等,则它们不相等,不然相等。
  2. 若是操做数有不一样的类型:
    2.1) 若是一个操做数为 null 而另外一个 undefined,则它们相等
    2.2) 若是一个值是数字,另外一个是字符串,先将字符串转换为数字,而后使用转换后的值比较
    2.3) 若是一个操做数是布尔值,则将 true 转换为 1,将 false 转换为 0,而后使用转换后的值比较
    2.4) 若是一个操做数是一个对象,而另外一个操做数是一个数字或字符串,则使用OPCA将该对象转换为原原始值,再使用转换后的值比较
  3. 在以上的其余状况下,操做数都不相等

例 1

1 == true // true
复制代码

上面的转换步骤:

  1. 1 == true (使用EEA 规则2.3 将 true 转换为 1)
  2. 1 == 1(操做数有相同的类型。使用 EEA 规则1 将相等转换为全等运算进行比较
  3. 1 === 1(两个操做数都是数字,而且具备相同的值。根据 IEA 规则 6,这是相等的)
  4. true

例 2

'' == 0 // true
复制代码

上面的转换步骤:

  1. '' == 0(一个操做数是字符串,另外一个操做数是数字,根据EEA规则2.2'' 被转换为数字 0 )
  2. 0 == 0(操做数类型相同,使用 EEA规则1 将相等转换为全等运算进行比较)
  3. 0 === 0(操做数类型相同,值相同,因此根据IEA规则6,它是一个恒等式)
  4. true

例 3

null == 0 // false
复制代码

上面的转换步骤:

  1. null == 0 (null 是原始类型,0 是 number 类型。根据EEA规则3)
  2. false

例 4

null == undefined // true
复制代码

上面的转换步骤:

  1. null == undefined(基于EEA规则2.1,操做数相等)
  2. true

例 5

NaN == NaN // false
复制代码

上面的转换步骤:

  1. NaN == NaN(两个操做数都是数字。根据EEA规则1,将相等转换为全等运算进行比较)
  2. NaN === NaN(根据IEA规则4,操做数严格不相等)
  3. false

例 6

[''] == '' // true
复制代码

上面的转换步骤:

  1. [''] == ''(['']是一个数组和 '' 是一个字符串。应用EEA规则2.4并使用OPCA规则2将数组转换为原始值 '')
  2. '' == '' (两个操做数都是字符串,将相等转换为全等运算进行比较)
  3. '' === '' (两个操做数类型相同,值相同。使用IEA规则7,它们是相等的)
  4. true

例 7

{} == true // false
复制代码

上面的转换步骤:

  1. {} == true(使用EEA规则2.3,将 true 操做数转换为 1)
  2. {} == 1(第一个操做数是一个对象,所以有必要使用OPCA将其转换为原始值)
  3. “[object object]”== 1(由于第一个操做数是字符串,第二个操做数是数字,根据 EEA规则2.2“[object object]”转换为数字)
  4. NaN == 1(两个操做数都是数字,所以使用 EEA规则1 将相等转换为全等运算进行比较)
  5. NaN === 1(根据 IEA规则4,没有什么是与 NaN 相等的,结果是 false)
  6. false

实用技巧

即便在详细研究了本文中的全部示例、学习了算法以后,你会发现要当即理解复杂的比较还须要时间的积累。

告诉你一些技巧。 将本文添加到书签中(使用Ctrl + D),下一次看到有趣的状况时,能够根据等式算法编写逐步的计算。 若是检查至少 10 个示例,则之后不会有任何问题。

如今就能够试试,如 [0] == 0 的结果和转化步骤是什么?

相等运算符==进行类型转换。所以,可能会产生意想不到的结果,例如 {}== truefalse( 参见例7)。在大多数状况下,使用全等操做符 === 更安全。

总结

相等和全等运算符号多是最经常使用的运算符之一。理解它们是编写稳定且bug较少的 JS 的步骤之一。

代码部署后可能存在的BUG无法实时知道,过后为了解决这些BUG,花了大量的时间进行log 调试,这边顺便给你们推荐一个好用的BUG监控工具 Fundebug

原文:dmitripavlutin.com/the-legend-…

交流(欢迎加入群,群工做日都会发红包,互动讨论技术)

阿里云最近在作活动,低至2折,有兴趣能够看看:promotion.aliyun.com/ntms/yunpar…

干货系列文章汇总以下,以为不错点个Star,欢迎 加群 互相学习。

github.com/qq449245884…

由于篇幅的限制,今天的分享只到这里。若是你们想了解更多的内容的话,能够去扫一扫每篇文章最下面的二维码,而后关注我们的微信公众号,了解更多的资讯和有价值的内容。

clipboard.png

每次整理文章,通常都到2点才睡觉,一周4次左右,挺苦的,还望支持,给点鼓励

相关文章
相关标签/搜索