迭代器是一个特殊的对象,具有专门为迭代过程设计的专有接口,所有迭代器对象都有一个 next() 方法,每次调用返回一个结果对象。迭代器保存一个内部指针,用于指向当前集合中值的位置,调用 next() ,返回下一个可用的值。
结果对象包含两个属性:value 和 done,value 表示下一个返回值,done 是一个布尔值,当没有更多可返回数据时返回 true。 结果对象两种形式:
value 下一个返回值(不为undefined),done 为 false value 为 undefined ,done 为 true
ES6 中迭代器编写规则复杂,但 ES6 同时引入生成器对象,它使创建迭代器对象的过程变得更简单。下面先介绍生成器
生成器
生成器是一种返回迭代器的函数,生成器调用方式与普通函数相同,只不过返回的是一个迭代器。
function *createIterator(){
yield 1;
yield 2;
}
let iterator = createIterator();
console.log(iterator.next()); // Object { value: 1, done: false }
console.log(iterator.next()); // Object { value: 2, done: false }
console.log(iterator.next()); // Object { value: undefined, done: true }
上述代码中:
createIterator() 前的星号表明它是一个生成器,我们知道函数还有一种 new 方式调用,通过 new 调用生成器的话会抛出错误,生成器是没有 [Constructor] 属性。 yield 关键字是 ES6 新特性,下面来介绍它。
yield 使用
yield 关键字,通过它来指定调用迭代器 next() 返回值及返回顺序。每执行完一条 yield 语句后函数就会自动停止执行,直到再次调用迭代器的 next() 或 throw() 方法才会继续执行。
function *createIterator (array) {
for(let i = 0; i < array.length; i++){
yield array[i];
}
}
let iterator = createIterator([1,2]);
console.log(iterator.next().value); // 1
console.log(iterator.next().value); // 2
yield 使用限制:只能在生成器内部使用,在其他地方使用或生成器内部的函数使用都会抛出语法错误。
yield 1; // 抛出语法错误
function *fun (array) {
// 可以使用
array.forEach(function () {
// 不可以使用
yield 1; //抛出语法错误
})
}
生成器表达式
注意不能用箭头函数来创建生成器。
// 函数表达式
let createIterator = function *(array) {
for(let i = 0; i < array.length; i++){
yield array[i];
}
}
// 对象的生成器
let object = {
array : [1,2],
// 第一种方式:
createIterator: function *() {
for(let i = 0, len = this.array.length; i < len; i++){
yield this.array[i];
}
}
// 第二种方式:简写方式
*createIterator () {
for(let i = 0, len = this.array.length; i < len; i++){
yield this.array[i];
}
}
}
迭代器
可迭代对象
可迭代对象具有 Symbol.iterator 属性,是一种与迭代器密切相关的对象。 Symbol.iterator 通过指定函数可以返回一个作用于附属对象的迭代器。在 ES6 的所有集合对象和字符串都是可迭代对象,它们都有默认的迭代器,就是通过 Symbol.iterator() 来获取的。
for-of 循环中,首先使用对象默认迭代器[Symbol.iterator],然后每执行一次都会调用迭代器的next(),从其返回对象的value属性读取值并存储在变量中。
let array = [1,2,3];
// for-of
for (let num of array) {
console.log( num );
}
//依次:1, 2, 3
// 模拟
let iterator = array[Symbol.iterator]();
let num = iterator.next().value;
while ( num !== undefined ) {
console.log(num);
num = iterator.next().value;
}
//依次:1, 2, 3
通过上述代码,明白在 JS 引擎中执行 for-of 循环语句中,通过 Symbol.iterator 获取数组 array 的默认迭代器,并用它遍历数组元素。 下面利用 Symbol.iterator 创建一个可迭代对象。
// 创建一个可迭代对象
let object = {
array: [1,2,3],
*[Symbol.iterator] () {
for( let number of this.array){
yield number;
}
}
}
for ( let number of object ) {
console.log( number );
}
默认情况下,对象是不可迭代的,但如果给 Symbol.iterator 属性添加一个生成器,则该对象将变为可迭代对象。
内建迭代器
ES6 中有 3 中类型的集合对象:数组、Set集合和Map集合。这3中对象都内建了以下三种迭代器:
- entries() 返回一个迭代器,其值为多个键值对
- values() 返回一个迭代器,其值为集合的值
- keys() 返回一个迭代器,其值为集合中所有键名
let array = [1,2,3];
// entries()
for( var item of array.entries() ) {
console.log(item);
}
// 依次打印:Array [ 0, 1 ], Array [ 1, 2 ], Array [ 2, 3 ]
// values()
for( var item of array.values() ) {
console.log(item);
}
// 依次打印:1, 2, 3
// keys()
for( var item of array.keys() ) {
console.log(item);
}
// 依次打印:0, 1, 2
每个集合类型都有一个默认的迭代器,在 for-of 循环中,数组和Set集合调用 values() 迭代器,Map集合调用 entries() 迭代器。
// 数组
let array = [1,2,3];
for ( let item of array ) {
console.log(item); // 依次打印:1,2,3
}
// Set集合
let set = new Set([1,2,3]);
for ( let item of set ) {
console.log(item); // 依次打印:1,2,3
}
// Map集合
let map = new Map([["key1", "value1"], ["key2", "value2"]]);
for ( let item of map ) {
console.log(item);
//依次打印:Array [ "key1", "value1" ],Array [ "key2", "value2" ]
}
展开运算符与迭代器
ES6 新增加的展开运算符(...),它可以操作所有可迭代对象,根据默认迭代器来选取要引用的值,从迭代器读取所有值,然后按顺序返回。
let object = {
array: [1,2],
*[Symbol.iterator] () {
for ( let item of this.array ) {
yield item;
}
}
}
let array = [...object];
console.log(array); // Array [ 1, 2 ]