什么是迭代器?
迭代器的意义是什么?
可迭代协议
可迭代协议允许 JavaScript 对象去定义或定制它们的迭代行为, 例如(定义)在一个 for..of 结构中什么值可以被循环(得到)。
当一个对象实现了 @@iterator
方法,就成为了可迭代对象。意思是这个对象(或者它原型链上的某个对象)必须有一个名字是 Symbol.iterator 的属性:
属性 | 值 |
---|---|
[Symbol.iterator] | 返回一个对象的无参数函数,被返回对象需要符合迭代器协议。 |
当该对象需要被迭代的时(例如用于一个for..of循环中),它的@@iterator
方法此时会被调用,返回的那个符合迭代器协议的对象在迭代中不断产生值。
迭代器协议
迭代器协议定义了一种标准的方式来产生一个有限或无限序列的值,并且所有的值都已经被迭代后,就会有一个默认的返回值。
当一个对象只有满足下述条件才会被认为是一个迭代器:
它实现了一个 next()
的方法并且拥有以下含义:
属性 | 值 |
---|---|
next | 返回一个对象的无参函数,被返回对象拥有两个属性:
next 方法必须要返回一个对象,该对象有两个必要的属性: done和value,如果返回一个非对象值(比如false和undefined) 会展示一个 TypeError ("iterator.next() returned a non-object value") 的错误 |
创建一个迭代器对象
var myIterator = {
next: function() {
let ret = Math.random(10);
if (ret > 0.8) {
console.log('exit:', ret);
return {done: true};
}else {
return {done: false, value: ret};
}
},
[Symbol.iterator]: function() { return this }
}
在 for...of
循环中使用该迭代器:
for (let c of myIterator) {
console.log(c);
}
可以看到类似以下输出:
0.02448891409447973
0.7289375899470307
0.35493549963412474
0.4402992067394058
0.3310587101417566
0.7577731432499597
0.43807458917601916
exit: 0.8322643163446177
内置可迭代对象
String
, Array
, TypedArray
, Map
and Set
是所有内置可迭代对象, 因为它们的原型对象都有一个@@iterator
方法.
以String
为例:
String
是一个内置的可迭代的对象。
String 的默认迭代器会一个接一个返回该字符串的字符:
> let str = "hi";
undefined
> let iterator = str[Symbol.iterator]();
undefined
> iterator.next()
{ value: 'h', done: false }
> iterator.next()
{ value: 'i', done: false }
> iterator.next()
{ value: undefined, done: true }
> iterator.next()
{ value: undefined, done: true }
一些内置的语法结构,比如 spread operator (展开语法:[...val]),内部也使用了同样的迭代协议:
> [...str]
[ 'h', 'i' ]
我们可以通过自己的 @@iterator
方法重新定义迭代行为:
let s = new String("hi");
let iterator = s[Symbol.iterator]();
console.log(iterator.next()); // 'H'
console.log(iterator.next()); // 'i'
console.log(iterator.next()); // { value: undefined, done: true }
s[Symbol.iterator] = function() {
return {
next: function() {
if (this._first) {
this._first = false;
return {value: "bye", done: false};
}else {
return {done: true};
}
},
_first: true
};
}
let iterator2 = s[Symbol.iterator]();
console.log(iterator2.next()); //{ value: 'bye', done: false }
console.log(iterator2.next()); //{ done: true }
console.log(iterator2.next()); //{ done: true }
// 不改变字符串的值
console.log(s + ""); // "Hi"
console.log(...s); // "Bye"
- 字符串直接量
s = "Hi";
,无法重新定义迭代行为。- 重新定义迭代行为,不改变字符串的值。
几种迭代器
简单迭代器
function makeIterator(array){
var nextIndex = 0;
return {
next: function(){
return nextIndex < array.length ?
{value: array[nextIndex++], done: false} :
{done: true};
}
};
}
var it = makeIterator(['yo', 'ya']);
console.log(it.next().value); // 'yo'
console.log(it.next().value); // 'ya'
console.log(it.next().done); // true
无穷迭代器
function idMaker(){
var index = 0;
return {
next: function(){
return {value: index++, done: false};
}
};
}
var it = idMaker();
console.log(it.next().value); // '0'
console.log(it.next().value); // '1'
console.log(it.next().value); // '2'
// ...
生成器式的迭代器
function* makeSimpleGenerator(array){
var nextIndex = 0;
while(nextIndex < array.length){
yield array[nextIndex++];
}
}
var gen = makeSimpleGenerator(['yo', 'ya']);
console.log(gen.next().value); // 'yo'
console.log(gen.next().value); // 'ya'
console.log(gen.next().done); // true
function* idMaker(){
var index = 0;
while(true)
yield index++;
}
var gen = idMaker();
console.log(gen.next().value); // '0'
console.log(gen.next().value); // '1'
console.log(gen.next().value); // '2'
// ...
但是,如果试着将前两种这些迭代器放入for ... of
会发现报错,也就是不是可迭代对象。但是生成器对象既是迭代器又是可迭代对象。
function* idMaker(){
var index = 0;
while(true)
yield index++;
}
var gen = idMaker();
console.log(gen.next());
console.log(gen.next());
for(let a of gen) {
console.log(a);
}
一个良好的迭代即实现了迭代器协议,又实现了可迭代协议,方式就是可迭代协议返回的是自身,之前提到的例子就是这种情况,既是迭代器又是可迭代对象。
var myIterator = {
next: function() {
let ret = Math.random(10);
if (ret > 0.8) {
console.log('exit:', ret);
return {done: true};
}else {
return {done: false, value: ret};
}
},
[Symbol.iterator]: function() { return this }
}