這些是本人在 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規範。 而後根據平常的開發,嘗試作的總結。類型轉換基本就介紹完了。介紹這一知識的目的不是讓咱們在開發中寫出這些生澀的代碼,而是讓咱們透過寫的代碼,理解其運行的本質,這樣,能讓咱們寫出更好的代碼。咱們在學習的過程當中,更加應該,知其然知其因此然。這樣,咱們寫出來的代碼纔會又更高的可讀性和穩定性。
若有理解錯誤或者表達不清楚的地方,歡迎一塊兒交流。