本系列文章經過重新編排和擴充,已出書為ECMAScript關鍵30天。原始文章因當時準備時程緊迫,多少有些許錯誤。為了避免造成讀者的困擾,以及配合書籍的內容規劃,將會陸續更新本系列文章。
本篇文章在 2021/11/8 已更新。
有關迭代的標準,主要依循以下幾個原則-
可迭代的資料型態,物件本身或它的原型鏈中,必須實作迭代的執行方法,並且必須使用Symbol
內建的常數-Symbol.iterator
作為方法的鍵。
有些標準內建物件已經預設為Iterable,常見的有-
for of
在前面介紹的陣列、Set 跟 Map都有提到在 ES2015 中新增了這個迭代器。
為了要提供 Iterable 有一致的迭代方法,因此產生了 for ... of
的寫法。統整一下for of
在遍歷常用的 Iterable 下的情境吧。
// String
const str = "12";
for (let v of str) {
console.log(v);
}
// 1
// 2
// Array
const arr = ["a", "b"];
for (let v of arr) {
console.log(v);
}
// a
// b
// Map
const map = new Map();
map.set(1, "a");
map.set(2, "b");
for (let v of map) {
console.log(v);
}
// [1, "a"]
// [2, "b"]
// Set
const set = new Set([1, 1, "a", "b"]);
for (let v of set) {
console.log(v);
}
// 1
// a
// b
// arguments
function fn(params) {
for (let v of arguments) {
console.log(v);
}
}
fn("a", "b");
// a
// b
// NodeList
const nodeList = document.querySelectorAll("meta");
for (let v of nodeList) {
console.log(v);
}
// <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
// <meta name="viewport" content="width=device-width">
當物件開始迭代後,就會呼叫物件中的@@iterator
方法,並且透過執行後回傳的結果,取得每次的目標值和迭代狀態。一個標準的迭代器,至少要實作叫做next
的方法,必且須符合以下條件-
done
屬性,以布林值表示物件是否迭代完畢。value
屬性,每次迭代後取得的目標值。舉常用的標準內建物件-陣列,來使用它的迭代器來看看。
const names = ['Yuri', 'Ann', 'Joe'];
const iterator = names[Symbol.iterator]();
console.log(iterator.next()); // {value: 'Yuri', done: false}
console.log(iterator.next()); // {value: 'Ann', done: false}
console.log(iterator.next()); // {value: 'Joe', done: false}
console.log(iterator.next()); // { value: undefined, done: true}
generator 是在 ES2015 中提出全新的函式類型。
在一般的函式,每次的調用都會從頭開始執行,並回傳一種結果,或是不回傳。而 generator 的機制就像上面提到的迭代器一樣,每次的函式調用,並不是單純執行函式,而是回傳 generator 物件,並從下一個指向的地方開始執行,並且回傳不同的執行結果。
function* fnName() {...}
要宣告 generator 的函式,必須以function
開頭,並加上 *
。
function* generatorFn() {
//...
}
yield
& next()
在 generator 裡,以 yield
的作陳述句的前綴字,在 generator 物件中,如果要取得下個陳述句的回傳結果,則執行物件的內建方法 next()
。
而回傳結果會被封裝成以下形式的物件:
{
done: boolean, // 回傳是否以執行完return
value: any // 陳述句的執行結果
}
// 宣告 generator function
function* generatorFn() {
yield 1;
yield 2;
return "done";
}
// 建立 generator 的物件
const gen1 = generatorFn();
const r1 = gen1.next(); // Object { done: false, value: 1 }
const r2 = gen1.next(); // Object { done: false, value: 2 }
const r3 = gen1.next(); // Object { done: true, value: "done" }
for ... of
在上面提到 generator 也是可迭代的物件之一。所以我們也可以使用 for ... of 來遍歷執行結果。
for (const v of generatorFn()) {
console.log(v);
}
// 1
// 2
yield*
進行委派,在這之後的產生器或可迭代物件執行迭代的行為next
傳入值,取代上一個 yield 陳述式的執行結果redux-saga
:比起 以 promise 處理非同步的 redux-thunk
的架構更加獨立,方便測試。也更貼近非同步存取資料的特性。