0. 前言
这篇文章是《你所不知道的JavaScript》读书笔记系列的第七篇文章。在这篇文章中,我想跟大家一起聊的内容是强制类型转换。
- 《你所不知道的JavaScript》读书笔记(一):作用域和闭包(上)
- 《你所不知道的JavaScript》读书笔记(一):作用域和闭包(下)
- 《你所不知道的JavaScript》读书笔记(二):this指向问题
- 《你所不知道的JavaScript》读书笔记(三):对象和类(上)
- 《你所不知道的JavaScript》读书笔记(三):对象和类(下)
- 《你所不知道的JavaScript》读书笔记(四):类型与值
1. 值类型转换
将值从一种类型显式的转换为另一种类型通常称为类型转换(也叫显示强制类型转换),隐式的情况称为强制类型转换。也可以这样来区分:类型转换发生在静态类型语言的编译阶段,而强制类型转换则发生在动态类型语言的运行时。二者的区别如下:
- 我们能够从代码中看出哪些地方式显示强制类型转换
- 隐式强制类型转换则不那么明显,通常是某些操作产生的副作用 举个栗子:
var a = 42
var b = a + "" // 隐式强制类型转换
var c = String(a) // 显示强制类型转换
1.1 显式强制类型转换
显式强制类型转换式那些显而易见的类型转换,主要发生在数字字符、字符串和布尔类型的值之间。
1.1.1 字符串和数字之间的显式转换
字符串和数字之间的转换时通过String(...)
和Number(...)
这两个内建函数来实现的,例子如下:
var a = 42
var b = String(a)
var c = "3.14"
var d = Number(c)
这里需要注意:他们前面都没有new
关键字,因此并不创建封装对象。 接下来我们来分别聊一聊String()
和Number()
这两个内建函数。
String(..)
:使用这个函数进行强制类型转换时,遵循基本类型值的字符串化规则:null
转换为"null"
;undefined
转换为"undefined"
;true
转换为"true"
;数字的字符串化则遵循通用规则;对普通对象来说,除非自定义,否则强制类型转换后的结果是内部属性[[class]]
的值。Number(..)
:使用这个函数进行强制类型转换时,遵循数字常量的相关规则:null
转换为0
;undefined
转换为NaN
;true
转换为1
;false
转换为0
;处理失败是返回NaN
。 除此之外,对象的toString()
方法和toNumber()
方法也可以完成字符串和数字之间的转化,所采用的规则如上。
1.1.2 显示解析数字字符串
我们采用parseInt()
来解析数字字符串。解析字符串中的数字和字符串强制类型转换为数字的返回结果都是数字,但解析和转换两者之间还是有明显的区别。解析字符串允许字符串中含有非数字字符;转换不允许出现非数字字符。 举个栗子:
var a = "42"
var b = "42px"
Number(a) // 42
ParseInt(a) // 42
Number(b) // NaN
ParseInt(b) // 42
注意:parseInt()
针对的时字符串值。向parseInt()
传递数字和其他类型的参数都是没用的。非字符串参数会首先被强制转换为字符串,虽然存在隐式强制转换机制,但是我们应该避免向parseInt()
传递非字符串参数
1.1.3 显示转换为布尔值
采用Boolean()
函数或者对象的toBoolean()
方法可以将其他类型的值强制转化为布尔类型。转换规则为除了以下几种值(这些值被称为假值),其余的都被强制转化为true
:
- undefined
- null
- false
- +0、-0、NaN
- "" 这里有个奇怪的现象:
var a = new Boolean(false)
var b = new Number(0)
var c = new String("")
var d = Boolean(a && b && c)
这段代码的运行结果是true
,说明每个封装了假值的对象强制类型转换之后都是true
.这与我们的认知不相符。书中给的解释如下:
虽然JavaScript代码中会出现假值对象,但它实际上并不属于JavaScript语言的范畴。浏览器在某些特定情况下,在常规JavaScript语法基础上自己创建了一些外来值,这些就是“假值对象”。假值对象看起来和不同对象并无二致,但将他们强制类型转换为布尔值时结果为false 但是我觉得他这个解释是有问题的。采用
new
关键字创建的假值对象强制类型转换之后会变成true
,其他情况不会。所以我觉得应该是采用new
关键字的原因。我们来回顾一下,new关键字做了什么事情:
- 创建一个全新的对象
- 这个新对象被执行[[Prototype]]连接
- 这个新对象会绑定到函数调用的this
- 如果函数没有返回其他对象,那么这个表达式中的函数调用会自动返回这个新对象 然后,我又在浏览器的控制台用两种方式创建假值对象,得到如下的结果:
好了,现在破案了。利用new
关键字得到的返回值一定是个对象,而不用new
关键字得到的返回值就不一定是对象。对象经过强制类型转换之后一定会是true
.
1.2 隐式强制类型转换
隐式强制类型转换指的是那些隐蔽的强制类型转换,副作用也不是很明显。换句话说,你自己绝不不够明显的强制类型转换都可以算作隐式强制转换。在这一部分,我们主要的关注点在于几种隐式强制类型转换的方式:
+String
转换成数字~x ==> -(x+1)
~~Number
可以接触数字值的小数部分!!任意值
可以完成到布尔值的转换Number + ""
转换为字符串- 隐式强制转换为布尔值:
- 发生条件:
- if语句的条件判断表达式
- for语句中的条件判断表达式
- while和do…while循环中的条件判断表达式
- 三目运算符中的条件判断表达式
- 逻辑运算符||(逻辑或)和&&(逻辑与)左边的操作数
- || 和 &&
- && 和 || 运算符的返回值不一定是布尔类型,而是两个操作数其中一个的值
- || 和 && 首先会对第一个操作数执行条件判断,如果其不是布尔值就先进行
toBoolean
强制类型转换,然后再执行条件判断 - 对于 || 来说,如果条件判断的结果为
true
就返回第一个操作数的值,如果为false
就返回第二个操作数的值 - 对于 && 来说,如果条件判断的结果为
true
就返回第二个操作数的值,如果为false
就返回第一个操作数的值
- 发生条件:
2. 宽松相等和严格相等
宽松相等(==)和严格相等(===)都用来判断两个值是否相等,他俩的区别是 ==允许在相等比较重进行强制类型转换,而===不允许
在判断相等时,有几个特殊情况:
- NaN 不等于 NaN
- +0 等于 -0
- 如果宽松相等的一方有布尔值(x==y),则遵循以下规则:
- 如果Type(x)是布尔类型,则返回ToNumber(x)==y的结果
- 如果Type(y)是布尔类型,则返回x==ToNumber(y)的结果
除此之外,还有一个宽松不相等的概念:!=就是==的相反值,!==是===的相反值