概述
JavaScript对象到原始值转换的复杂性,主要原因在于某些对象类型有不止一种原始值的表示。比如,Date对象可以用字符串表示,也可以用时间戳表示。
因此JavaScript规范定义了多种对象到原始值的转换算法,不同的对象使用不同的转换算法。
对象到原始值转换算法
先来了解两个方法:
该方法的任务是返回对象的字符串表示。很多类都定义了自己特有的toString()版本,比如:
1 2 3 4
| [1, 2, 3].toString() /\d+/g.toString() // => '/\\d+/g' (function(x) {f(x)}).toString() // => 'function(x) {f(x)}' (new Date(2020,0,1)).toString() // => 'Wed Jan 01 2020 00:00:00 GMT+0800 (中国标准时间)'
|
这个方法的任务并没有太明确的定义,大体上可以认为它是把对象转换为代表对象的原始值(如果存在)。对象是复合值,且多数对象并不能真正通过一个原始值来表示,因此valueOf()方法默认情况下只返回对象本身,而非返回原始值。特例是Date对象,对valueOf方法进行了重写,返回的是1970.1.1至今的毫秒数:
1
| (new Date(2020,0,1)).valueOf()
|
解释完 toString() 和 valueOf() 方法后,进一步解释对象到原始值的转换算法,JavaScript规范定义了三种对象到原始值的转换算法:
偏字符串算法
首先尝试 toString() 方怯。如果这个方位有定义且返回原始值,则JavaScript使用该原始值(即使这个值不是字符串)。如果 toString() 不存在,或者存在但返回对象, 则 JavaScript 尝试 valueOf () 方怯。如果这个方告存在且返回原始值 ,则 JavaScript 使用该值。否则,转换失败,报 TypeError。
偏数值算法
与偏字符串算法类似,只不过先尝试 valueOf() 方法,再尝试 toString() 方法。
无偏好算法
该算法不倾向于任何原始值类型,而是由类定义自己的转换规则。JavaScript内置类型除了Date类都实现了偏数值算法,Date类实现了偏字符串算法。
总结
对象转换为布尔值
所有对象都转换为true,不需要用到前面所说的三种算法,像空数组、new Boolean(false)这样的包装对象。
对象转换为字符串
JavaScript会首先使用偏字符串算法将它转换为一个原始值,然后将得到的原始值再转换为字符串
对象转换为数值
JavaScript会首先使用偏数值算法将它转换为一个原始值,然后再将得到的原始值再转换为数值。
这里看一个例子:
1 2 3
| Number([]) Number([99]) Number({})
|
Number()会使用偏数值算法,先尝试 valueOf() ,由于 Array类继承了默认的valueOf方法,该方法不返回原始值,因此在尝试将数组转换为数值时,最终会调用 toString() 方法。先将 [] 和 [99] 转换为字符串,再将字符串转换为数值。Array定义了自己特有的toString()方法,[] 和 [99] 分别会被转换成空字符串和 ’99‘,再转换成数值0和99。而普通对象调用toString()方法后,会被转换成 [object Object],再转换成数值就是NaN。