原文: A Simple Guide to ES6 Iterators in JavaScript with Examples
迭代器是ES6提供的对数据集进行遍历的一种新方式
简介
我们有如下的数组:
const myFavouriteAuthors = [
'Neal Stephenson', 'Arthur Clarke', 'Isaac Asimov', 'Robert Heinlein'
];要获取数组中的元素,有很多种方式,例如for循环、while判断、for-of迭代。
如果考虑一个对象,该对象包含一个新对象allAuthors,allAuthors包含上面提到的数组:
const myFavouriteAuthors = {
allAuthors: {
fiction: [ 'Agatha', 'Dr. Seuss' ],
science: [ 'Neal Stephenson', 'Arthur Clarke', 'Isaac Asimov', 'Robert Heinlein' ]
}
};这个时候如何对该对象进行迭代?采用下面的方法会报错:
for (let author of myFavouriteAuthors) {
console.log(author)
}// TypeError: {} is not iterable可迭代和迭代器
我们给该对象定义一个新方法,来获取所有allAuthors包含的数组:
const myFavouriteAuthors = {
allAuthors: {
fiction: [ 'Agatha', 'Dr. Seuss' ],
science: [ 'Neal Stephenson', 'Arthur Clarke', 'Isaac Asimov', 'Robert Heinlein' ]
},
getAllAuthors() {
const authors = []
for(const author of this. allAuthors.fiction) {
authors.push(author)
}
for(const author of this. allAuthors.science) {
authors.push(author)
}
return authors
}
};这样通过getAllAuthors我们就可以获取所有authors了,但是这样做有几个问题:
- getAllAuthors 这个函数名不具有代表性,我也可以命名为retriveAllAuthors
- getAllAuthors 返回的数据格式也不具有代表性,在该例子中返回的是一个数组,当然我也可以修改代码返回以下格式:
[ {name: 'Agatha Christie'}, {name: 'J. K. Rowling'}, ... ]那么要如何设定一个规则,保证方法名称和返回的数据格式是确定且不可改变的?ES6新增了一种原始数据格式 Symbol ,通过 Symbol() 产生的值都是唯一的,可以作为对象的属性。同时,Symbol.iterator 会生成一个迭代器,该迭代器有 next 方法,next 方法会返回 value 和 done.
- 可迭代的数据结构是指外部可以获取它内部元素的数据结构,实现的方法是通过提供 Symbol.iterator 方法,这个方法是迭代器的构造函数,可以生成迭代器。
- 迭代器是一个可以遍历数据结构元素的指针。
让对象可迭代
为了让对象可迭代,我们需要实现 Symbol.iterator 这个方法,这里用到了对象的计算属性。
const iterable = { [Symbol.iterator]() {
let step = 0
const iterator = {
next() {
step += 1
if(step === 1) {
return { value: "This", done: false }
} else if(step === 2){
return { value: "is", done: false }
} else if(step === 3) {
return { value: "iterable", done: false }
}
return { value: undefined, done: true }
}
}
return iterator
}}
let iterator = iterable[Symbol.iterator]
iterator.next() // { value: "This", done: false }
iterator.next() // { value: "is", done: false }
iterator.next() // { value: "iterable", done: false }
iterator.next() // { value: undefined, done: true }上面的代码大致就是 for-of 的内部实现原理,创建一个迭代器后循环调用 next 方法直到返回的 done 为 true.
JS中可迭代的数据结构
- 数组和类数组
- 字符串
- Maps
- Sets
实践出真知
数组解构
const array = ['a', 'b', 'c', 'd', 'e'];const [first, ,third, ,last] = array;等同于
const array = ['a', 'b', 'c', 'd', 'e'];
const iterator = array[Symbol.iterator]();
const first = iterator.next().valueiterator.next().value // Since it was skipped, so it's not assigned
const third = iterator.next().valueiterator.next().value // Since it was skipped, so it's not assigned
const last = iterator.next().value扩展运算符
const array = ['a', 'b', 'c', 'd', 'e'];
const newArray = [1, ...array, 2, 3];下面代码同样可以实现:
const array = ['a', 'b', 'c', 'd', 'e'];
const iterator = array[Symbol.iterator]();
const newArray = [1];
for (let nextValue = iterator.next(); nextValue.done !== true;
nextValue = iterator.next()) {
newArray.push(nextValue.value);
}
newArray.push(2)
newArray.push(3)myFavoriteAuthors 定义迭代函数
const myFavouriteAuthors = {
allAuthors: {
fiction: [
'Agatha Christie',
'J. K. Rowling',
'Dr. Seuss' ],
scienceFiction: [
'Neal Stephenson',
'Arthur Clarke',
'Isaac Asimov',
'Robert Heinlein' ],
fantasy: [
'J. R. R. Tolkien',
'J. K. Rowling',
'Terry Pratchett' ],
},
[Symbol.iterator]() {
// 获取一个嵌套数组 [[],[]]
const genres = Object.values(this.allAuthors);
// 初始化嵌套数组下标和子数组下标
let currentAuthorIndex = 0;
let currentGenreIndex = 0;
return {
// 定义 next 方法
next() {
// 获取当前子数组
const authors = genres[currentGenreIndex];
// 根据子数组长度判断该子数组是否完成迭代
const doNothaveMoreAuthors = !(currentAuthorIndex < authors.length);
if (doNothaveMoreAuthors) {
// 数组下标+1
currentGenreIndex++;
// 子数组下标归零
currentAuthorIndex = 0;
}
// 根据数组长度判断是否迭代完成
const doNotHaveMoreGenres = !(currentGenreIndex < genres.length);
if (doNotHaveMoreGenres) {
// 是则返回 done 为 true
return { value: undefined, done: true };
}
// 返回当前子数组下标对应的值
// 子数组下标+1
return {
value: genres[currentGenreIndex][currentAuthorIndex++],
done: false
}
}
};
}};
for (const author of myFavouriteAuthors) { console.log(author);}
console.log(...myFavouriteAuthors)通过以上知识,你大致可以了解 ES6 新增的 iterator 是如何工作的,接下来就可以愉快地学习 generator 生成器了,这位博主同样有一篇讲述 generator 工作原理的,稍后我也会翻译并做整理,与大家共享~