什么是迭代器(Iterator)?
满足迭代器协议的对象。
迭代器协议:
对象的next
方法是一个无参函数,它返回一个对象,该对象拥有done
和value
两个属性:
done
(boolean
):- 如果迭代器已经经过了被迭代序列时为
true
。这时value
可能描述了该迭代器的返回值。 - 如果迭代器可以产生序列中的下一个值,则为
false
。这等效于连同done
属性也不指定。
- 如果迭代器已经经过了被迭代序列时为
value
: 迭代器返回的任何 JavaScript值。done
为true
时可省略。
ES5实现一个简单的迭代器:
function createIterator(items) {
var i = 0;
return {
next: function() {
var done = (i >= items.length);
var value = !done ? items[i++] : undefined;
return {
done: done,
value: value
};
}
};
}
var iterator = createIterator([1, 2, 3]);
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 }"
// 之后的所有调用
console.log(iterator.next()); // "{ value: undefined, done: true }"
什么是可迭代对象(Iterable)?
满足可迭代协议的对象是可迭代对象。
可迭代协议:
对象的[Symbol.iterator]
值是一个无参函数,该函数返回一个迭代器。
在ES6中,所有的集合对象(Array
、 Set
与 Map
)以及String
、arguments
都是可迭代对象,它们都有默认的迭代器。
可迭代对象可以在以下语句中使用:
for (let value of ['a', 'b', 'c']) {
console.log(value);
}
// "a"
// "b"
// "c"
[...'abc']; // ["a", "b", "c"]
console.log(...['a', 'b', 'c']); // ["a", "b", "c"]
function* gen() {
yield* ['a', 'b', 'c'];
}
gen().next(); // { value: "a", done: false }
let [a, b, c] = new Set(['a', 'b', 'c']);
a; // 'a'
理解 for...of 循环
for...of
接受一个可迭代对象(Iterable),或者能被强制转换/包装成一个可迭代对象的值(如'abc')。遍历时,for...of
会获取可迭代对象的[Symbol.iterator]()
,对该迭代器逐次调用next(),直到迭代器返回对象的done
属性为true
时,遍历结束,不对该value处理。
for...of
循环实例:
var a = ["a","b","c","d","e"];
for (var val of a) {
console.log( val );
}
// "a" "b" "c" "d" "e"
转换成普通for循环示例,等价于上面for...of
循环:
var a = ["a","b","c","d","e"];
for (var val, ret, it = a[Symbol.iterator]();
(ret = it.next()) && !ret.done;
) {
val = ret.value;
console.log( val );
}
// "a" "b" "c" "d" "e"
使迭代器可迭代
在什么是迭代器部分,我们自定义了一个简单的生成迭代器的函数createIterator
,但并该函数生成的迭代器并没有实现可迭代协议,所以不能在for...of
等语法中使用。可以为该对象实现可迭代协议,在[Symbol.iterator]
函数中返回该迭代器自身。
function createIterator(items) {
var i = 0;
return {
next: function () {
var done = (i >= items.length);
var value = !done ? items[i++] : undefined;
return {
done: done,
value: value
};
},
[Symbol.iterator]: function () { return this }
};
}
var iterator = createIterator([1, 2, 3]);
console.log(...iterator)
什么是生成器(Generator)?
生成器函数
生成器函数(GeneratorFunction)是能返回一个生成器(generator)的函数。生成器函数由放在 function 关键字之后的一个星号( * )来表示,并能使用新的 yield 关键字。
function *aGeneratorfunction(){
yield 1
yield 2
yield 3
};
var aGeneratorObject = aGeneratorfunction()
// 生成器对象
aGeneratorObject.toString() // "[object Generator]"
生成器对象既是迭代器,又是可迭代对象
function *aGeneratorfunction(){
yield 1
yield 2
yield 3
};
var aGeneratorObject = aGeneratorfunction()
// 满足迭代器协议,是迭代器
aGeneratorObject.next() // {value: 1, done: false}
aGeneratorObject.next() // {value: 2, done: false}
aGeneratorObject.next() // {value: 3, done: false}
aGeneratorObject.next() // {value: undefined, done: true}
// [Symbol.iterator]是一个无参函数,该函数执行后返回生成器对象本身(是迭代器),所以是可迭代对象
aGeneratorObject[Symbol.iterator]() === aGeneratorObject // true
// 可以被迭代
var aGeneratorObject1 = aGeneratorfunction()
[...aGeneratorObject1] // [1, 2, 3]
在生成器中return
遍历返回对象的done
值为true
时迭代即结束,不对该value
处理。
function *createIterator() {
yield 1;
return 42;
yield 2;
}
let iterator = createIterator();
iterator.next(); // {value: 1, done: false}
iterator.next(); // {value: 42, done: true}
iterator.next(); // {value: undefined, done: true}
done
值为true时迭代即结束,迭代不对该value处理。所以对这个迭代器遍历,不会对值42处理。
let iterator1 = createIterator();
console.log(...iterator); // 1
添加[Symbol.iterator]
使Object
可迭代
根据可迭代协议,给Object
的原型添加[Symbol.iterator]
,值为返回一个对象的无参函数,被返回对象符合迭代器协议。
Object.prototype[Symbol.iterator] = function () {
var i = 0
var items = Object.entries(this)
return {
next: function () {
var done = (i >= items.length);
var value = !done ? items[i++] : undefined;
return {
done: done,
value: value
};
}
}
}
var a = {
name: 'Jimmy',
age: 18,
job: 'actor'
}
console.log(...a) // [ 'name', 'Jimmy' ] [ 'age', 18 ] [ 'job', 'actor' ]
使用生成器简化代码:
Object.prototype[Symbol.iterator] = function* () {
for (const key in this) {
if (this.hasOwnProperty(key)) {
yield [key, this[key]];
}
}
}
var a = {
name: 'Jimmy',
age: 18,
job: 'actor'
}
console.log(...a) // [ 'name', 'Jimmy' ] [ 'age', 18 ] [ 'job', 'actor' ]
生成器委托 yield*
function* g1() {
yield 1;
yield 2;
}
function* g2() {
yield* g1();
yield* [3, 4];
yield* "56";
yield* arguments;
}
var generator = g2(7, 8);
console.log(...generator); // 1 2 3 4 "5" "6" 7 8
最后一个例子
分析下面这段代码:
function* fibs() {
var a = 0;
var b = 1;
while (true) {
yield a;
[a, b] = [b, a + b];
}
}
var [first, second, third, fourth, fifth, sixth] = fibs();
console.log(first, second, third, fourth, fifth, sixth);
在这段代码里,fibs
是一个生成无限长的斐波那契数列的生成器,[a, b] = [b, a + b]
是利用解构赋值的交换赋值写法(=赋值是从右到左计算,所以先计算右侧a+b
,然后才结构,所有有交换赋值的效果),写成生成有限长的数组的ES5写法如下:
function fibs1(n) {
var a = 0;
var b = 1;
var c = 0;
var result = []
for (var i = 0; i < n; i++) {
result.push(a);
c = a;
a = b;
b = c + b;
}
return result;
}
console.log(fibs1(6)) // [0, 1, 1, 2, 3, 5]
而第一段代码里,就是从fibs()
迭代器(生成器是迭代器的子集)中解构出前六个值,代码示例如下:
function* fibs2(n) {
var a = 0;
var b = 1;
for (var i = 0; i < n; i++) {
yield a;
[a, b] = [b, a + b];
}
}
console.log(...fibs2(6))
为什么要使用迭代器、生成器,有什么好处?
...还没想清楚
以上,有很多个人理解的部分,欢迎纠错(* ̄︶ ̄)
参考: