小知识,大挑战!本文正在参与“程序员必备小知识”创作活动
强制类型转换对比隐式类型转换会直观许多,转换的结果是显示的、符合转换的直观的,根据API可以很明确的知道转换结果,但是隐式类型转换就会隐喻很多,因为触发隐式类型转换存在多种方式,很容易在开发过程中忽视,导致经验不足的开发者而言较为晦涩难懂
隐式类型转换
如果遇到两个数据类型不一样,而需要进行逻辑运算符 (&&、 ||、 !)、运算符 (+、-、*、/)、关系操作符 (>、 <、 <= 、>=)、相等运算符 (==) 或者 if/while 条件的操作时,便会触发隐式类型转换,这也是导致常被忽略的原因之一
'+' 的隐式类型转换规则
'+' 号操作符,不仅可以用作数字相加,还可以用作字符串拼接。仅当 '+' 号两边都是数字时,进行的是加法运算。当使用 + 运算符计算 string 和其他类型相加时,都会转换为 string 类型进行拼接。其他情况,都会转换为 number 类型,但是 undefined 会转换为 NaN,相加结果也是 NaN
- 如果其中有一个是字符串,另外一个是 undefined、null 或布尔型,则调用 toString() 方法进行字符串拼接;如果是纯对象、数组、正则等,将复杂类型将转换为基本类型(转换会存在优先级),再进行运算、拼接
- 如果其中有一个是数字,另外一个是 undefined、null、布尔型或数字,则会将其转换成数字进行加法运算,对象的情况如上
- 如果其中一个是字符串、一个是数字,则按照字符串规则进行拼接。
1 + 1; // 2
1 + '1'; // '11'
1 + null; // 1
'1' + null; // '1null'
1 + undefined; // NaN
'1' + undefined; // '1undefined'
1 + true; // 2
'1' + true; // '1true'
1 + false; // 1
'1' + false; // '1false'
'==' 的隐式类型转换规则
在开发过程中使用频率非常高,其转换规则大致流程如下:
-
如果类型相同,无须进行类型转换,直接判断是否相等
-
如果其中一个操作值是 null 或者 undefined,那么另一个操作符必须为 null 或者 undefined,才会返回 true,否则都返回 false
null == undefined // true
null == 1 // false
undefined == 1 // false
- 如果其中一个是 Symbol 类型,那么返回 false
const symbol = Symbol()
symbol == 2 // false
symbol == 'symbol' // false
- 两个操作值如果为 string 和 number 类型,那么就会将字符串转换为 number
'1' == 1 // true
- 如果一个操作值是 boolean,那么转换成 number
1 == true // true
11 == true // false
0 == true // false
- 如果一个操作值为 object 且另一方为 string、number 或者 symbol,就会把 object 转为原始类型再进行判断
Object 的转换较其他类型会比较复杂一些,因为其中会牵扯到元编程的概念,但是Object的转行趋势就是从引用类型向基本类型数据转换,转换优先级如下:
首先会判断是否存在Symbol.toPrimitive,若是存在则优先调用并返回数据
若是不存在,则调用该对象上 valueOf 或 toString 这两个方法,该方法的返回值是转换为基本类型的结果
注意点:Symbol.toPrimitive返回的必须是基础数据类型,否则控制台会报错
const obj = {
[Symbol.toPrimitive]() {
return {
}
}
};
obj + 1;
上述代码会在控制台报错:Uncaught TypeError: Cannot convert object to primitive value
存在Symbol.toPrimitive方法
const obj = {
[Symbol.toPrimitive]() {
return 1
},
valueOf() {
return 2;
},
toString() {
return 3
}
};
console.log(obj + 1); // 2
不存在Symbol.toPrimitive方法,只存在两种valueOf和toString方法时,对象倾向于转换成什么,就会优先调用哪个方法。如果倾向于转换为 Number 类型,就优先调用 valueOf;如果倾向于转换为 String 类型,就只调用 toString
倾向转向Number类型时,优先使用valueOf返回的数据,若valueOf返回的非基础数据类型将继续调用toString
valueOf返回的是基础数据类型
const obj = {
valueOf() {
return 2;
},
toString() {
return '3'
}
};
console.log(obj + 1); // 3, 走valueOf方法
valueOf返回的不是基础数据类型,那么将继续走toString方法
const obj = {
valueOf() {
// 返回非基础类型
return {};
},
toString() {
return '3'
}
};
console.log(obj + 1); // '31' 走toString方法
倾向转换为String类型,优先使用toString返回的数据,若toString返回的非基础数据类型将继续调用valueOf
toString返回的是基础数据类型
const obj = {
valueOf() {
// 返回非基础类型
return 2;
},
toString() {
return '3'
}
};
// 内容为 '3', 走toString方法
document.write(obj);
alert(obj)
toString返回的是非基础数据类型,则会继续调用valueOf
const obj = {
valueOf() {
return 2;
},
toString() {
// 返回非基础类型
return {}
}
};
// 内容为 2, 走valueOf方法
document.write(obj);
alert(obj)
到了这里,读者会不会想着,若是toString和valueOf返回的都是非基础类型的数据,那么此时该是怎样呢?
答案是报错:Uncaught TypeError: Cannot convert object to primitive value