上期在阅读《Javascript权威指南》第六版类型转换这一章节的时候,我虽然搞清楚了之前留下的很多疑问,比如说数据类型转换的基本规则,对象到原始值的转换规则等。但是对于书中3.8.3节(对象转换为原始值)中的一段文字存有疑惑,今天回头又看了一遍,总算是搞明白了。
首先引用下这段文字。
“+”和“==”应用的对象到原始值的转换包含日期对象的一种特殊情形。日期类是JavaScript语言核心中唯一的预先定义类型,它定义了有意义的向字符串和数字类型的转换。对于所有非日期的对象来说,对象到原始值的转换基本上是对象到数字的转换(首先调用valueOf),日期对象则使用对象到字符串的转换模式,然而,这里的转换和上文讲述的并不完全一致:通过valueOf或toString返回的原始值将被直接使用,而不会被强制转换为数字或字符串。
和“==”一样,“<”运算符以及其他关系运算符也会做对象到原始值的转换,但要除去日期对象的特殊情形:任何对象都会首先尝试调用valueOf,然后调用toString。不管得到的原始值是否直接使用,它都不会进一步被转换为数字或字符串。
“+”、“==”、“!=”和关系运算符是唯一执行这种特殊的字符串到原始值的转换方式的运算符。其他运算符到特定类型的转换都很明确,而且对日期对象来讲也没有特殊情况。例如“-”(减号)运算符把它的两个操作数都转换为数字。
复制这么长一段文字呢,也不是为了凑字数,是我一开始真的没看明白这段。因为我一直纠结在这节内容前面说的对象转换为原始值的规则,死死地认为:
- 对象转原始值都应该按照两条路线走,一条路线是转为字符串,一条路线是转为数字。
- 对象转为字符串这条路线,是优先调用
toString()
方法,其次调用valueOf()
方法,如果最后得到的原始值不是字符串,再把这个原始值转为字符串。 - 对象转为数字这条路线,是优先调用
valueOf()
方法,其次调用toString()
方法,如果最后得到的原始值不是数字,再把这个原始值转为数字。 - 否则就抛出类型错误。
这里写的转换规则比较粗略了,因为上一篇笔记中已经提到了比较详细的规则了,这里就捡重点看了。
掉进这个规则里,我就产生了固化思维,觉得所有的对象转原始值的情况都应该按这个规则来。所以对上面引用的这段话就开始想不明白了。大概产生了这些疑问:
- 引文中第一段的最后一句“通过valueOf或toString返回的原始值将被直接使用,而不会被强制转换为数字或字符串。”。我的疑惑是:为什么最后不会再强制转换了?
- 第二段中提到的“关系运算符中对象到原始值的转换,都会首先调用valueOf,然后调用toString”。我的疑惑是:为什么日期对象又不特殊处理(首先调用toString)了呢?
其实我上篇写到最后一小节隐式转换的时候,已经提到了,不同运算符对于对象的转换规则是特殊的。
在不同的使用场景中,
javascript
会根据实际情况进行类型的隐式转换。
可能是写完之后回头看这段文字又串戏了,懵逼了。
其实还是要看javascript
到底期望什么类型的操作数。之所以+
, ==
比较特殊,是因为javascript
不太明确操作数的类型。
就拿+
来说吧,可能是用来做数字加法,也可能是用来拼接字符串。所以javascript
必须把这些情况都考虑到,针对这个运算符来定个特殊的规则。
而==
是相等运算符,与恒等运算符===
是不一样的。恒等运算符会首先判断数据类型是否一致,而==
运算符不要求两个操作数类型一致,当两个操作数不一致时,会按照一定的规则进行操作数的隐式转换。
而一些其他算术运算符,比如-
, *
, /
,它们都很明确操作数的类型,希望操作数是数字。所以即使你给的操作数不是数字,它也会转为数字来运算。
所以,如果其中一个操作数是对象,会发生对象到原始值的转换,然后这个原始值也会被转为数字(如果这个原始值本身不是数字)。
1 | var a = {name: '飞白'}; |
大概的思路是这个样子的,而每个运算符是怎么样的一个运算流程,还是要看去看它的官方说明。
写这么一篇没什么实际内容的东西,主要还是想记录下自己的这种疑惑吧,希望自己以后不要再被这种文字绕进去了,要多想想程序这样设计到底是为了解决什么问题,这样才能更容易理解或猜到规则背后的逻辑。