小時候,鄉愁是一枚小小的郵票,javascript
我在這頭,java
母親在那頭。git
長大後,鄉愁是一張窄窄的船票,es6
我在這頭,github
新娘在那頭。數組
後來啊,安全
鄉愁是一方矮矮的墳墓,微信
我在外頭,app
母親在裏頭。ide
而如今,
鄉愁是一灣淺淺的海峽,
我在這頭,
大陸在那頭。
——余光中《鄉愁》
本文爲讀 lodash 源碼的第三篇,後續文章會更新到這個倉庫中,歡迎 star:pocket-lodash
gitbook也會同步倉庫的更新,gitbook地址:pocket-lodash
compact
函數用來去除數組中的假值,並返回由不爲假值元素組成的新數組。
false
、null
、0
、 ""
、undefined
和 NaN
都爲假值。
例如:
var arr = [1,false,2,null,3,0,4,NaN,5,undefined] _.compact(arr) // 返回 [1,2,3,4,5]
function compact(array) { let resIndex = 0 const result = [] if (array == null) { return result } for (const value of array) { if (value) { result[resIndex++] = value } } return result }
compact
的源碼只有寥寥幾行,至關簡單。
首先判斷傳入的數組是否爲 null
或者 undefined
,若是是,則返回空數組。
而後用 for...of
來取得數組中每項的值,若是不爲假值,則存入新數組 result
中,最後將新數組返回。
到這裏,源碼分析完了。
可是在看源碼的時候,發現這裏用了 for...of
來作遍歷,其實除了 for...of
外,也能夠用 for
或者 for...in
來作遍歷,那爲何最後選了 for...of
呢?
使用 for
循環,很容易就將 compact
中關於循環部分的源碼改寫成如下形式:
for (let i = 0; i < array.length; i++) { const value = array[i] if (value) { result[resIndex++] = value } }
這樣寫,確定是沒有問題的,可是不夠簡潔。
再來看 for...in
循環,先來將源碼改寫一下:
for (let index in array) { const value = array[i] if (value) { result[resIndex++] = value } }
先看看MDN上關於 for...in
的用法:
for...in語句以任意順序遍歷一個對象的 可枚舉屬性。
關於可枚舉屬性,能夠點擊上面的連接到MDN上了解一下,這裏不作太多的解釋。
在數組中,數組的索引是可枚舉屬性,能夠用 for...in
來遍歷數組的索引,數組中的稀疏部分不存在索引,能夠避免用 for
循環形成無效遍歷的弊端。
可是,for...in
有兩個致命的特性:
for...in
的遍歷不能保證順序for...in
會遍歷全部可枚舉屬性,包括繼承的屬性。for...in
的遍歷順序依賴於執行環境,不一樣執行環境的實現方式可能會不同。單憑這一點,就斷然不能在數組遍歷中使用 for...in
,大多數狀況下,順序對於數組的遍歷都至關重要。
關於第二點,先看個例子:
var arr = [1,2,3] arr.foo = 'foo' for (let index in arr) { console.log(index) }
在這個例子中,你指望輸出的是 0,1,2
,可是最後輸出的多是 0,1,2,foo
(for...in
不能保證順序)。由於 foo
也是可枚舉屬性,在 for..in
會被遍歷出來。
最後來看看 for...of
。
當咱們在控制檯中打印一個數組,並將它展開來查看時,會在數組的原型鏈上發現一個很特別的屬性 Symbol.iterator
。
其實 for...of
循環內部調用的就是數組原型鏈上的 Symbol.iterator
方法。
Symbol.iterator
在調用的時候會返回一個遍歷器對象,這個遍歷器對象中包含 next
方法,for...of
在每次循環的時候都會調用 next
方法來獲取值,直到 next
返回的對象中的 done
屬性值爲 true
時中止。
其實咱們也能夠手動調用來模擬遍歷的過程:
const arr = [1,2,3] const iterator = a[Symbol.iterator]() iterator.next() // {value: 1, done: false} iterator.next() // {value: 2, done: false} iterator.next() // {value: 3, done: false} iterator.next() // {value: undefined, done: true}
知道這些原理後,徹底能夠改寫數組中的 Symbol.iterator
方法,例如遍歷時將數組中的值都乘2:
Array.prototype[Symbol.iterator] = function () { let index = 0 const _self = this return { next: function () { if (index < _self.length) { return {value: _self[index++] * 2, done: false} } else { return {done: true} } } } }
使用 Generator
函數能夠寫成如下的形式:
Array.prototype[Symbol.iterator] = function* () { let index = 0 while (index < this.length) { yield this[index++] * 2 } }
所以在不改寫 Symbol.iterator
的狀況下,使用 for...of
來遍歷數組是安全的,由於這個方法是數組的原生方法。
關於 Iterator
和 Generator
能夠點擊參考中的連接詳細查看。
署名-非商業性使用-禁止演繹 4.0 國際 (CC BY-NC-ND 4.0)
最後,全部文章都會同步發送到微信公衆號上,歡迎關注,歡迎提意見:
做者:對角另外一面