一個對象爲了變成可遍歷對象,好比說能夠用 for ... in
結構遍歷其屬性值,必須實現 @@iterator
方法, 意思是這個對象(或者它原型鏈 prototype chain
上的某個對象)必須有一個名字是 Symbol.iterator
的屬性。閉包
屬性 | 值 |
---|---|
[Symbol.iterator] |
返回一個對象的無參函數,被返回對象符合可遍歷協議。 |
當一個對象被認爲是一個迭代器時,它實現了一個 next()
的方法。
該方法返回一個對象包含 done
和 value
屬性,done
的值表示迭代器是否能夠產生序列中的下一個值,value
爲迭代器返回的任何 JavaScript
值。done
爲 true
時可省略。異步
let a = { q: 'q', w: 'w', e: 'e', }; Object.defineProperty(a, length, { enumerable: false, value: 3 });
如今對 a 嘗試用 for ... in
結構遍歷其屬性值async
for (let v of a) { console.log(v); }
報錯:函數
Uncaught TypeError: a[Symbol.iterator] is not a function
定義一個函數利用閉包實現一個將 a
轉變爲可迭代對象工具
function Iterator(obj) { let i; return ()=> { return { next: ()=> { if (i < obj.length) { for (i in obj) { return { done: false, value: obj[i] }; } } return { done: true }; } }; }; } a[Symbol.iterator] = Iterator(a);
Generator
函數最大特色就是能夠交出函數的執行權(即暫停執行)。異步操做須要暫停的地方,都用 yield 語句註明。調用 Generator
函數並不會執行本體,而是每次調用 next
方法的時候,執行到下一個碰到的 yield
處。ui
function* anotherGenerator(i) { yield i + 1; let x = yield i + 2; yield x + 3; } function* generator(i){ yield i; yield* anotherGenerator(i); // 執行權轉交給另外一個 generator 函數的話 yield 後面帶星號,直接調用的話是沒有效果的 yield i + 10; } var gen = generator(10); console.log(gen.next().value); // 10 console.log(gen.next().value); // 11 console.log(gen.next().value); // 12 console.log(gen.next(2).value); // 5 console.log(gen.next().value); // 20 // next 的返回值和迭代器的 next 相似,yield 語句的執行結果做爲 value,是否還有下一個 yield 決定 done // next 方法能夠帶有參數,這個參數能夠傳入 Generator 函數,做爲上個階段異步任務的返回結果
Generator
函數內部還能夠部署錯誤處理代碼,捕獲函數體外拋出的錯誤。prototype
function* gen(x){ try { var y = yield x + 2; } catch (e){ console.log(e); } return y; } var g = gen(1); g.next(); g.throw('出錯了'); // 出錯了
上面代碼的最後一行,Generator
函數體外,使用指針對象的 throw
方法拋出的錯誤,能夠被函數體內的 try ... catch
。指針
Generator函數返回的遍歷器對象,還有一個return方法,能夠返回給定的值,而且終結遍歷Generator函數。code
function* gen() { yield 1; yield 2; yield 3; } var g = gen(); g.next() // { value: 1, done: false } g.return('foo') // { value: "foo", done: true } g.next() // { value: undefined, done: true }
若是Generator函數內部有try...finally代碼塊,那麼return方法會推遲到finally代碼塊執行完再執行。對象
function* numbers () { yield 1; try { yield 2; yield 3; } finally { yield 4; yield 5; } yield 6; } var g = numbers() g.next() // { done: false, value: 1 } g.next() // { done: false, value: 2 } g.return(7) // { done: false, value: 4 } g.next() // { done: false, value: 5 } g.next() // { done: true, value: 7 }
上面代碼中,調用return方法後,就開始執行finally代碼塊,而後等到finally代碼塊執行完,再執行return方法。
co 庫是tj寫的一個讓Generator函數自動執行的工具。
let co = require('co'); let p = co(gen); p.then(function (){ console.log('ok'); })
co 函數返回一個 Promise 對象,所以能夠用 then 方法添加回調函數。
ES7 爲 generator 的語法糖