用 for
循環語句來迭代數據時,須要初始化一個變量來記錄每一次的迭代位置,但嵌套循環時就會變得繁瑣。因而,es6引入了迭代器和 for...of
的概念來簡化數據迭代操做。git
迭代器是一種特殊的對象,它具備 next()
方法,每次調用返回一個結果對象。該對象包含兩個屬性,value表示下一次返回的值,done表示迭代是否結束,爲 bool
值。咱們模擬實現產生迭代器的函數:es6
function createIterator(items) {
let i = 0;
return {
next() {
let done = i >= items.length;
let value = !done ? items[i++] : undefined;
return { done, value };
}
};
}
let iterator = createIterator([1, 2, 3]);
console.log(iterator.next()); // { done: false, value: 1 }
console.log(iterator.next()); // { done: false, value: 2 }
console.log(iterator.next()); // { done: false, value: 3 }
console.log(iterator.next()); // { done: true, value: undefined }
複製代碼
可迭代對象是指具備 Symbol.iterator
屬性的對象,該屬性值是一個返回迭代器的函數。在es6中,全部集合對象(數組,Set
集合和 Map
集合)和字符串都是可迭代對象,都有默認的迭代器,即有 Symbol.iterator
屬性值。github
咱們能夠爲對象增長 Symbol.iterator
屬性來自定義建立一個可迭代對象:數組
// 省略createIterator代碼
let obj = {
items: [],
push(x) {
this.items.push(x);
return this.items;
},
[Symbol.iterator]() {
return createIterator(this.items);
}
};
複製代碼
對於可迭代對象的遍歷es6給咱們提供了 for...of
語句,來看一個例子:函數
let arr = [1, 2, 3];
for (let x of arr) {
console.log(x);
}
// 1
// 2
// 3
複製代碼
其實 for...of
每一次循環會調用可迭代對象的 Symbol.iterator
方法生成的迭代器的 next()
方法,並把結果對象的value值賦給變量,直到done的值爲true
。咱們用 for...of
訪問自定義的迭代對象:post
// ...
obj.push(1).push(2);
for (let x of obj) {
console.log(x);
}
// 1
// 2
複製代碼
爲了更好的訪問數組,Set
集合和 Map
集合,es6爲這3種對象提供了內置迭代器:ui
entries()
: 返回一個迭代器,值爲包含鍵和值兩個元素的數組keys()
: 返回一個迭代器,值爲集合的鍵values()
: 返回一個迭代器,值爲集合的值咱們來看數組的例子:this
let arr = [1, 2, 3];
for (let key of arr.keys()) {
console.log(key);
}
// 0
// 1
// 2
for (let value of arr.values()) {
console.log(value);
}
// 1
// 2
// 3
for (let entry of arr.entries()) {
console.log(entry);
}
// [ 0, 1 ]
// [ 1, 2 ]
// [ 2, 3 ]
複製代碼
Map
集合的結果和上面相似,只是Set
集合的鍵和值都是同樣的,因此keys()
和 values()
是等價的。spa
不一樣的集合有本身默認的迭代器。數組和 Set
使用 values()
方法返回的迭代器,而Map
集合是用 entries()
方法:code
let map = new Map([['name', 'wozien'], ['age', 23]]);
for (let entry of map) {
console.log(entry);
}
// [ 'name', 'wozien' ]
// [ 'age', 23 ]
複製代碼
可用數組解構的方式來處理迭代的返回值:
let map = new Map([['name', 'wozien'], ['age', 23]]);
for (let [key, value] of map) {
console.log(key + ' ' + value);
}
// name wozien
// age 23
複製代碼
在以前咱們能夠用展開運算符 (...)
把 Set
集合轉爲一個數組,其實展開運算符可做用於任何可迭代對象,它會把迭代器 next()
方法的返回值按順序插入到數組中:
let map = new Map([['name', 'wozien'], ['age', 23]]);
let arr = [...map];
console.log(arr); // [ [ 'name', 'wozien' ], [ 'age', 23 ] ]
複製代碼
做用於自定義的迭代對象:
// 省略createIterator代碼
let obj = {
[Symbol.iterator]() {
return createIterator([1, 2, 3]);
}
};
let arr = [...obj];
console.log(arr); // [ 1, 2, 3 ]
複製代碼
上面的除了手動實現 createIterator()
函數外,es6爲咱們提供了生成器(Generator)
,方便咱們生成迭代器。經過在 function
關鍵字後的星號(*)
來表示,在函數體內用 yield
關鍵字控制迭代器 next()
的返回值:
function* createIterator() {
yield 1;
yield 2;
yield 3;
}
let iterator = createIterator();
console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { value: undefined, done: true }
複製代碼
經過生成器生成的迭代器每次調用 next()
執行函數代碼時 ,每次執行 yield
語句完後就會自動中止執行。直到再次調用 next()
方法纔會繼續執行。
function* createIterator() {
console.log(1);
yield;
console.log(2);
yield;
console.log(3);
}
let iterator = createIterator();
console.log(iterator.next()); // 1 { value: undefined, done: false }
console.log(iterator.next()); // 2 { value: undefined, done: false }
複製代碼
有了生成器,咱們能夠改寫自定義的可迭代對象:
let obj = {
items: [],
push(x) {
this.items.push(x);
return this.items;
},
[Symbol.iterator]: function*() {
for (let item of this.items) {
yield item;
}
}
};
obj.push(1).push(2);
for (let x of obj) {
console.log(x);
}
// 1 2
複製代碼
或者能夠用es6定義對象函數的方式:
let obj = {
// ...
*[Symbol.iterator]() {
for (let item of this.items) {
yield item;
}
}
};
複製代碼
更多關於生成器的說明,參考下一篇es6-生成器Generator