引用数据类型之间的转换机制
1. 序言
首先,在聊对象到原始值转换机制前我们需要搞明白两个类型转换方法:valueOf()
和 toString()
。
1.1 valueOf()
在JavaScript中,valueOf
方法通常用于包装对象(Wrapper Objects)中,其中包括 Number
、String
和 Boolean
。这些包装对象是由 JavaScript 自动创建的,以便处理基本数据类型和对象之间的转换。valueOf
方法被设计为返回原始数据类型的值,以便在需要时进行自动转换。
需要注意的是,valueOf
方法并非只在包装对象中有用。你可以在自定义对象上实现 valueOf
方法,以便在需要时定义对象的原始值。这样的行为通常是为了更好地控制对象的转换行为。
Number 对象:表示数字值,并有一个 valueOf
方法,用于返回原始数值。
let numObj = new Number(42);
console.log(numObj.valueOf()); // 42
String 对象:表示字符串,并有一个 valueOf
方法,用于返回原始字符串。
let strObj = new String("Hello");
console.log(strObj.valueOf()); // "Hello"
Boolean 对象:表示布尔值,并有一个 valueOf
方法,用于返回原始布尔值。
let boolObj = new Boolean(true);
console.log(boolObj.valueOf()); // true
1.2 toString()
其实引用数据类型转字符串就是调用原型上Object.prototype.toString()方法,但是这个方法对于不同类型的数据进行了分类处理。
Object.toString():默认情况下,如果对象没有覆盖 toString
方法,它会继承 Object.prototype.toString
方法的行为,返回一个由 [object
和对象的 class 属性值
和 ]
组成的字符串
。
let obj = {};
obj.toString(); // '[object Object]'
Array.toString():数组对象的 toString
方法返回数组内部元素以逗号拼接的字符串。
let arr = [1,2,3];
arr.toString(); // '1,2,3'
xx.toString():对象可以定义自己的 toString
方法,以覆盖默认行为。
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)
的操作步骤如下:
- 如果参数
obj
是基本数据类型,直接返回该值,因为基本数据类型已经是原始值。 - 否则,调用对象的
valueOf
方法。如果valueOf
返回原始值,则直接返回。 - 如果
valueOf
方法未返回原始值,则调用对象的toString
方法。如果toString
返回原始值,则直接返回。 - 如果
toString
方法也未返回原始值,则抛出错误,因为无法将对象转换为数字。
2.2 ToPrimitive(obj, String)
当我们希望将对象转换为字符串时,ToPrimitive(obj, String)
的操作步骤如下:
- 如果参数
obj
是基本数据类型,直接返回该值,因为基本数据类型已经是原始值。 - 否则,调用对象的
toString
方法。如果toString
返回原始值,则直接返回。 - 如果
toString
方法未返回原始值,则调用对象的valueOf
方法。如果valueOf
返回原始值,则直接返回。 - 如果
valueOf
方法也未返回原始值,则抛出错误,因为无法将对象转换为字符串。
2.3 对象转布尔值
在 JavaScript 中,对象在布尔上下文中被视为 true
。因此,对于 ToPrimitive(obj, Boolean)
操作,直接返回 true
。
2.4 ToPrimitive 总结
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官方文档。
- 首先通过
ToPrimitive
方法使对象类型转换成基本数据类型。 - 然后再利用
ToNumber
方法使原始数据类型转换成 Number 类型。
4. 举一反三 结合实战
4.1 一元运算符
在 JavaScript 中,一元加号操作符 +
不仅仅用于数学运算,它还可以用于将一个数据转换为 Number
类型,
console.log(+'1'); // Number('1')
console.log(+[]); // 0
console.log(+{}); // NaN
console.log(+[1,2,3]); // NaN
4.2 二元运算符
二元运算符其实也是遵循讲左右两边的数转为 Number 类型进行计算,但是当加号两边有字符串出现时则按字符串进行拼接。
console.log(1 + '1'); // 11
console.log(1 + null); // 1
console.log([] + {}); // [object Object]
对于空数组 []
和空对象 {}
,JavaScript 会尝试将它们转换为字符串并进行字符串拼接。
4.3 进阶
// 官方文档规定它与任何值,包括自身,进行比较都会返回 false
console.log(NaN == NaN); // false
// 布尔值 true 会被转换为数字 1
console.log(true == 1); // true
// 空对象 {} 会被转换为字符串 "[object Object]",然后再转换为数字 NaN
console.log(1 == {}); // false
// 对象之间的比较不会比较它们的内容,而是比较它们在内存中的引用地址
console.log({} == {}); // false
5. [] == ![]
具体步骤:
[] == false
:![]的结果是
false。[] == 0
:在比较一个对象(例如 [])和布尔值(例如 false)时,JavaScript 会将布尔值转换为数字。'' == 0
:在比较一个对象(例如 [])和数字时,JavaScript 会将对象转换为原始值。0 == 0
:在比较字符串和数字时,JavaScript 会将字符串转换为数字。true
。