目录
- 一、隐式类型转换条件
- 二、== 的隐式类型转换
- 三、+ 的隐式类型转换
- 四、object 的隐式类型转换
- 探讨 object 的隐式转换执行顺序
- 探讨 Symbol.toPrimitive 属性如何将对象转换为原始值
在前端js这门动态弱类型语言中,不仅存在着显示类型转换,还存在许多隐式类型转换,让人头大。为了减少小伙伴们的踩坑,今天总结了一些常见的隐式类型转换规则,让我们来看看有哪些。
一、隐式类型转换条件
- 逻辑运算符:&&、||、!
- 运算符:+、-、*、/
- 关系操作符:>、<、<=、>=
- 相等运行算符:==
- if / while 条件
二、== 的隐式类型转换
- 类型相同,则无需进行类型转换。
- 如果其中一个操作数是 null 或者 undefined ,那么另一个操作数必须是 null 或者 undefined 才会返回 true ,否则返回 false 。
- 如果其中一个操作数是 Symbol ,那么返回 false。
- 如果两个操作数都为 string 和 number 类型,那就就将字符串转换为 number。
- 如果一个操作数是 boolean 类型,那么转换成 number。
- 如果一个操作数为 object ,且另一方为 string、number、或者 Symbol ,就会把object 转换为原始类型再进行判断。
测试:
null == undefined; // true
null == 0; // false
'' == null; // false
'' == 0; // true
'123' == 123; // true
0 == false; // true
1 == true; // true
var a = {
value: 0,
valueOf: function(){
this.value++;
return this.value;
}
}
console.log(a==1 && a==2 && a==3); // true
// 对象隐式转换为原始类型,调用对象的valueOf方法返回值,此对象的valueOf()返回一个自定义自增方法,每调用一次增加1,最后结果为3.
// a==1时,a.valueOf()返回1,所以1==1为true,此时a.value==1;
// a==2时,a.valueOf()返回2,所以2==2为true,此时a.value==2;
// a==3时,a.valueOf()返回3,所以3==3为true,此时a.value==3;
// 这时,如果再判断a==1&&a==2&&a==3,则返回false,因为此时的a已经为4了
console.log(a==1 && a==2 && a==3); // false
三、+ 的隐式类型转换
+号操作符,不仅可以用作数字相加,还可以用作字符串拼接。
- 如果其中一个操作数是 string,另外一个操作数是 undefined、null 或者 boolean,则调用 toString() 方法进行字符串拼接。
- 如果是纯对象、数组、正则等,则默认调用对象的转换方法,会存在优先级,然后再进行拼接。
- 如果其中有一个是 number ,另外一个是 undefined、null、boolean、number,则会将其转换为数字进行加法运算,对象的情况参考上一条规则。
- 如果其中一个是 string ,一个是 number,则按照字符串规则进行拼接。
测试:
1 + 2; // 3
'1' + '2'; // '12'
'1' + 3; // '13' 字符串拼接
'1' + undefined; // '1undefined'
'1' + null; // '1null'
'1' + true; // '1true'
'1' + 1n; // '11' 字符串和BigInt相加,BigInt转换为字符串
1 + undefined; // NaN undefined转换为NaN
1 + null; // 1 null转换为0
1 + true; // 2 true转换为1
1 + 1n; // TypeError: Cannot mix BigInt and other types, use explicit conversion "无法混合BigInt和其他类型,请使用显式转换"
四、object 的隐式类型转换
- 如果部署了 [Symbol.toPrimitive] 方法,优先调用再返回;
- 若不存在 [Symbol.toPrimitive] 方法,则调用 valueOf 方法,如果返回基础数据类型,则执行结束;
- 否则调用 toString 方法,如果转换为基础数据类型,则返回;
- 最后,如果都没有返回基础数据类型,则报错。
测试:
var obj = {
value: 1,
valueOf(){
console.log('valueOf', 2);
return 2;
},
toString(){
console.log('toString', 3);
return 3;
},
[Symbol.toPrimitive](){
console.log('[Symbol.toPrimitive]', 4);
return 4;
}
}
console.log(obj + 1); // 5
// 调用[Symbol.toPrimitive]方法,获取返回值4,4+1=5
10 + {}; // '10[object Object]'
// {}调用valueOf方法返回自身,然后继续调用toString方法返回字符串'[object Object]',10与其相加得 '10[object Object]'
[1, 2, undefined, 4, 5] + 10; // '1,2,,4,510'
// 数组调用valueOf方法返回数组本身,然后调用toString方法转换为字符串'1,2,,4,5',与10相加得 '1,2,,4,510'
探讨 object 的隐式转换执行顺序
1、有 [Symbol.toPrimitive] 方法,执行方法后获取原始值返回,执行结束;若返回不是原始值则报错。
var obj = {
value: 1,
valueOf(){
console.log('valueOf', 2);
return 2;
},
toString(){
console.log('toString', 3);
return 3;
},
[Symbol.toPrimitive](){
console.log('[Symbol.toPrimitive]', 4);
return 4;
}
}
console.log(obj + 1); // 5
2、valueOf方法返回值不为原始值时,则继续寻找toString方法执行,获取返回值,执行结束。
var obj = {
value: 1,
valueOf(){
console.log('valueOf', 2);
return {};
},
toString(){
console.log('toString', 3);
return 3;
},
}
console.log(obj + 1); // 4
3、若valueOf方法返回值为原始值时,执行结束。
var obj = {
value: 1,
valueOf(){
console.log('valueOf', 2);
return 2;
},
toString(){
console.log('toString', 3);
return 3;
}
}
console.log(obj + 1); // 3
4、若无 Symbol.toPrimitive 方法,并且最终获取不到原始值时,报错。
var obj = {
value: 1,
valueOf(){
console.log('valueOf {}');
return {};
},
toString(){
console.log('toString {}');
return {};
}
}
console.log(obj + 1); // ncaught TypeError: Cannot convert object to primitive value
探讨 Symbol.toPrimitive 属性如何将对象转换为原始值
Symbol.toPrimitive 指将被调用的指定函数值的属性转换为相对应的原始值。
在 Symbol.toPrimitive属性(用作函数值)的帮助下,一个对象可被转换为原始值。该函数由字符串参数 hint 调用,目的是指定原始值转换结果的首选类型。 hint 参数可以是"number"、“string” 和 “default” 中的一种。
// An object without Symbol.toPrimitive property. 对象中不存在Symbol.toPrimitive属性
var obj1 = {};
console.log(+obj1); // NaN
console.log(`${obj1}`); // "[object Object]"
console.log(obj1 + ''); // "[object Object]"
// An object with Symbol.toPrimitive property. 对象中存在Symbol.toPrimitive属性
var obj2 = {
[Symbol.toPrimitive](hint) {
if (hint == 'number') {
return 10;
}
if (hint == 'string') {
return 'hello';
}
return true;
}
};
console.log(+obj2); // 10 -- hint is "number"
console.log(`${obj2}`); // "hello" -- hint is "string"
console.log(obj2 + ''); // "true" -- hint is "default"
超强测试题:
{} + ''; // 0
// 代码执行顺序为从左到右,此处的{}被认为是代码块而非对象,所以不进行类型转换,仅剩余 +'' 被转换number类型
+{} + ''; // 'NaN'
// 此处先执行+{},转换为number类型NaN,与空字符串相加得'NaN'
'' + {}; // '[object Object]'
// 此处的+号前面空字符串为string类型,按照代码执行顺序以及字符串拼接规则,
// 需将{}转换为string类型与其相加,调用toString方法获得'[object Object]'