== 和 === 的详解

JavaScript面试中 == 和 === 的区别是最常问的问题


最常见的问题也是最基础的知识点

== 和 === 运算符用于比较两个值是否相等,它们允许任意类型的操作数,相等则返回true否则返回false。=== 称为严格相等运算符,用来检测两个操作数是否严格相等。== 称作相等运算符,用来检测两个操作数是否相等,允许进行类型转换。

在比较之前要确认操作数的类型,原始值还是对象。 原始值(undefined、null、布尔值、数字和字符串)与对象(数组和函数)有着根本的区别。原始值是不可更改的:任何方法都无法改变一个原始值。而常常我们所看见的值被改变了,也只是返回的是一个新的原始值原本的原始值并未改变。

var s = 'hello world';
s.toUpperCase();       // "HELLO WORLD", 返回值并没有改变s的值
s                      //"hello world",原始值s并没改变
  • 原始值之间的比较是通过比较值,只有当它们的值相等时才相等。
  • 对象和原始值不同,它们是可修改的。而对象的比较就不是值的比较了,即使两个对象拥有相同的属性以及相同的属性值,它们也不一定是相等的。同理各个索引元素相等的两个数组也是这样。
var obj1 = {x: 1}, obj2 = {x: 1};
obj1 === obj2;                       // 返回false
var a = [], b = [];
a === b;                             // 返回false

通常我们将对象称为引用类型,来和JavaScript的基本类型区分开来。即对象值都是引用,对象的比较是引用的比较:当且仅当它们引用的是同一个基对象,它们才相等。


回归正题。== 和 === 运算符最大的区别在与它们在比较时是否存在类型转换的过程。

严格相等运算符 === 的比较规则:

  • 如果两个值类型不相同,则它们不相等
  • 如果两个值都是null或者undefined,则它们相等
  • 如果两个都是true或者false,则它们相等
  • 一个或者两个都是NaN,则它们不相等
  • 两个值为数字且数值相等,则它们相等。一个为0另一个为-0,则它们也相等
  • 如果两个同为字符串,且所含对应位上的16位数完全相等,则它们相等
  • 如果两个引用值指向同一个对象、数组或者函数,则它们相等

相等运算符 == 的比较规则:

  • 如果两个操作数的类型相同,则和上述严格相等的比较规则一样
  • 一个值是null,另一个值是undefined,则它们相等
  • 一个值是数字,另一个值是字符串,先将字符串转化为数字,然后使用转换后的值进行比较
  • 一个值是true,则将其转化为1再进行比较。如果一个值是false,则将其转化为0再进行比较
  • 如果一个值是对象,另一个值是数字或者字符串,则将对象转化为原始值,然后再进行比较。对象通过toString()方法或者valueOf()方法转换为原始值,除了日期类只是用toString()转化。

小例子:

"1" == true    //true

上述表达式结果为true,表明类型不相同的两个值比较结果为相等。根据上面的比较规则可得:布尔值的true首先转换为数字1,然后再进行比较。随后字符串”1“转换为数字1,因其两个数字的值相等,则最终返回true。

JavaScript经典问题

有意思的JavaScript面试题:如何让(a == 1 && a == 2 && a == 3) 的值为true

最常用的方式是使其每次使用都自动加一

var a = { value : 0 };
a.valueOf = function() {
    return this.value += 1;
};

console.log(a == 1 && a == 2 && a == 3); // true

按照上述相等运算符的比较规则最后一条, a == 1 , 1 是基本类型, JavaScript引擎会尝试将 a 转换成 数字类型的原始值,然后在上面的算法中, a.valueOf 被调用并且返回1(自增1并且返回自己)。在 a == 2 和 a == 3 发生了同样的类型转换并增加自己的值。最终达到了a == 1 && a == 2 && a == 3 返回true。

另外还有一些我在网上发现的解决方法:

var a = [1,2,3];
a.join = a.shift;
console.log(a == 1 && a == 2 && a == 3); // true

利用数组的特性:可以看到数组 toString 会调用本身的 join 方法,这里把自己的join方法该写为shift,每次返回第一个元素,而且原数组删除第一个值,正好可以使判断成立。

var aᅠ = 1;
var a = 2;
var ᅠa = 3;
console.log(aᅠ==1 && a== 2 &&ᅠa==3); // true

注意if里面的空格,它是一个Unicode空格字符,不被ECMA脚本解释为空格字符(这意味着它是标识符的有效字符),这种解题思路只能说是转了个空子,和隐式转换并没有关系。

拓展
想到了a == 1 && a == 2 && a == 3可以相等,那么a === 1 && a === 2 && a === 3是不是也可以相等呢?
答案是肯定的,只需要使用到Object.defineProperty直接定义参数的getter方法,让a每次使用时都加一即可。

var val = 0;
Object.defineProperty(window, 'a', {
  get: function() {
    return ++val;
  }
});
console.log(a === 1 && a === 2 && a === 3); // true

ps:Object.defineProperty是一个十分重要的东西,在很多地方都能够使用到它。目前笔者正在学习vue,在依赖侦测这一块,vue的底层实现就是依赖Object.defineProperty来实现的。可以达到数据双向绑定的效果。由于ES6 在浏览器的支持度还不太理想,实际上ES6中的proxy也可以实现这种效果。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值