Skip to content
On This Page

引用数据类型之间的转换机制

1. 序言

首先,在聊对象到原始值转换机制前我们需要搞明白两个类型转换方法:valueOf()toString()

1.1 valueOf()

在JavaScript中,valueOf 方法通常用于包装对象(Wrapper Objects)中,其中包括 NumberStringBoolean。这些包装对象是由 JavaScript 自动创建的,以便处理基本数据类型和对象之间的转换。valueOf 方法被设计为返回原始数据类型的值,以便在需要时进行自动转换。

需要注意的是,valueOf 方法并非只在包装对象中有用。你可以在自定义对象上实现 valueOf 方法,以便在需要时定义对象的原始值。这样的行为通常是为了更好地控制对象的转换行为。

Number 对象:表示数字值,并有一个 valueOf 方法,用于返回原始数值。

javascript
let numObj = new Number(42);
console.log(numObj.valueOf());  // 42

String 对象:表示字符串,并有一个 valueOf 方法,用于返回原始字符串。

javascript
let strObj = new String("Hello");
console.log(strObj.valueOf());  // "Hello"

Boolean 对象:表示布尔值,并有一个 valueOf 方法,用于返回原始布尔值。

javascript
let boolObj = new Boolean(true);
console.log(boolObj.valueOf());  // true

1.2 toString()

其实引用数据类型转字符串就是调用原型上Object.prototype.toString()方法,但是这个方法对于不同类型的数据进行了分类处理。

Object.toString():默认情况下,如果对象没有覆盖 toString 方法,它会继承 Object.prototype.toString 方法的行为,返回一个由 [object 和对象的 class 属性值] 组成的字符串

javascript
let obj = {};
obj.toString();  // '[object Object]'

Array.toString():数组对象的 toString 方法返回数组内部元素以逗号拼接的字符串。

javascript
let arr = [1,2,3];
arr.toString();  // '1,2,3'

xx.toString():对象可以定义自己的 toString 方法,以覆盖默认行为。

javascript
const customObject = {
  toString: function() {
    return "Custom Object";
  }
};
customObject.toString();  // "Custom Object"

2. ToPrimitive

ToPrimitive (Object 数据类型转原始数据类型)。在 JavaScript 中,ToPrimitive 是一个抽象操作,用于将一个值转换为对应的原始值。原始值可以是字符串、数字或布尔值。ToPrimitive 的行为是在某些操作中定义的,例如使用 == 运算符进行比较、调用 Object.prototype.toString 方法等。

ToPrimitive 操作是由 ECMAScript 规范(ES5 官方文档) 定义的:ToPrimitive Conversions官方文档

2.1 ToPrimitive(obj, Number)

当我们希望将对象转换为数字时,ToPrimitive(obj, Number) 的操作步骤如下:

  1. 如果参数 obj 是基本数据类型,直接返回该值,因为基本数据类型已经是原始值。
  2. 否则,调用对象的 valueOf 方法。如果 valueOf 返回原始值,则直接返回。
  3. 如果 valueOf 方法未返回原始值,则调用对象的 toString 方法。如果 toString 返回原始值,则直接返回。
  4. 如果 toString 方法也未返回原始值,则抛出错误,因为无法将对象转换为数字。

2.2 ToPrimitive(obj, String)

当我们希望将对象转换为字符串时,ToPrimitive(obj, String) 的操作步骤如下:

  1. 如果参数 obj 是基本数据类型,直接返回该值,因为基本数据类型已经是原始值。
  2. 否则,调用对象的 toString 方法。如果 toString 返回原始值,则直接返回。
  3. 如果 toString 方法未返回原始值,则调用对象的 valueOf 方法。如果 valueOf 返回原始值,则直接返回。
  4. 如果 valueOf 方法也未返回原始值,则抛出错误,因为无法将对象转换为字符串。

2.3 对象转布尔值

在 JavaScript 中,对象在布尔上下文中被视为 true。因此,对于 ToPrimitive(obj, Boolean) 操作,直接返回 true

2.4 ToPrimitive 总结

markdown
ToPrimitive(obj,Number)  ==> Number()
1. 如果参数obj是数据基本类型,直接返回
2. 否则,调用 `valueOf` 方法,如果得到原始值,则返回 
3. 否则,调用 `toString` 方法,如果得到原始值,则返回
4. 否则,报错

ToPrimitive(obj,String)  ==> String()
1. 如果参数obj是数据基本类型,直接返回
2. 否则,调用 `toString` 方法,如果得到原始值,则返回
3. 否则,调用 `valueOf` 方法,如果得到原始值,则返回 
4. 否则,报错

ToPrimitive(obj,Boolean)  ==> 直接 true
对象转布尔直接就是 true

3. 对象转 Number

来到了这里,如果前面的步骤你已经读懂了,其实这一步只是总结或者说是引擎显示执行的一步,我们如果想让对象转Number类型,只需要调用 Numebr() 方法则行,但是引擎隐式执行的步骤其实还有 valueOf() 以及 ToPrimitive(obj,Number),其中 ToPrimitive() 方法我们是无法显示使用的,下面我们来对引擎执行的底层机制来进行一个总结梳理。

To Number Conversions:我们来看ES5官方文档中对ToNumber方法的定义,To Number Conversions官方文档

  1. 首先通过 ToPrimitive 方法使对象类型转换成基本数据类型。
  2. 然后再利用 ToNumber 方法使原始数据类型转换成 Number 类型。

4. 举一反三 结合实战

4.1 一元运算符

在 JavaScript 中,一元加号操作符 + 不仅仅用于数学运算,它还可以用于将一个数据转换为 Number 类型,

javascript
console.log(+'1');      // Number('1')
console.log(+[]);       // 0
console.log(+{});       // NaN
console.log(+[1,2,3]);  // NaN

4.2 二元运算符

二元运算符其实也是遵循讲左右两边的数转为 Number 类型进行计算,但是当加号两边有字符串出现时则按字符串进行拼接。

javascript
console.log(1 + '1');   // 11
console.log(1 + null);  // 1
console.log([] + {});   // [object Object]

对于空数组 [] 和空对象 {},JavaScript 会尝试将它们转换为字符串并进行字符串拼接。

4.3 进阶

javascript
// 官方文档规定它与任何值,包括自身,进行比较都会返回 false
console.log(NaN == NaN);  // false

// 布尔值 true 会被转换为数字 1
console.log(true == 1);   // true

// 空对象 {} 会被转换为字符串 "[object Object]",然后再转换为数字 NaN
console.log(1 == {});     // false

// 对象之间的比较不会比较它们的内容,而是比较它们在内存中的引用地址
console.log({} == {});    // false

5. [] == ![]

具体步骤:

  1. [] == false:![] 的结果是 false。
  2. [] == 0:在比较一个对象(例如 [])和布尔值(例如 false)时,JavaScript 会将布尔值转换为数字。
  3. '' == 0:在比较一个对象(例如 [])和数字时,JavaScript 会将对象转换为原始值。
  4. 0 == 0:在比较字符串和数字时,JavaScript 会将字符串转换为数字。
  5. true