你确定你知道 == 和 === 的区别吗?
前言
- 在学习本章内容前,你需要一些前置知识,你可以移步到此专栏 你学了也不懂的JavaScript。
- 通过本篇文章的学习你将学习到
==和===操作符的区别,以及如何让a == 2 && a== 3为true。 - 在平时的面试中,我们可能会遇到一个问题,就是
==和===有啥区别,下面我们就来模拟一下场景:
- 面试官: 你知道宽松相等和严格相等有什么区别吗?
- 我的内心: 很好,这我知道,难不倒我,到我开始表现的时候了😊
- 我:这很简单啊,
==不就是检查值是否相等嘛,===检查值和类型是否相等嘛; - 面试官: ...... 你先回去等通知吧👋👋👋
- 此时门外一个人敲门进来了,我来我来,这个我会;
==允许在相等比较中进行强制类型转换,而===不允许;- 面试官: 很好,你面试通过了,这边呢,工资给你开到3000...
- 好了,水吹完了,那么接下来我们就开始去讲解一下
==是怎么在比较中进行强制类型转化的,以及是怎么样的转换规则,通过这篇文章你将会学到很多奇奇怪怪的知识。
性能
==似乎比===做的事情更多,因为它还要对值的类型进行强制类型转换,但是实际上虽然强制类型转换确实要多花点时间,但仅仅是微秒级(百万分之一秒)的差别而已。- 如果两个值的类型不同,我们就需要考虑有没有强制类型转换的必要,有就用
==,没有就用===,冉在乎性能。
抽象相等
- ES5规范11.9.3 节的
抽象相等比较算法定义了==运算符的行为,在使用==比较的时候,当使用==对x和y进行比较时,会返回true或者false的值,它们主要遵循以下的规则:
- 如果
x和y的类型相同:- 如果
x是undefined,返回true; - 如果
x是null,返回true; - 如果
x是Number类型,则:- 如果
x是NaN,则返回false; - 如果
y是NaN,则返回false; - 如果
x和y相同,则返回true; - 如果
x是-0和y是+0,则返回true; - 如果
y是-0和x是+0,则返回true; - 其他情况返回
false;
- 如果
- 如果
x是string类型,并且x和y的完全相同的值(长度相同,对应位置的字符相同),则返回true; - 如果
x的Boolean类型,并且x和y都是true或者false,则返回true,否则返回false; - 如果
x和y引用同一个对象,则返回true,否则返回false;
- 如果
- 如果
x为null且y为undefined,则返回true; - 如果
y为null且x为undefined,则返回true; - 如果
x是number类型且y是string类型,则返回x == ToNumber(y)的比较结果; - 如果
y是number类型且x是string类型,则返回y == ToNumber(x)的比较结果; - 如果
x是boolean类型,返回ToNumber(x) == y的比较结果; - 如果
y是boolean类型,返回ToNumber(y) == x的比较结果; - 如果
x是string类型或者number类型,并且y是object类型,返回x == ToPrimitive(y)的比较结果; - 如果
y是string类型或者number类型,并且x是object类型,返回y == ToPrimitive(x)的比较结果; - 否则返回
false;
- 抽象相等的这些规则正是隐式强制类型转换不受人喜爱的原因,但是认真一看规则,其实简单明了。
字符串和数字之间的相等比较
const a = 77;
const b = "77";
console.log(a === b); // false
console.log(a == b); // true
- 因为没强制类型转换,所以
a === b为false,77和"77"不相等。 - 而
a == b是宽松相等,即如果两个值的类型不同,则对其中之一或者两者都进行强制类型转换,具体怎么转换的,请看定义,它们是这样的:
- 如果
x是number类型且y是string类型,则返回x == ToNumber(y)的比较结果; - 如果
y是number类型且x是string类型,则返回y == ToNumber(x)的比较结果;
- 也就是说,
a == b在代码中实际上是这样的行为:
const a = 77;
const b = "77";
console.log(a === Number(b)); // true
其他类型和布尔值之间的相等比较
==最容易出错的是一个地方是true和false于其他类型之间的相等比较,例如:
const a = "77";
const b = true;
console.log(a == b); // false
- 我们都知道
"77"是一个真值,为什么==的结果不是true呢?因为规范是这样定义的:
- 如果
x是boolean类型,返回ToNumber(x) == y的比较结果; - 如果
y是boolean类型,返回ToNumber(y) == x的比较结果;
- 首先
b是boolean类型,所以ToNumber(b)将b的类型强制转换为1,变成1 == '77',二者的类型仍然不同,"77"根据规则被强制类型转换为77,最后变成1 == 77,所以结果输出为false;
null 和 undefined 之间的相等比较
null和undefined之间的==也设计隐式强制类型转换,ES5规范是这样规定的:
- 如果
x为null且y为undefined,则返回true; - 如果
y为null且x为undefined,则返回true;
- 在
==中null和undefined相等(它们也与其自身相等),除此之外其他值都不和它们两个相等。 - 这也就是说,在
==中null和undefined是一回事,可以相互进行隐式强制类型转换:
var a = null;
var b = undefined;
console.log(a == b); // true
console.log(a == null); // true
console.log(b == null); // true
console.log(a == false); // false
console.log(b == false); // false
console.log(a == ""); // false
console.log(b == ""); // false
console.log(a == 0); // false
console.log(b == 0); // false
nullheundefined之间的强制类型转换是安全可靠的,上例中除null和undefined以外的其他值均为无法返回true的结果。
对象和非对象之间的相等比较
- 对于对象和基本类型之间的相等比较,
ES5规范是遵循这样的规则:
- 如果
x是string类型或者number类型,并且y是object类型,返回x == ToPrimitive(y)的比较结果; - 如果
y是string类型或者number类型,并且x是object类型,返回y == ToPrimitive(x)的比较结果;
- 例如:
var a = 77;
var b = [77];
console.log(a == b); // true
[77]首先调用了ToPrimitive抽象操作,返回"77",变成"77" == 77,然后又变成77 == 77,最后二者相等。其中代码的转变过程是以下的形式:
var a = 77;
var b = [77];
console.log(a === Number(b.toString())); // true
- 我们再来看一个
string类型和object类型的例子:
var a = "abc";
var b = Object(a);
console.log(a === b); // false
console.log(a == b); // b
-
a == b结果为true,因为b通过TOPrimitive进行强制类型转换(拆封),并返回基本数据类型值"abc",与a相等。 -
但是规则总有特例,原因是
==算法中其他优先级更高的原则。例如:
var foo = null;
var bar = Object(foo);
console.log(foo == bar); // false
var a = undefined;
var b = Object(a);
console.log(a == b); // false
var c = NaN;
var d = Object(NaN);
console.log(c == d); // false
- 因为没有对应的封装对象,所以
null和undefined不能够被封装和Object()均返回一个空对象,你也可以理解成空对象调用toString()方法返回的值是"[object Object]",不等于null和undefined。 NaN能够被封装为数字封装对象,但拆封之后NaN == NaN返回false,因为NaN不等于NaN。
希望你永远不会用到
- 在上面的讲解中,我们已经介绍了
==中的隐式强制类型转换,现在来看一下那些需要特别注意和避免的比较少见的情况。
返回其他的数字
- 首先来看看更改内置原生模型会导致哪些奇怪的结果:
Number.prototype.valueOf = function () {
return 3;
};
console.log(new Number(77) == 3); // true
2 == 3不会有这样的问题,因为2和3都是数字基本类型值,不会调用Number.prototype.valueOf()方法。而Number(2)涉及ToPrimitive强制类型转换,因此会调用valueOf()。- 再来看一种情况,这个你可能面试题里经常看到,
a == 2 && a == 5在什么情况下为true?
if (a == 2 && a == 3) {
// ...
}
- 你也许觉得不可能,因为
a不会同时等于2和3。但 同时似乎说的不对,因为a == 2在a == 3之前执行。如果让a..valueOf()每次调用都产生副作用,比如第一次返回2,第二次返回3,就会出现这样的情况。这实现起来很简单:
const a = {
value: 1,
};
a.valueOf = function () {
return this.value++;
};
if (a == 1 && a == 2 && a == 3) {
console.log("嗨,没想到吧,意不意外,惊不惊喜"); // 这里正常输出了
}
假值的相等比较
==中的隐式强制类型转换最为让人不满的地方是假值的相等比较下面分别列出了常规和非常规的情况:
console.log("0" == null); // false
console.log("0" == undefined); // false
console.log("0" == false); // true
console.log("0" == NaN); // false
console.log("0" == 0); // true
console.log("0" == ""); // false
console.log(false == null); // false
console.log(false == undefined); // false
console.log(false == NaN); // false
console.log(false == 0); // true
console.log(false == ""); // true
console.log(false == []); // true
console.log(false == {}); // false
console.log("" == null); // false
console.log("" == undefined); // false
console.log("" == NaN); // false
console.log("" == 0); // true
console.log("" == []); // true
console.log("" == {}); // false
console.log(0 == null); // false
console.log(0 == undefined); // false
console.log(0 == NaN); // false
console.log(0 == []); // true
console.log(0 == {}); // false
- 接下来我们挑一些有代表性的例子来具体讲解一下:
- 其中这些例子中最好辨认的是在
==比较中,NaN与任何值相比都是false,NaN也是; - 在
"0" == false中会对false先转换成数字类型,即0,变成了"0" == 0,再对"0"转换,最终变成了0 == 0,所以输出为false; - 在
false == []中,会对[]进行ToPrimitive类型转换,转换成原始类型"",这时候变成了false == "",所以输出结果为true,至于为什么是true,我想你应该懂了吧
- 接下来还有一些例子:
console.log([] == ![]); // true
console.log("" == [null]); // true
console.log("" == [undefined]); // true
-
喔喔喔,这是什么,怎么都输出了
true,它们到底都干了啥? -
在第一个中,
!运算符对[]做了取反操作,因为[]为真值,而![]也就变成了false了,所以[] == ![]变成了[] == false,前面我们有讲过这个比较的结果,所以最后的结果也很正常了。 -
在第二、三个例子中,
[null].toString()最后返回"",而难懂的是String(null)返回的是"null",而String([Null])去返回的是"",这就是难懂的地方,这也是js令人深入理解的原因。 -
所以
==和===选择哪一个取决于是否允许在相等比较中发生强制类型转换。 -
好了,本篇的内容讲解到此结束了,有什么不了解的可以在评论区留下你的疑问。
参考文章
- 书籍 `你不知道的JavaScript.
- ES5规范
转载自:https://juejin.cn/post/7168761409629601800