这些是本人在 github.pages上写的博客,欢迎你们关注和纠错,本人会按期在github pages上更新。有想要深刻了解的知识点能够留言。git
在 JavaScript 中,将一种值类型转换为另外一种值类型,叫作类型转换,出于动态型语言的特性,类型转换发生在运行时阶段。这些转换在咱们平时写的代码里无处不在,尽管咱们没有注意,可是这些转换已经存在于咱们的代码里了。像 if、for、while、==、===、+、- 等等语句中。github
而在 JavaScript 中,有两种转换风格:隐式强制类型转换和显式强制类型转换。安全
举个栗子bash
let str = 42 + '' // '42' 隐式
let anotherStr = String(42) // '42' 显示
复制代码
下面,将会从类型转换的运行机制对转换值的机制进行深刻分析oop
下面介绍一些抽象操做,ToString, ToNumber, ToBoolean, ToPrimitive 注意:这里的抽象操做不表明方法,而是对类型进行转换执行的一系列方法。性能
ToString 主要负责处理非字符串类型转换为字符串类型。咱们将待转换的类型进行划分:学习
基本类型和对象类型ui
string 类型是 JS 中很特殊,也是最重要的基本类型,基本每一个内置对象都实现了自身的 toString 方法。spa
基本类型值的操做很常规,都遵循着通用的规则。prototype
null -> 'null'
undefined -> 'undefined'
true -> 'true'
21 -> '21'
复制代码
对普通的对象而言,机制就变得复杂起来。
对象自身调用 toString() 进行字符串化或者显示字符串化(如: String() )。
基本上 obj.toString() === String(obj) 过程以下:
let arr = [1, 2, 3]
arr.toString() === String(arr) // '1,2,3'
let obj = { name: 'jack' }
obj.toString() === String(obj) // [object, Object]
复制代码
特殊状况:调用 String(obj) 时,若是对象的 toString 方法被重写为普通属性,则会退而求其次执行 valueOf 方法。若是执行的结果未返回基本类型,则会报错。
let obj = {
valueOf() {
return '12'
}
toString: undefined
}
String(obj) // 12
obj.valueOf = () => ({})
String(obj) // TypeError:Cannot convert object to primitive value
复制代码
建议:不要轻易的重写对象属性的 valueOf 和 toString 方法。由于这两个属性涉及对象的表现形式。在类型转换中相当重要。
Object.prototype.toString() 方法返回的是内部属性 [[ class ]] 的值。如([objecct, Object], [object, Function])
该抽象操做为了将对象类型转换为基本类型,步骤以下:
let nullObj = Object.create(null)
Number(nullObj) || String(nullObj) // VM3789:1 Uncaught TypeError: Cannot convert object to primitive value
let obj = {
valueOf: function() {
return '12'
}
}
Number(obj) // 12 通过的步骤 valueOf -> '12' -> 通过字符串转数字 -> 12
Number([]) // 0 通过步骤 valueOf -> [] -> toString -> '' -> 通过字符串转数字 -> 0
复制代码
ToNumber 主要负责将其余类型转换为 number 类型
处理规则:
Number('') // 0
Number(true) // 1
Number(false) // 0
Number(undefined) // NaN
Number(null) // 0
Number('1d1') // NaN
let obj = {
valueOf() {
return '142'
}
toString() {
return 'obj self'
}
}
Number(obj) // 经历过程 obj 先通过ToPrimitive抽象操做 valueOf -> '142' ->ToNumber -> 142
obj.valueOf = null // 这个时候 valueOf 不是一个方法,因此直接调用 toString()
Number(obj) // ToPrimitive 无 valueOf 方法 -> toString() -> NaN
复制代码
假植(falsy)和真值,在 JavaScript 中,除了 true 和 false,还有其余一些列的真值和假值,这些真值和假植有区别于 true 和 false 总的来讲,JavaScript中的值分为两类,真值和假值。由于 boolean 类型只有两个值,没有第三个值。
下面的这些值是假值,假值的布尔类型转换为false
除了以上列出的假值,其余全为真值。因此ToBoolean的操做也很简单。就是寻找以上假值中是否存在目标值。 存在即为false,不存在即为true
规则以下:
Number('12') // 12
+'12' // 12
Number('') // 0
+'' // 0
Number({}) // NaN
Number([]) // 0
String(12) // '12'
String({}) // '[object Object]'
String([]) // ''
Boolean('') // false
Boolean(0) // false
Boolean(1) // true
!!1 // true
!1 // false
[1,2,'',undefined,0].filter(Boolean) -> [1,2]
[1,3,'',undefined,0].filter(v -> !!v) -> [1,2]
复制代码
parseInt 方法区别于 Number 方法。不一样点有
注意:parseInt 要解析的参数,若是第一个参数以0开头,会按照八进制数据进行解析。0x 会按照十六进制进行解析。会覆盖默认的十进制解析。即便显式的指定十进制解析,也会进行覆盖。
parseInt('112ssasd') // 112
parseInt(true) // NaN
parseInt(null) // NaN
parseInt(112, 2) // 3 解析过程 112 按照二进制解析 二进制只能识别 0 和 1,因此 只能解析 11,二进制的结果为3
parseInt(012) // 10
parseInt(0x12) // 18
复制代码
常见的考题: [ 1,2,4 ].map(parseInt) 的结果,根据上述分析,显然不是[ 1,2,4 ]了。 下面的几种结果,停下来,思考一下,本身能够分析出来,也就对该方法完全掌握了
let noop = function() { }
parseInt(noop, 15)
parseInt(noop, 16)
parseInt(1/0)
parseInt(1/0, 19)
复制代码
二元运算符 + 是最重要的一个操做符,由于该运算符便可以做为两个数字进行相加,又能够做为字符串的链接符号。 如今,咱们讨论做为链接符时的注意点以及相关规则。
规则:
先将 + 两侧的数据类型转化为基本数据类型(ToPrimitive 抽象操做),若是一边有字符串,那么此时,就做为链接符使用。
注意点:
let num = 1
num + '' // '1'
let bool = true
bool + '' // 'true'
let nul = null
nul + '' // 'nul'
let arr = [ 1,2,3 ]
arr + '' // '1,2,3'
let obj = {}
obj + '' // '[object Object]'
arr.valueOf = () => '111'
arr + '' // '111'
arr + 1 // '1111'
1 + [1] // '11'
复制代码
数字的隐式转换有多种,像二元操做符 + - * / % 均可以对类型进行数字的隐式类型转换,咱们先来讨论特殊的 +
null + 1 // 1
true + 1 // 2
false + 1 // 1
undefined + 1 // NaN
let obj = {
valueOf: function() {
return 12
}
}
obj + 1 // 13
复制代码
而其余的二元操做符 如: -、*、%、/ 都会对操做符两侧进行 ToNumber 抽象操做。至关于 Number(variable)
1 - '12' // -11 至关于 1 - Number('12')
1 * '12' // 12
1 - [1] // 0
1 / '12' // 0.08333333333333333
1 % '12' // 1
复制代码
布尔值的隐式类型转换规则很简单 参照上述的 ToBoolean 的抽象操做就能够了,除去列出的假值,其余均为真值
那么布尔值的应用场景有哪些呢?
以前的写法是显示的转换 如:
let arr = [1, 2, 4, '', 0, undefined, null, [], {}]
arr.filter(Boolean) // [1, 2, 4, [], {}]
arr.filter(v => !!v) // [1, 2, 4, [], {}]
如今咱们能够写成隐式转换
arr.filter(v => v) // [1, 2, 4, [], {}]
复制代码
不少人都认为 || 和 && 是返回布尔值的,这是一种误解。其实这两个运算符历来都不是用来返回布尔值的,相反,这是一种运算,能够返回任意类型的值。 接下来详细介绍这两个运算符。
|| 的使用方式 如 a || b ,运算规则以下
先对 a 进行抽象类型转换 (ToBoolean抽象操做),若是 a 是真值,那么直接返回 a ,不然返回 b
let a = [], b = ''
a || b // [] 由于 a 是真值,因此直接返回 a,不对 b 进行计算。
b || a // [] 由于 b 是假值,因此直接返回 a
复制代码
&& 的使用方式 如 a && b,运算规则以下
先对 a 进行抽象类型转换(ToBoolean抽象操做),若是 a 是真值,那么直接返回 b,不然返回 a
let a = [], b = ''
a && b // '' 由于 a 是真值,因此直接返回 b
b && a // '' 由于 b 是假值,因此直接返回 b,不对 a 进行计算
复制代码
因此 这两种运算符的性质决定了其又叫短路运算符
这两个操做符,咱们平时在写代码时,用到的地方特别多,可是如何抉择?为何推荐使用全等 ===,而不推荐使用 == ? 咱们都知道 == 是通过类型转换以后再比较值是否相等。那么值转换究竟是什么样的顺序?
下面将经过深刻分析,为何 === 比 == 要好。首先咱们须要熟悉规则是什么?
== 的规则以下:
根据ES5规范 11.9.3.2-3规定。 null == undefined
let arr = []
arr == false // true 转换过程 [] -> ToPrimitive -> ''(这步还熟悉吧), 两边都 ToNumber '' -> 0, false -> 0
arr == 0 // true
1 == true // true
2 == true // false 由于 true -> ToNumber -> 1 至关于 2 == 1 为false
[1] == 1 // true
[1] == [1] // false
[] == {} // false
{} == [] // 会报 SyntaxError,为何?
复制代码
针对上述的 {} == [] ,这里 JS 引擎会将 == 前面的大括号解析为块级做用域。因此会报语法错误
至关于
{
// some code
}
== []
复制代码
因此改为 ({}) == [] 就能够查看结果了。
比较少见的状况:
建议:
总结:我的来看,抽象相等 == 用的好的话能够进行不少有趣的代码组合,前提是类型之间的互相切换咱们已经很熟悉了。可是每次比较均可能会形成两侧的数据进行屡次数据类型转换。性能和安全性,稳定性都不如 严格抽象全等 === 来的高。
最后,咱们再简单介绍一下,> 、< 、>= 、<= 这几种状况。
先介绍下规则:
// 两边出现非字符串
let arr = [12]
arr < true // false
arr < 13 // true
// 两边出现字符串
'042' < '12' // true
let anotherArr = [042]
let temp = [12]
anotherArr < temp // false 为何结果为false '042' < '12' 不是为 true 吗 ? 本身思考下 会得出答案的
let obj = {}
let obj1 = {}
obj < obj1 // false 由于 '[object Object]' === '[object Object]'
复制代码
最后咱们来讨论下 a <= b的状况。
举个栗子
let obj = {}
let obj1 = {}
obj < obj1 // false
obj == obj1 // false
obj > obj1 // false
// 上述3个的结果应该是没有任何问题
obj <= obj1 // true ???
obj >= obj1 // true ???
复制代码
意想不到的事情发生了是吧,这不是程序执行的问题,这个结果正符合规范的要求
咱们看刚才的例子
obj <= obj1 会被执行为 !(obj > obj1) -> !false -> true
obj >= obj1 会被执行为 !(obj < obj1) -> !false -> true
复制代码
这样,结果就很顺其天然了吧。
今天介绍的类型转换,不少知识点都是参考 KYLE SIMPSON 著有的 YOU DONT KNOW JAVASCRIPT一书。部分知识参照 ES5规范。 而后根据平常的开发,尝试作的总结。类型转换基本就介绍完了。介绍这一知识的目的不是让咱们在开发中写出这些生涩的代码,而是让咱们透过写的代码,理解其运行的本质,这样,能让咱们写出更好的代码。咱们在学习的过程当中,更加应该,知其然知其因此然。这样,咱们写出来的代码才会又更高的可读性和稳定性。
若有理解错误或者表达不清楚的地方,欢迎一块儿交流。