【Javascript通识】一个==的故事
首先先给出规范关于==的逻辑,表怕,后面我会一一来解说的
更详细的规范请参阅ecma sec;
我把上面的文字表述改为了图像,来帮助一下理解
看图说话
==
进行比较分为相同类型和非相同类型的比较
相同数据类型
数据类型 | 结果 |
---|---|
null == null | true |
undefined == undefined | true |
‘string’ == ‘string’ | true |
false == false | true |
true == true | true |
1 == 1 | true |
+0 == -0 | true |
NaN == NaN | false |
object类型就是看有没有指向相同的引用
真的是一看就懂,嚯嚯????????
不同相同数据类型
不同数据类型的比较就比较蛋疼了????????????,因为会涉及隐式类型转换
string与number进行比较
string会先转换成number,‘1’ => 1, ‘s’ => NaN,数字类型的文字可以转成对应数字,非数字类型的就直接NaN,而且就像之前说的,NaN不等于任何值,自己都不等于,就是这么霸气????;
null和undefined进行比较
记住了就是true,这个是直接写在规范里面的,虽然可以死记硬背,但是规范里面还是有机可循的null & undefined这两位都代表没有可以提供的值,null == undefined
-> true,但是null === undefined
-> false,因为这两个值的type还是不一样的,通过Object.prototype.toString.call(null)
和Object.prototype.toString.call(undefined)
可以看出,null的type是“[object Null]”,而undefined的type是“[object Undefined]”,所以根据严格比较符的定义,两个值将不使用隐式类型转换,直接进行值的比较,所以两个类型不一样的值进行比较肯定是false;
⚠️ null == false和undefined == false不为true 上面说了,null和undefined表示没有值,而false表示一个boolean值,叫false,所以两者是不会相等的
Boolean和其他值进行比较
Boolean与其类型值进行比较之前都会把自己转换为number类型
Object和其他类型进行比较
Object和其他类型的比较时,会试探执行toPrimitive
-> valueOf
-> toString
这三个方法。
var obj1 = {};
obj1[Symbol.toPrimitive] = function() {
console.log('this is in toPrimitive');
return '2222'
};
obj1 == 2222 // true
var obj2 = {};
obj2.valueOf = function(){
console.log('this is valueOf');
return [];
};
obj2.toString = function() {
console.log('this is toString');
return false
};
obj2 == false // true
// this is valueOf
// this is toString
// true
要注意不是每个对象上面都有[Symbol.toPrimitive]
,比如Object,Function,Array构造函数创建的对象都没有[Symbol.toPrimitive]
方法,但是Date上有
var date = new Date()
var date = new Date(2001,10,10,20,20,20,999);
console.log(date[Symbol.toPrimitive]("string")); // Sat Nov 10 2001 20:20:20 GMT+0800 (中国标准时间)
console.log(date[Symbol.toPrimitive]("number")); // 1005394820999
Symbol.toPrimitive vs valueOf vs toString
这三个方法都是在比较的时候会使用到,目的都是为了很简单,就是为了将复杂数据类型转换为原始数据类型进行比较,在用法上有以下方面需要注意:
⚠️在[Symbol.toPrimitive]方法中如果返回一个对象类型的返回值会报错,因为toPrimitive的中文就是变为原始类型,例子如下:
var a={};
a[Symbol.toPrimitive]=function(){return {}}
a[Symbol.toPrimitive]()
// Uncaught TypeError: Cannot convert object to primitive value
⚠️valueOf和toString中可以返回任何值
⚠️如果valueOf中返回的是object类型,Date,Function,Array,Object等,比较都会再使用toString方法进行比较,在使用Number, String, Boolean这些构造函数时要注意,不要使用new来构造,直接使用构造函数即可,否则返回的也是一个对象,所以不要以为new Number(), new String()出来是原始数据类型;
var a = {};
a.valueOf = function(){
console.log("this is in valueOf");
return new Number('2')
};
a.toString = function(){
console.log("this is in toString");
return '222'
}
a == '2'
// this is in valueOf
// this is in toString
// false
a == 222
// this is in valueOf
// this is in toString
// true
案例分析
我这里再举三个复(hu)杂的????
????1
[] == ![] // true
WTF,表面上看不出复杂,但是暗地里波涛汹涌,按照之前说的,任何object对象对会把自己通过toPrimitive -> valueOf -> toString的方式把自己变成一个原始类型的值,
- 由于[]没有toPrimitive,所以就轮到了valueOf;
- 由于[].valueOf()依然是[],所以轮到了toString;
- toString的方法是Array本身重写过的,一般如[1,2]的数组会返回"1,2",所以一个空数组的toString就是""左边已经转好了;
- 轮到右边,大家都知道所有object取反都是0,就是这么干脆
- “” == 0
- 任何字符串都会转换成数值和数值进行比较,所以答案很明显了toNumber("") -> 0; 0==0 ????
????2
var obj1 = {};
obj1 == 1;
// false
obj1 == "[object Object]";
// false
- obj 没有toPrimitive,所以由valueOf继续比较
- valueOf返回的{},由toString继续比较
- obj.toString()返回的就是"[object Object]"
????3
var date = new Date(2001,10,10,20,20,20,999);
date == "Sat Nov 10 2001 20:20:20 GMT+0800 (中国标准时间)";
// true
date == 1005394820999;
// false
这是为毛?之前我们看过例子
var date = new Date(2001,10,10,20,20,20,999);
console.log(date[Symbol.toPrimitive]("string")); // Sat Nov 10 2001 20:20:20 GMT+0800 (中国标准时间)
console.log(date[Symbol.toPrimitive]("number")); // 1005394820999
因为在进行 == 操作的时候,[Symbol.toPrimitive]方法参数是"default",所以我们可以仿造写一个简陋的Date的[Symbol.toPrimitive]方法
var Date1 = function(){}
Date1.prototype[Symbol.toPrimitive] = function(type) {
console.log(type, ">>>")
switch(type) {
case "string":
return "Sat Nov 10 2001 20:20:20 GMT+0800 (中国标准时间)";
case "number":
return 1005394820999;
default:
console.log("execute default")
return "Sat Nov 10 2001 20:20:20 GMT+0800 (中国标准时间)";
}
}
date = new Date1();
date == "Sat Nov 10 2001 20:20:20 GMT+0800 (中国标准时间)";
// default >>>
// execute default
利用上面的例子可以进一步才想==号的实现方式,以下是伪代码
if(oerator equal '==') {
return dateInstance[Symbol.toPrimitive]("default") equal "Sat Nov 10 2001 20:20:20 GMT+0800 (中国标准时间)"
}
顺带一提关系比较符[Symbol.toPrimitive]的参数是number,参照以下例子
var date = new Date(2001,10,10,20,20,20,999);
console.log(date >= 1005394820999); //true
console.log(date < 1005394820999); // false
伪代码就是
if(oerator equal '>=') {
return dateInstance[Symbol.toPrimitive]("number") great than or equal to 1005394820999;
}
❤️⚡️
所以以上就是一个 == 的故事,诸君,如果不想折腾自己的话,还是用 ===号吧,之后还要说一个 ===,>= <= > < 的故事,See U next time????