犀牛书笔记-对象到原始值转换

概述

JavaScript对象到原始值转换的复杂性,主要原因在于某些对象类型有不止一种原始值的表示。比如,Date对象可以用字符串表示,也可以用时间戳表示。

因此JavaScript规范定义了多种对象到原始值的转换算法,不同的对象使用不同的转换算法。

对象到原始值转换算法

先来了解两个方法:

  • toString()方法

该方法的任务是返回对象的字符串表示。很多类都定义了自己特有的toString()版本,比如:

1
2
3
4
[1, 2, 3].toString()               // => '1,2,3'
/\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()方法

这个方法的任务并没有太明确的定义,大体上可以认为它是把对象转换为代表对象的原始值(如果存在)。对象是复合值,且多数对象并不能真正通过一个原始值来表示,因此valueOf()方法默认情况下只返回对象本身,而非返回原始值。特例是Date对象,对valueOf方法进行了重写,返回的是1970.1.1至今的毫秒数:

1
(new Date(2020,0,1)).valueOf()      // 1577808000000

解释完 toString() 和 valueOf() 方法后,进一步解释对象到原始值的转换算法,JavaScript规范定义了三种对象到原始值的转换算法:

  • 偏字符串算法

    首先尝试 toString() 方怯。如果这个方位有定义且返回原始值,则JavaScript使用该原始值(即使这个值不是字符串)。如果 toString() 不存在,或者存在但返回对象, 则 JavaScript 尝试 valueOf () 方怯。如果这个方告存在且返回原始值 ,则 JavaScript 使用该值。否则,转换失败,报 TypeError。

  • 偏数值算法

    与偏字符串算法类似,只不过先尝试 valueOf() 方法,再尝试 toString() 方法。

  • 无偏好算法

    该算法不倾向于任何原始值类型,而是由类定义自己的转换规则。JavaScript内置类型除了Date类都实现了偏数值算法,Date类实现了偏字符串算法。

总结

  1. 对象转换为布尔值

    所有对象都转换为true,不需要用到前面所说的三种算法,像空数组、new Boolean(false)这样的包装对象。

  2. 对象转换为字符串

    JavaScript会首先使用偏字符串算法将它转换为一个原始值,然后将得到的原始值再转换为字符串

  3. 对象转换为数值

    JavaScript会首先使用偏数值算法将它转换为一个原始值,然后再将得到的原始值再转换为数值。

    这里看一个例子:

    1
    2
    3
    Number([])     // => 0
    Number([99]) // => 99
    Number({}) // => NaN

    Number()会使用偏数值算法,先尝试 valueOf() ,由于 Array类继承了默认的valueOf方法,该方法不返回原始值,因此在尝试将数组转换为数值时,最终会调用 toString() 方法。先将 [] 和 [99] 转换为字符串,再将字符串转换为数值。Array定义了自己特有的toString()方法,[] 和 [99] 分别会被转换成空字符串和 ’99‘,再转换成数值0和99。而普通对象调用toString()方法后,会被转换成 [object Object],再转换成数值就是NaN。