序言
在上篇文章中我们了解了原始数据类型之间的相互转换规则以及原始数据类型转换成对象的规则,它们就像JS转换规则中的基石,我们今天要讲的是对象到原始值的转换机制,最后就要利用到原始数据类型之间的相互转换机制。那么话不多说,开始我们今天的内容吧~
首先,在聊对象到原始值转换机制前我们还需要搞明白两个类型转换方法:
valueOf()
和toString()
。
valueOf()
在JavaScript中,valueOf
方法通常用于包装对象(Wrapper Objects)中,其中包括 Number
、String
和 Boolean
。这些包装对象是由 JavaScript 自动创建的,以便处理基本数据类型和对象之间的转换。valueOf
方法被设计为返回原始数据类型的值,以便在需要时进行自动转换。
需要注意的是,valueOf
方法并非只在包装对象中有用。你可以在自定义对象上实现 valueOf
方法,以便在需要时定义对象的原始值。这样的行为通常是为了更好地控制对象的转换行为。
1. Number 对象:
Number
对象表示数字值,并有一个 valueOf
方法,用于返回原始数值。
javascript
let numObj = new Number(42);
console.log(numObj.valueOf()); // 输出 42
2. String 对象:
String
对象表示字符串,并有一个 valueOf
方法,用于返回原始字符串。
javascript
let strObj = new String("Hello");
console.log(strObj.valueOf()); // 输出 "Hello"
3. Boolean 对象:
Boolean
对象表示布尔值,并有一个 valueOf
方法,用于返回原始布尔值。
javascript
let boolObj = new Boolean(true);
console.log(boolObj.valueOf()); // 输出 true
toString()
其实引用数据类型转字符串就是调用原型上Object.prototype.toString()方法,但是这个方法对于不同类型的数据进行了分类处理:
{}.toString() 返回由 "[Object" 和 class 和 "]" 组成的字符串
[].toString() 返回由数组内部元素以逗号拼接的字符串
xx.toString() 直接返回字符串字面量
1. Object.toString()
默认情况下,如果对象没有覆盖
toString
方法,它会继承Object.prototype.toString
方法的行为,返回一个由[object
和对象的class 属性值
和]
组成的字符串
。
javascript
{}.toString() // 返回 "[object Object]"
2. Array.toString()
数组对象的 toString
方法返回数组内部元素以逗号拼接的字符串。
javascript
[1, 2, 3].toString() // 返回 "1,2,3"
3. xx.toString()
对象可以定义自己的 toString
方法,以覆盖默认行为。
javascript
const customObject = {
toString: function() {
return "Custom Object";
}
};
customObject.toString() // 返回 "Custom Object"
ToPrimitive(Object数据类型转原始数据类型)
在JavaScript中,ToPrimitive
是一个抽象操作,用于将一个值转换为对应的原始值。原始值可以是字符串、数字或布尔值。ToPrimitive
的行为是在某些操作中定义的,例如使用 ==
运算符进行比较、调用 Object.prototype.toString
方法等。
ToPrimitive
操作是由 ECMAScript规范(ES5官方文档) 定义的:
不得不说,有没有一种官方文档能人性化一点,为了让文档无懈可击,可是真折磨读者,所以大家不必纠结官方文档的文字多么苦涩难懂,我给大家取其精华,去其糟粕完如下:
ToPrimitive(obj, Number)
当我们希望将对象转换为数字时,ToPrimitive(obj, Number)
的操作步骤如下:
- 如果参数
obj
是基本数据类型,直接返回该值,因为基本数据类型已经是原始值。 - 否则,调用对象的
valueOf
方法。如果valueOf
返回原始值,则直接返回。 - 如果
valueOf
方法未返回原始值,则调用对象的toString
方法。如果toString
返回原始值,则直接返回。 - 如果
toString
方法也未返回原始值,则抛出错误,因为无法将对象转换为数字。
ToPrimitive(obj, String)
当我们希望将对象转换为字符串时,ToPrimitive(obj, String)
的操作步骤如下:
- 如果参数
obj
是基本数据类型,直接返回该值,因为基本数据类型已经是原始值。 - 否则,调用对象的
toString
方法。如果toString
返回原始值,则直接返回。 - 如果
toString
方法未返回原始值,则调用对象的valueOf
方法。如果valueOf
返回原始值,则直接返回。 - 如果
valueOf
方法也未返回原始值,则抛出错误,因为无法将对象转换为字符串。
对象转布尔值
在 JavaScript 中,对象在布尔上下文中被视为 true
。因此,对于 ToPrimitive(obj, Boolean)
操作,直接返回 true
。
总结
md
- ToPrimitive(obj,Number) ==> Number()
1. 如果参数obj是数据基本类型,直接返回
2. 否则,调用 `valueOf` 方法,如果得到原始值,则返回
3. 否则,调用 `toString` 方法,如果得到原始值,则返回
4. 否则,报错
- ToPrimitive(obj,String) ==> String()
1. 如果参数obj是数据基本类型,直接返回
2. 否则,调用 `toString` 方法,如果得到原始值,则返回
3. 否则,调用 `valueOf` 方法,如果得到原始值,则返回
4. 否则,报错
# 对象转布尔就是true
- ToPrimitive(obj,Boolean) ==> 直接True
对象转Number
来到了这里,如果前面的步骤你已经读懂了,其实这一步只是总结或者说是引擎显示执行的一步,我们如果想让对象转Number类型,只需要调用Numebr()方法则行,但是引擎隐式执行的步骤其实还有valueOf()
以及ToPrimitive(obj,Number)
,其中ToPrimitive()
方法我们是无法显示使用的,下面我们来对引擎执行的底层机制来进行一个总结梳理吧~
To Number Conversions
我们来看ES5官方文档中对ToNumber方法的定义:
首先通过
ToPrimitive
方法使对象类型转换成基本数据类型然后再利用
ToNumber
方法使原始数据类型转换成Number类型。
举一反三 结合实战
一元运算符
在 JavaScript 中,一元加号操作符 +
不仅仅用于数学运算,它还可以用于将一个数据转换为Number
类型,我们其实已经知道它的转换过程了,那么让我们通过一些例子来深入了解一元加号操作符在不同情境下的行为吧。
转换字符串为数字
javascript
console.log(+'1'); // Number('1')
在这个例子中,一元加号操作符将字符串 '1'
转换为数字。这其实就是执行了正常的原始数据类型相互转换中的字符串转Number
。
空数组转换为数字
javascript
console.log(+ []); // 0
这里对空数组使用一元加号会尝试将数组转换为数字,首先就是通过ToPrimitive
方法使对象类型转换成基本数据类型,我们已经知道会将这个数组转换成字符串类型,空数组转换成字符出为'0',最后转换为数字类型结果为数字 0
。
空对象转换为数字
javascript
console.log(+ {}); // NaN
这里对空对象使用一元加号会尝试将对象转换为数字,首先就是通过ToPrimitive
方法使对象类型转换成基本数据类型,我们已经知道会将这个对象转换成字符串"[object Object]"
,最后转换为数字类型结果为数字 NaN
。
尝试将包含元素的数组转换为数字
arduino
javascriptCopy code
console.log(+ [1,2,3]); // NaN
对包含元素的数组使用一元加号同样会尝试将数组转换为数字。由于数组中有多个元素,通过ToPrimitive
方法将其转换为基本数据类型时会变成字符串"1,2,3"
,最后转换成数字类型为NaN
二元运算符
二元运算符其实也是遵循讲左右两边的数转为Number类型进行计算,但是当加号两边有字符串出现时则按字符串进行拼接。
js
console.log(1 + '1'); // 11
console.log(1 + null); // 1
console.log([] + {}); // [object Object]
console.log(1 + '1');
- 由于
1
是数字,而'1'
是字符串,当它们相加时,会发生字符串拼接而不是数学运算。结果是字符串'11'
。
console.log(1 + null);
- 当数字和
null
进行加法运算时,null
会被转换为数字0
。因此,结果是数字1
。
console.log([] + {});
- 对于空数组
[]
和空对象{}
,JavaScript 会尝试将它们转换为字符串并进行字符串拼接。由于数组和对象的默认toString
方法返回的是字符串""
和"[object Object]"
,所以最终结果是字符串"[object Object]"
。
进阶
还有一些经典题目让我们来看看:
js
console.log(NaN == NaN); //false
console.log(true == 1); //true
console.log(1 == {} ); //false
console.log({} == {}); //false
这些代码的解释如下:
console.log(NaN == NaN);
- 在 JavaScript 中,NaN(Not a Number)是一个特殊的数值,官方文档规定它与任何值,包括自身,进行比较都会返回
false
。因此,NaN == NaN
返回false
是符合规范的行为。
console.log(true == 1);
- 在这里,布尔值
true
会被转换为数字1
。因此,这个比较返回true
。
console.log(1 == {});
- 在这里,空对象
{}
会被转换为字符串"[object Object]"
,然后再转换为数字NaN
。因此,这个比较相当于1 == 'NaN'
,最终返回false
。
console.log({} == {});
- 在这里,两个空对象进行比较。然而,对象之间的比较不会比较它们的内容,而是比较它们在内存中的引用地址。因此,两个独立的空对象的比较会返回
false
。
回到面试题
到了尾声,我们再回过头来看我们刚开始抛出的那道有趣且复杂的问题,你现在是否能够解答这道题并且说明思路?
js
console.log([] == ![]);
这道题的输出结果就是true
,接下来我们来理清楚引擎的执行过程。
步骤:
看到
!
号,先将对象[]
转换成Boolean
类型值为,![]
的结果为false
[]
和false
都转换成基本数据类型然后也就可以写成
console.log('' == 0)
;最后都转换成Number类型,也就可以写成
console.log(0 == 0)
,结果为true
总结
那么到了这里,我们这两次关于JS中类型转换规则就讲完了,第一篇文章很基础但是很重要,因为我们发现,JS引擎进行类型转换的最底层逻辑其实就是将复杂数据类型一直尝试转换成原始数据类型再进行比较。 这是这两次内容汇总的笔记总结:
md
# 基本数据类型的转换
1. 转布尔值 只有字符串和数字可以转换成布尔值为true 并且需要有实际的值
2. 转数字
3. 转字符串
4. 转对象
# 对象转原始值
- 转字符串 调用的其实就是Object.prototype.toString()
1. {}.toString() 返回由 "[Object" 和 class 和 "]" 组成的字符串
2. [].toString() 返回由数组内部元素以逗号拼接的字符串
3. xx.toString() 直接返回字符串字面量
- valueOf
用于转换包装类 只对包装类中的三种原始数据类型对象转换成原始数据类型有效
# ToPrimitive(Object数据类型转原始数据类型)
- ToPrimitive(obj,Number) ==> Number()
1. 如果参数obj是数据基本类型,直接返回
2. 否则,调用 `valueOf` 方法,如果得到原始值,则返回
3. 否则,调用 `toString` 方法,如果得到原始值,则返回
4. 否则,报错
- ToPrimitive(obj,String) ==> String()
1. 如果参数obj是数据基本类型,直接返回
2. 否则,调用 `toString` 方法,如果得到原始值,则返回
3. 否则,调用 `valueOf` 方法,如果得到原始值,则返回
4. 否则,报错
# 对象转布尔就是true
- ToPrimitive(obj,Boolean) ==> 直接True
# 一元运算符 +
一元加号操作符 `+` 不仅仅用于数学运算,它还可以用于将一个数据转换为`Number`类型。
# 二元运算符 +
lprim + rprim
ToPrimitive(v1) + ToPrimitive(v2)
1. 当+两边有一个是字符串则按字符串进行拼接
2. 否则,转到 number 进行计算
结语
那么到了这里我们今天的文章就结束啦~
创作不易,如果感觉这个文章对你有帮助的话,点个赞吧♥
更多内容:面试官:能不能手写浅拷贝和深拷贝?其实只要搞懂引用类型的存储方式
博主的开源Git仓库 欢迎收藏: gitee.com/cheng-bingw...