类型转换在各个语言中都存在,而在 JavaScript 中因为缺少对其的了解而不慎在使用中常常形成bug被人诟病。为了不某些场景下的意外,甚至推崇直接使用 Strict Equality( === )来代替 ==。这确实能避免不少bug,但更是一种对语言不理解的逃避(我的观点)。数组
先抛出在 You Don’t Know JavaScript (中) 看到的一个例子安全
[] == [] // false [] == ![] // true {} == !{} // false {} == {} // false
是否是很奇怪?本文将从书中看到的知识与规范相结合,来详细说明一下JavaScript在类型转换时候发生的故事。函数
不少人喜欢说显示类型转换与隐式类型转换,但我的感受只是说法上的不一样,实质都在发生了类型转换而已,故不想去区分他们了(感受一万我的有一万种说法)spa
仅在6大基本类型 null undefined number boolean string object 做讨论 symbol未考虑code
var a = String(1) var b = Number('1') var c = 1 + '' var d = +'1'
a,b直接调用了原生函数,发生了类型转换。c,d使用了+运算符的一些规则,发生了类型转换。这些是很简单的也是咱们经常使用的。对象
其实真正起做用的,是语言内部对规范中抽象操做的实现,接下来咱们所说的 ToString, ToNumber, ToBoolean
等都是抽象操做,而不是JS里对应的内置函数ip
按照如下规则转化被传递的参数字符串
Argument Type | Result |
---|---|
Undefined | “undefined” |
Null | “null” |
Boolean | true -> “true” false – > “false” |
Number | NaN -> “NaN” +0 -0 -> “0” -1 -> “-1” infinity -> “Infinity” 较大的数科学计数法 (详见规范9.8.1) |
String | 不转换 直接返回 |
Object | 1. 调用ToPrimitive抽象操做, hint 为 String 将返回值做为 value 2. 返回ToString(value) |
String(undefined) // "undefined" String(null) // "null" String(true) // "true"
ToPrimitive 抽象操做下面会说起string
按照如下规则转换被传递参数it
Argument Type | Result |
---|---|
Undefined | NaN |
Null | +0 |
Boolean | true -> 1 false -> +0 |
Number | 直接返回 |
String | 若是不是一个字符串型数字,则返回NaN(具体规则见规范9.3.1) |
Object | 1. 调用ToPrimitive抽象操做, hint 为 Number 将返回值做为 value 2. 返回ToNumber(value) |
按照如下规则转换被传递参数
Argument Type | Result |
---|---|
Undefined | false |
Null | false |
Boolean | 直接返回 |
Number | +0 -0 NaN -> false 其余为true |
String | 空字符串(length为0) -> false 其余为true |
Object | true |
顾名思义,该抽象操做定义了该如何将值转为基础类型(非对象),接受2个参数,第一个必填的要转换的值,第二个为可选的hint,暗示被转换的类型。
按照如下规则转换被传递参数
Argument Type | Result |
---|---|
Undefined | 直接返回 |
Null | 直接返回 |
Boolean | 直接返回 |
Number | 直接返回 |
String | 直接返回 |
Object | 返回一个对象的默认值。一个对象的默认值是经过调用该对象的内部方法[[DefaultValue]]来获取的,同时传递可选参数hint。 |
var a = {} a.toString = function () {return 1} a.valueOf = function () {return 2} String(a) // "1" Number(a) // 2 a + '' // "2" ??????? +a // 2 a.toString = null String(a) // "2" a.valueOf = null String(a) // Uncaught TypeError: balabala
彷佛咱们发现了一个很不合规范的返回值,为何 a + ''
不该该返回”1″吗
基础概念已经了解了,那么在 == 中到底发生了什么样的类型转换,而致使了常常产生出乎意料的bug,致使了它臭名昭著。
x == y 判断规则以下:
[] == [] // false // 1. 两遍类型都为 Object,比较引用地址,不一样返回false 搞定 [] == ![] // true // 1. ![]强制类型转换 变为 [] == false // 2. 根据规范第7条,返回 [] == ToNumber(false), 即 [] == 0 // 3. 根据规范第9条,返回ToPromitive([]) == 0,数组的valueOf为自己,不是原始值,则返回toString()即 "" == 0 // 4. 根据规范第5条,返回ToNumber("") == 0, 即 0 == 0 // 5. 根据规范第1条,返回 true // 下面的不赘述了,分析相似上面 {} == !{} // false {} == {} // false
咱们不难看出如下几点
引用 << 你不知道的JS(中) >> 中的2句话
- 若是两遍的值中有 true 或者 false , 千万不要使用 == (会被转为数字0,1来进行判断,会出现一些意外的状况)
- 若是两遍的值中有[]、””或者0,尽可能不要使用 ==
先来看看这个例子
var a = { b: 42 } var b = { b: 43 } a < b // false a == b // false a > b // false a <= b // true a >= b // true
是否是感受到世界又崩塌了???
让咱们来仔细分析一下
var a = { b: 42 } var b = { b: 43 } a < b // false // 1. 两遍调用ToPrimitive, 返回[object Object] 两遍一致 返回 false a == b // false // 两遍不一样的引用,返回false a > b // false // 同 a < b a <= b // true // 按规范实际上是处理成 !(a > b) 因此为true a >= b // true
因此在不相等比较的时候,咱们最后仍是进行手动的类型转换较为安全
深刻了解类型转换的规则,咱们就能够很容易取其精华去其糟粕,写出更安全也更简洁可读的代码