跳至主要内容

[JS] JavaScript 疊代器(Iterator)

/**
* Iterator 可以透過 Symbol.iterator 來客制化疊代的方式
* 裡面會包含 next function,這個 function 會回傳 value 和 done
* 像這樣:
**/

next () {
return index < arr.length {
value: <value>,
done: <true|false>
}
}

觀念

  • 在 ES6 中,有三類數據結構原生具備 Iterator 接口:ArrayArray-like ObjectSetMap 結構
  • 除此之外,其他數據結構(主要是物件)的 Iterator 接口,都需要自己在 Symbol.iterator 屬性上面部署,這樣才會被 for...of 迴圈遍歷。
  • iterator 會返回 value(當前成員的值) 和 done(Boolean,表示是否遍歷結束)

基本使用

Symbol 時有提到 JS 中有內建的 Symbol,大部分是提供內建的函式方法(Well-known Symbols),其中 Symbol.iterator 就是裡面內的 Symbol,對於可疊代的型別,在 Symbol.iterator 中就有提供函式可以讓它在 for...of 時使用。

/**
* Demonstrate an iterator
**/

let arr = [1, 2, 3];
console.log(typeof arr[Symbol.iterator]); // function

let current = arr[Symbol.iterator]();
current.next(); // Object {value: 1, done: false}
current.next(); // Object {value: 2, done: false}
current.next(); // Object {value: 3, done: false}
current.next(); // Object {value: undefined, done: true}

模擬 iterator 的 function

/**
* Create a function to simulate iterator
**/
var it = makeIterator(['a', 'b']);

it.next(); // { value: "a", done: false }
it.next(); // { value: "b", done: false }
it.next(); // { value: undefined, done: true }

function makeIterator(array) {
let nextIndex = 0;
return {
next: function () {
return nextIndex < array.length
? { value: array[nextIndex++], done: false }
: { value: undefined, done: true };
},
};
}

搭配 for...of,客制化自己的 iterator

for...of 在當 Iterator 的 donetrue 時就不再疊代。

Array 部分

/**
* Create a custom iterable array
**/
arr[Symbol.iterator] = function () {
let nextIndex = 0;
return {
next() {
return nextIndex < arr.length
? {
value: arr[nextIndex++] * 2,
done: false,
}
: {
value: undefined,
done: true,
};
},
};
};
for (item of arr) {
// 當 done 為 true 時就不在疊代
console.log(item); // 2, 4, 6
}

物件部分

/**
* Create a custom iterable Object
**/

let person = {
firstName: 'Aaron',
lastName: 'Chen',
hobbies: ['computer', 'programming', 'sports'],
// 讓 JS 知道這個物件具有 iterator,所以是 iterable
[Symbol.iterator]: function () {
let index = 0;
let hobbies = this.hobbies;
return {
next() {
return index < hobbies.length
? {
done: false,
value: hobbies[index++],
}
: {
done: true,
value: undefined,
};
},
};
},
};

for (let hobby of person) {
console.log(hobby); // computer, programming, spots
}

Demo Code

ES6 Iterator @ PJCHENder JSFiddle

參考資料