面试官:说说你对相等操作符的理解

阅读4分钟
面试

背景

前几天,同事间在闲谈的时候,讨论了一些面试题目:

Q: null == undefined 等于什么?
A: false

很好,那么下面呢?

Q: [] == false 等于什么?
A: fasle  ❎(正确的结果是 true)

Q: undefined == 0 呢?
Q: undefined == 1 呢?
......

是的,上面的这些问题,都是一些老掉牙的面试题目了,都被玩烂了,但是,有多少人真正理解呢?比如上面的 [] == false 等于什么?会不会有人这么理解: == 操作符左右两边的操作数,值的类型是可以忽略的,只用比较操作数转化为它的原始值就可以,所以 [] 为 true, true === false 结果为 fasle。但是呢?又感觉比较牵强,有点问题,总觉得好像是有一套完整的比较规则。

我相信,即便是工作 5,6 年的开发,类似的问题也不一定全部答出(懂的读懂)。好,下面我们就详细的讲解下相等操作符相关的知识,以及面试官真正想考察的点。

我们都知道,判断两个变量是否相等是编程中最重要的操作之一。像字符串、布尔值、数值的比较都比较直观,但是对象的比较就有点抽象了。ECMAScript 中提供了两组比较的模式,第一组是等于、不等于,这种模式比较特殊,比较之前操作数会先进行类型转换,正因如此,就有人提出这种转换是否真的有必要?所以后面的标准中,出现了第二组比较模式,全等、不全等。这种模式也是现在主流的变量的比较方式。

等于、不等于

ECMAScript 中 ==表示等于,用!=表示不等于。如果==操作符左右两边的操作数相等,就返回true,如果!= 操作符左右两边的操作数不相等,就返回 true。相信每个人的知道。但是具体是怎么比较的呢?

比较之前,这两个操作符都会先进行强制类型转换,再确定操作数是否相等。下面是比较的规则(很重要):

  1. 如果任一操作数是布尔值,会将其转换为数值再比较是否相等。false 转换为 0,true 转换为 1;

  2. 如果任一操作数是字符串,会将其转换为数值再比较是否相等;

  3. 如果一个操作数是对象,另外一个操作数不是,则会使用对象的valueOf() 方法将其转换为原始类型,再根据前面的规则进行比较;

    注意:如果使用对象的valueOf() 方法转换后还是对象,则会尝试使用 toString()方法转换为字符串,再根据前面的规则进行比较。

  4. 如果两个操作数都是对象,则要比较两个操作数是否都指向同一个地址,是的话就返回 true,反之返回 false。

当然在比较的时候,也要遵循下面的特殊规则:

  • null 和 undefined 相等
  • null 和 undefined 不能转换为其它类型的值再进行比较
  • 如果任一操作数是 NaN,则相等操作符都会返回 fasle,不相等操作符返回 true。记住:即便两个操作数都是 NaN,相等操作符都返回 false,即:NaN 不等于 NaN

下面是一些常见的特殊情况和比较的结果:

null == undefined // true
undefined == 0 // false
null == 0 // false

'NaN' == NaN // false
6 ==  NaN // false
NaN == NaN // false
NaN != NaN // true

true == 1 // true
true == 2 // false
false == 0 // true

'5' == 5 // true
'5' == true // false
[] == 0 // true
[] == [] // false
[] == ![] // true

我们挑选上面几个例子来实操一下,并且描述下比较的过程:

例子一:

"5" == 5 // true
Number("5") == 5
 5 == 5
true

例子二:

"5" == true // false
"5" == Number(true)
"5" == 1
Number("5") == 1
 5 == 1
false

例子三:

[] == 0 // true
[].valueOf() == 0
[] == 0
[].toString() == 0
"" == 0
Number("") == 0
0 == 0
true

例子四:

[] == [] // false
   ↓ 引用地址不相等
true

例子五:有点特殊,遇到!操作符,会先将操作数转化为 Boolean 类型,然后再取反

[] == ![] // true
[] == !true
[] == false
[] == Number(false)
[] == 0
[].valueOf() == 0
[] == 0
[].toString() == 0
"" == 0
Number("") == 0 
0 == 0
true

小结:用等于、不等于操作符比较两个变量的时候,在遵循特殊情况(上面的 3 点)下,会按照上面 1-4 的顺序进行比较,最终都会转换为数值进行比较。

全等、不全等

ECMAScript 中 ===表示全等,用!==表示不全等。全等、不全等与等于、不等于类似,但是在比较时不进行类型的转换,两个操作数只有在不转换类型的前提下相等,才会返回 true。

下面是一些常见的特殊情况和比较的结果:

null === undefined // false
undefined === 0 // false
null === 0 // false

'NaN' === NaN // false
6 ===  NaN // false
NaN === NaN // false
NaN !== NaN // true

true === 1 // false
true === 2 // false
false === 0 // false

'5' === 5 // false
'5' === true // false
[] === 0 // false
[] === [] // false
[] === ![] // false

简单对比两种模式,例如下面的代码:

55 == '55' // true 转换后相等
55 === '55' // false,不相等,因为数据类型不同

55 != '55' // fasle,转换后相等
55 !== '55' // true,不相等,因为数据类型不同

55 !== '55'也可以这么问:”数值 55 和字符串 55 有区别吗?“答案是:“有”(true)

总结

上面详细介绍了相等操作符的两种模式,希望能够帮助到大家。对了,这里提一下,由于等于、不等于操作符存在类型转换问题,因此推荐使用全等、不全等操作符。这样有助于在代码中保持数据类型的完整性,并且也更加清晰易懂。


此文自动发布于:github issues