首先,我们知道在许多其它语言中是不支持数字与字符串的相加,其结果会报错。而我们也知道JavaScript在这种情况下会进行隐式类型转换,1+“2”会变成“1”+“2”,最终结果为“12”。
接下来,我们看看其背后的底层原理究竟是什么。
以下面代码为示例:
a+b
V8(chrome的JavaScript引擎)会提供了一个 ToPrimitve 方法,其作用是将 a 和 b 转换为原生数据类型,其转换流程如下:
- 先检测该对象中是否存在 valueOf 方法,如果有并返回了原始类型,那么就使用该值进行强制类型转换;
- 如果 valueOf 没有返回原始类型,那么就使用 toString 方法的返回值;
- 如果 vauleOf 和 toString 两个方法都不返回基本类型值,便会触发一个 TypeError 的错误。
将对象转换为原生类型的流程图如下所示:
当 V8 执行 1+“2”时,因为这是两个原始值相加,原始值相加的时候,如果其中一项是字符串,那么 V8 会默认将另外一个值也转换为字符串,相当于执行了下面的操作:
Number(1).toString() + "2"
下面再看一组更典型的例子:
var Obj1 = {
toString() {
return "200"
},
valueOf() {
return 100
}
}
Obj1+"3"
var Obj2 = {
toString() {
return "200"
},
valueOf() {
return {}
}
}
Obj2+"3"
var Obj3 = {
toString() {
return {}
},
valueOf() {
return {}
}
}
Obj3+"3"
上面三组案例的结果分别为:
"1003"
"2003"
VM263:9 Uncaught TypeError: Cannot convert object to primitive value
at <anonymous>:9:6
根据上面的运算查找流程,相信此结果应该一目了然。
简要总结一下,在JavaScript执行加法过程中,V8 会先通过 ToPrimitive 函数,将对象转换为原生的字符串或者是数字类型,在转换过程中,ToPrimitive 会先调用对象的 valueOf 方法,如果没有 valueOf 方法,则调用 toString 方法,如果 vauleOf 和 toString 两个方法都不返回基本类型值,便会触发一个 TypeError 的错误。