for of
更深刻的理解iterator
究竟是何方神聖?for of
來遍歷對象呢?for of
?Generator
又是何方神聖?Generator
有什麼用呢?for of
提及 for of
相信每一個寫過 JavaScript
的人都用過 for of
,平時咱們用它作什麼呢?大多數狀況應該就是遍歷數組了,固然,更多時候,咱們也會用 map()
或者 filer()
來遍歷一個數組。 可是就像咱們標題裏面說的,它跟 Generator
能扯上什麼關係呢?數組
首先咱們想一個問題,爲何使用 for of
或者 map()
/ filer()
方法就能夠遍歷一個數組 (或者類數組對象: Strings
, Maps
, Sets
, arguments
) 呢? 爲何不能用他們來遍歷一個對象呢?數據結構
在真正揭開謎底以前,站在 for of
的角度想一下,如今讓你去遍歷一個數組,你須要知道什麼信息呢?異步
帶着這樣的思考,咱們打印一個數組來看看這裏面的玄機:ide
const numbersArray = [1, 2, 3]; console.dir(numbersArray); 複製代碼
數組 (或者類數組對象: Strings
, Maps
, Sets
, arguments
) 的原型中都實現了一個方法 Symbol.iterator
,問題來了,那麼這個 Symbol.iterator
又有什麼用呢? 拿出來試一下就知道了:函數
let iterator = numbersArray[Symbol.iterator](); // 咱們把這個 Symbol.iterator 打印一下看看裏面到底有些什麼 console.dir(iterator); 複製代碼
這裏有一個 next()
方法對嗎?執行這個 next()
方法:post
iterator.next(); // 輸出 {value: 1, done: false} iterator.next(); // 輸出 {value: 2, done: false} iterator.next(); // 輸出 {value: 3, done: false} iterator.next(); // 輸出 {value: undefined, done: true} 複製代碼
請注意,當下標超出時,value: undefinedui
咱們發現這個 iterator.next()
每次都返回了一個對象。這對象包含兩個信息:當前下標的值,以及遍歷是否結束的標誌。這印證了咱們以前思考,有了這兩個信息,你做爲 for of
函數,也能打印出數組的每一項了不是嗎?this
新的問題來了,iterator
究竟是何方神聖呢?spa
iterator
(迭代器) & The iterator protocol
(迭代協議)聊到了 iterator
咱們不得不先說一下 The iterator protocol
(迭代協議).net
MDN 上是這麼說的:The iterator protocol
容許 JavaScript
對象去定義或定製它們的迭代行爲 ,因此上面出現的 Symbol.iterator
這個方法,就是數組對於這個協議的實現。那麼按照這個協議,數組是怎麼實現了一個 iterator
呢?
這一大段看起來比較費勁,簡單來講就像咱們上一章節所印證的,它實現的方式是定義了一個 next()
方法,而這個 next()
方法每次被執行都會返回一個對象: {value:xxx/undefined , done: true/false }
其中 value
表明的是當前遍歷到的值,done
表明是否遍歷結束。
本小節回答了咱們以前的提問: 爲何不能用 for of
來遍歷一個對象呢? 緣由很簡單:JavaScript
的對象中沒有實現一個這樣的 iterator
。你能夠打印一個對象來看看結果如何:
console.dir({ a: 1, b: 2 }); 複製代碼
okay, 到這裏若是就結束的話,那咱們瞭解得還不夠深刻,因而再問一個問題:
Why is there no built-in object iteration ? (爲何在 object
中沒有內置迭代器呢? )
object
中沒有內置迭代器呢?對啊,爲何呢? 咱們在各樣的場景中也須要來遍歷一個對象啊?爲何沒有內置一個迭代器呢?要回答這個問題,咱們得從另一個角度出發,瞭解一些基本的概念:
咱們經常說遍歷對象,可是簡單來講,只會在兩種層級上來對一個 JavaScript
對象進行遍歷:
程序的層級 - 什麼意思呢?在程序層級上,咱們對一個對象進行迭代,是在迭代展現其結構的對象屬性。 可能還不是很好理解,舉個栗子:Array.prototype.length
這個屬性與對象的結構相關,但卻不是它的數據。
數據的層級 - 意味着迭代數據結構並提取它的數據。舉個栗子:咱們在迭代一個數組的時候,迭代器是對於它的 每個數據進行迭代,若是 array = [a, b, c, d]
那麼迭代器訪問到的是 1, 2, 3, 4
。
明白了這個原因,JavaScript
雖然不支持用 for of
來遍歷對象,可是提供了一個 for in
方法來遍歷全部非 Symbol
類型而且是可枚舉的屬性。
標準不支持,若是咱們就是要用 for-of
來遍歷對象呢?那咱們能夠任性的實現一個啊:
Object.prototype[Symbol.iterator] = function*() { for (const [key, value] of Object.entries(this)) { yield { key, value }; } }; 複製代碼
for (const { key, value } of { a: 1, b: 2, c: 3 }) { console.log(key, value); } 複製代碼
不知道你有沒有注意一個細節,在咱們任性的實現一個 iterator
的代碼中,咱們用到了一個很奇怪的結構 function*() {}
,這個就是咱們接下來要介紹的 Generator
看到這個名字以爲很厲害哈,但其實很簡單,寫一個 Generator
你只須要在函數名和 function
關鍵字中間加一個 *
號就能夠了。至於裏面的 yield
是什麼,後面會說的。
talk is cheap , show me the code
,用一個例子,簡單說一下概念。
咱們如今定義了一個這樣的 Generator
叫作 gen
function* gen() { yield 1; yield 2; yield 3; yield 4; } 複製代碼
咱們只能看到,這裏面有 4 個語句,那打印一下看看唄:
這裏發現了一個熟悉的函數,next()
方法,咱們把 gen
實例化一下,執行一下它的 next()
來看看結果:
仍是熟悉的味道,那麼到這裏,咱們已經知道,Generator
能夠實例化出一個 iterator
,而且這個 yield
語句就是用來中斷代碼的執行的,也就是說,配合 next()
方法,每次只會執行一個 yield
語句。
多說一句,針對 Generator
自己,還有一個有意思的特性,yield
後面能夠跟上另外一個 Generator
而且他們會按照次序執行:
function* gen() { yield 1; yield* gen2(); return; } function* gen2() { yield 4; yield 5; } let iterator = gen(); console.log(iterator.next()); console.log(iterator.next()); console.log(iterator.next()); console.log(iterator.next()); 複製代碼
結果頗有意思不是嗎?並且 return
會終結整個 Generator
,換句話說:寫在 return
後面的 yield
不會執行。
Generator
有什麼用? 聰明的同窗可能已經猜到了,是的,它可以中斷執行代碼的特性,能夠幫助咱們來控制異步代碼的執行順序:
例若有兩個異步的函數 A
和 B
, 而且 B
的參數是 A
的返回值,也就是說,若是 A
沒有執行結束,咱們不能執行 B
。
那這時候咱們寫一段僞代碼:
function* effect() { const { param } = yield A(); const { result } = yield B(param); console.table(result); } 複製代碼
這時候咱們若是須要獲得 result
那麼咱們就須要:
const iterator = effect() iterator.next() iterator.next() 複製代碼
執行兩次 next()
獲得結果,看起來很傻不是嗎?有沒有好的辦法呢?(廢話,確定有啊) 假設你在每次執行 A()
/ B()
的請求結束以後,都會自動執行 next()
方法呢?這不就解決了嗎?
這樣的庫早就存在了,建議你們參考 co
的源碼,固然你也能夠經過閱讀 這篇文章 來看看,到底 Generator
是怎麼玩的。
ps:
https://blog.csdn.net/zshdd/article/details/82897452
function readFile(fileName) {
return new Promise(function (resolve,reject) {
fs.readFile(fileName,function (err,data) {
if(err) reject(err);
resolve(data.toString());
})
})
}
function* gentT2() {
var f1 = yield readFile('ip.txt');
var f2 = yield readFile('ip1.txt');
console.log(f1,f2);
}
function run(gen) {
var g=gen();
function next(data) {
var res=g.next(data);
if(res.done) return res.value;
res.value.then(function (data) {
next(data);
});
}
next();
}
run(gentT2);
// 方便的自動執行generator函數 相似的庫(co)
https://www.jianshu.com/p/36c74e4ca9eb