提示:在继续阅读之前,请注意此文章最后更新于 1327 天前,其中的部分内容可能已经无效或过时。
ToPrimitive
首先贴一个ToPrimitive
的概念,这是一个拆箱相关的概念。
有以下三种逻辑
- 是否有Symbol.toPrimitive,如果有执行此操作
- 如果没有Symbol.toPrimitive,会执行默认的操作,规则如下:
- hint不可设置,有三个值'default','number','string'
- default会先调用valueOf(),如果没有或出错,调用toString()
- 'number'会调用valueOf(),如果没有或出错,调用toString()
- 'string'会先调用toString(),如果没有或出错,调用valueOf()
示例:
// 展示hint为default的情况
let obj={
toString(){return 'toString'},
valueOf(){return {}}, // Error
[Symbol.toPrimitive](hint){
console.log(hint);
// 相当于返回undefined
}
};
+obj; // number NaN
`${obj}`; // string undefined
'1'+obj; // default 1undefined
Number(obj);// number NaN
String(obj);// string undefined
// 默认[Symbol.toPrimitive]
let obj={
toString(){return 'toString'},
valueOf(){return 1}, // Error
};
+obj; // 1
`${obj}`; // toString
'1'+obj; // 11
Number(obj);// 1
String(obj);// toString
抽象相等比较算法(x==y)

例子:
/************ 1. **********/
// 1.a
undefined == undefined; // true
// 1.b
null == null; // true
// 1.c.i x或y之一为NaN,返回false
NaN == NaN; // false
// 1.c.iii~1.c.v,x或y之一为+0,另一为-0。或两者数值相等,返回true
+ 0 == -0; // true
// 1.c.vi 其余返回false
// 1.e
'asd'=='asd' //true
/************ 2~11 **********/
// 2~3. x,y其中一个为null,另一为undefined返回true
undefined == null // true
// 4~5. x为Number,y为String,返回x==ToNumber(y)的结果,即number和string比,把string转为number
1 == '1' // true
// 7~8. x为Boolean,返回ToNumber(x)==y的结果
false == '' // Number(false)=>0,0=='' 命中规则4,Number('')=>0 true
// 9~10. x为String或Number, y为Object,返回x==ToPrimitive的结果
'[object Object]' == {} // true ToPrimitive的结果为'[object Object]',因此为true
let a = {
[Symbol.toPrimitive]() {
return 1;
}
}
a == 1; // true
与上面等价的定义:
- 字符串比较可以按这种方式强制执行:
"" + a == "" + b
- 数值比较可以按这种方式强制执行:
+a == +b
- 布尔值比较可以按这种方式强制执行:
!a == !b
几个面试题
undefined == null // true
[]==![] // true
{}==!{} // false
第一个很简单,根据规则2,直接返回true
第二个,流程如下:
1.令x为[], y为![],计算y得false,式子变为[]==false
2.根据规则7,y为Boolean,对y进行ToNumber(y)
转换,式子变为[]==0
3.根据规则10,x为Object,y为Number,需要执行ToPrimitive(x)
,得''==0
4.最后根据规则4,x为String,y为Number,需执行ToNumber('')
,得0==0
5.结果为true
第三个进行同样的流程:
1.令x为{},y为!{},得y为false,即比较{}==false
2.根据规则7,对y进行ToNumber(y)
转换,得{}==0
3.根据规则10,对x进行ToPrimitive(x)
转换,得'[object Object]'==0
4.结果为false
可以进行几点验证
let a = {
[Symbol.toPrimitive]() {
return 0;
}
}
a == !{}; // true 因为按照上述流程最后ToPrimitive(a)的结果为0,即0==0
let b = []
b == !b // true
b[Symbol.toPrimitive] = function () {
return 1
}
b==!![] // true
b == !b; // false
b[Symbol.toPrimitive] = function () {
return '[object Object]'
}
b == {} // false 因为两个都是对象,肯定是不相等的。按上面的规则也属于未列出的其它情况(返回false),写这个例子是告诉自己有时候别搞着搞着把自己绕进去了
// 设计一个a满足这个式子
a == 1 && a == 2 && a == 3
let a = {
i: 1,
valueOf() {
return this.i++;
},
toString: function () {
console.log('toString')
},
}
其实知道上述规则以后,这题并不难。ToPrimitive
的方式选一种即可
另一种方案
var a = [1,2,3];
a.join = a.shift;
其原理也是调用toString(),toString()调用join()
总结
核心逻辑其实就是把==两边都变成Number,然后进行比较。