面试官:说说你对相等操作符的理解
背景
前几天,同事间在闲谈的时候,讨论了一些面试题目:
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。相信每个人的知道。但是具体是怎么比较的呢?
比较之前,这两个操作符都会先进行强制类型转换,再确定操作数是否相等。下面是比较的规则(很重要):
-
如果任一操作数是布尔值,会将其转换为数值再比较是否相等。false 转换为 0,true 转换为 1;
-
如果任一操作数是字符串,会将其转换为数值再比较是否相等;
-
如果一个操作数是对象,另外一个操作数不是,则会使用对象的
valueOf()方法将其转换为原始类型,再根据前面的规则进行比较;注意:如果使用对象的
valueOf()方法转换后还是对象,则会尝试使用toString()方法转换为字符串,再根据前面的规则进行比较。 -
如果两个操作数都是对象,则要比较两个操作数是否都指向同一个地址,是的话就返回 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
