用 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