迭代器 iterator
在 JavaScript 中,迭代器是一个对象,它定义一个序列,并在终止时可能返回一个返回值。更具体地说,迭代器是通过使用
next()
方法实现Iterator protocol
的任何一个对象,该方法返回具有两个属性的对象: value,这是序列中的next
值;和done
,如果已经迭代到序列中的最后一个值,则它为true
。如果value
和done
一起存在,则它是迭代器的返回值。(来自 CDN)
简单来说,迭代器就是一个帮助我们对某个数据结构(可迭代对象)进行遍历的对象。
可迭代协议
在 JavaScript 中,「迭代器」
也是一个具体的对象,这个对象需要符合迭代协议:可迭代协议(Iterable Protocols)
和 迭代器协议(Iterator Protocols)
;
可迭代协议:表明可迭代
- 可迭代协议允许 JavaScript 对象定义或定制它们的迭代行为;
- 要成为可迭代对象, 一个对象必须实现 @@iterator 方法。
迭代器协议:表明迭代方式
- 迭代器协议定义了产生一系列值(无论是有限个还是无限个)的标准方式。当值为有限个时,所有的值都被迭代完毕后,则会返回一个默认返回值;
- 迭代器含有 next 方法,该方法执行会返回一个包含 done 和 value 的对象。
js
var arr = [1,2];
var arrIt = arr[Symbol.iterator]();
arrIt.next(); // {value: 1, done: false}
arrIt.next(); // {value: 2, done: false}
arrIt.next(); // {value: undefined, done: true}
可迭代对象
Array、String、TypedArray、Map、Set。 他们都有一个键为
「@@iterator」
的属性,通过Symbol.iterator
访问该属性。可以通过data[Symbol.iterator]()
返回一个「迭代器iterator」
,调用它的.next()
方法进行迭代!
Symbol
ES6 新引入的一种原始数据类型,可返回一个独一无二的值。
如何判断是否可迭代
js
return data[Symbol.iterator] && typof data[Symbol.iterator] == ‘function' ? true : false;
即满足以下条件:
- 有[Symbol.iterator]属性;
- [Symbol.iterator]的值为函数;
- 函数返回值是一个对象,包含 value 和 done。
使用场景
- 通过
for…of
进行遍历; - 解构赋值;
- 扩展运算符(
…
),可以将任何部署了Iterator
接口的数据结构,转为数组; - 创建一些对象时:new Map([Iterable])、new WeakMap([iterable])、new Set([iterable])、new WeakSet([iterable]);
- 一些方法的调用:Promise.all(iterable)、Promise.race(iterable)、Array.from(iterable);
实现迭代器
Generator(生成器)生成
js
// A 有限迭代器
let babies = ['hpy', 'zs', 'yxz', 'hfcj', 'zpj', 'cfy', 'yxx', 'lyc', 'zls', 'yxn', 'cym’];
babies[Symbol.iterator] = function* () {
let index = 0;
while (index < this.length) {
yield this[index];
index++;
}
};
var bIt = babies[Symbol.iterator]();
console.log(bIt.next()); //{value: 'hpy', done: false}
for (let baby of babies) {
console.log(baby);
}
// B 无限迭代器
function* idMaker(){
let index = 0;
while(true)
yield index++;
}
let gen = idMaker(); // "Generator { }"
console.log(gen.next().value); // 0
console.log(gen.next().value); // 1
console.log(gen.next().value); // 2
// ...
普通函数生成
js
const info = {
friends: ["aaa", "bbb", "ccc"],
[Symbol.iterator]() {
let index = 0;
const infoIterator = {
// next方法用箭头函数, 这样next方法中就不再绑定this, this会去上层作用域中寻找, 上层作用域中的this会指向info。
next: () => {
if (index < this.friends.length) {
return { done: false, value: this.friends[index++] };
} else {
return { done: true };
}
},
};
return infoIterator;
},
}
让对象可迭代 1:[Symbol.iterator]
方法返回的对象包含 next
方法
js
let range = {
start: 0,
end: 3,
[Symbol.iterator]: function () {
return {
next: () => {
if (this.start > this.end) {
return { value: undefined, done: true };
}
return { value: this.start++, done: false };
},
};
},
};
console.log(range[Symbol.iterator]().next()); //{ value: 0, done: false }
for (let item of range) {
console.log("range item: ", item);
}
// range item: 0
// range item: 1
// range item: 2
// range item: 3
让对象可迭代 2:[Symbol.iterator]
方法和next
方法分开定义
js
let range2 = {
start: 0,
end: 3,
[Symbol.iterator]: function () {
return this;
},
next: function () {
if (this.start > this.end) {
return { value: undefined, done: true };
}
return { value: this.start++, done: false };
},
};
console.log(range2.next()); //{ value: 0, done: false }
console.log(range2.next()); //{ value: 1, done: false }
for (let item of range2) {
console.log("range2 item: ", item);
}
// range2 item: 2
// range2 item: 3
_ 关于对象的迭代:如果先调用了next()
方法,那么for...of
就只会遍历剩余的项。比如这里只会输出range2 item: 2 range2 item: 3
_
看一些例子
js
var obj = {};
var arr = [1,2,3,4]
var str = 'abc'
var map = new Map();
map.set('age',30)
var set = new Set([1,2,3])
var sb_a = Symbol('a');
var sb_b = Symbol('b');
console.log('sb_b:', sb_b)
obj[sb_a] = 'a'
obj[sb_b]='b';
console.log(obj)
console.log('obj[sb_a]:', obj[sb_a])
console.log('\n')
console.log(map)
console.log(set)
console.log('\n')
var arrIt = arr[Symbol.iterator]();
console.log('arrIt.next():',arrIt.next())
console.log('arrIt.next():',arrIt.next())
console.log('arrIt.next():',arrIt.next())
console.log('arrIt.next():',arrIt.next())
console.log('arrIt.next():',arrIt.next())
console.log('\n')
var strIt = str[Symbol.iterator]();
console.log('strIt.next():',strIt.next())
console.log('strIt.next():',strIt.next())
console.log('strIt.next():',strIt.next())
console.log('strIt.next():',strIt.next())
console.log('\n')
var mapIt = map[Symbol.iterator]();
console.log('mapIt.next():',mapIt.next())
console.log('mapIt.next():',mapIt.next())
console.log('\n')
var setIt = set[Symbol.iterator]();
console.log('setIt.next():',setIt.next())
console.log('setIt.next():',setIt.next())
console.log('setIt.next():',setIt.next())
console.log('setIt.next():',setIt.next())
console.log('\n')
console.log(typeof set[Symbol.iterator])