JavaScript迭代器是在ES6中引入的,它们用于遍历一系列值,通常是某种集合。根据定义,迭代器必须实现一个next()函数,该函数以{ value, done }的形式返回一个对象,其中value是迭代序列中的下一个值,done是一个布尔值,用于确定序列是否已经被消耗。
JS对象都有一个默认的迭代器Symbol.iterator。有了Symbol.iterator迭代器,它就可以被for...of循环遍历。如Array、String、Map、Set等内置类型都有默认的迭代器,因此,可以直接使用for...of循环遍历它们。
1.自定义迭代器
当然我们也可以通过为对象添加Symbol.iterator属性来创建自定义迭代器,下面是一个用数组实现的链表示例:
class LinkedList {
constructor(data) {
this.data = data;
}
firstItem() {
return this.data.find(i => i.head);
}
findById(id) {
return this.data.find(i => i.id === id);
}
[Symbol.iterator]() {
let item = { next: this.firstItem().id };
return {
next: () => {
item = this.findById(item.next);
if (item) {
return { value: item.value, done: false };
}
return { value: undefined, done: true };
},
};
}
}
const myList = new LinkedList([
{ id: 'a10', value: 'First', next: 'a13', head: true },
{ id: 'a11', value: 'Last', next: null, head: false },
{ id: 'a12', value: 'Third', next: 'a11', head: false },
{ id: 'a13', value: 'Second', next: 'a12', head: false },
]);
for (let item of myList) {
console.log(item); // 'First', 'Second', 'Third', 'Last'
}
2.生成器
生成器是指返回迭代器的函数,通过 function
后面加个 *
来表示,函数的内部使用 yield 关键字
来返回迭代器对象,类似如下形式:
function* foo(index) {
while (index < 2) {
yield index;
index++;
}
}
const iterator = foo(0);
console.log(iterator.next().value);
// Expected output: 0
console.log(iterator.next().value);
// Expected output: 1
3.实战
在网上看到一个不错的面试题,比较两个字符串的大小,详细要求如下:
1.两个字符串都是用 - 链接的数字,比如:1-2-33-41-5
2.比较方式是从左到右,依次比较每个数字的大小,遇到相等的数字继续向后比较,遇到不同的数字直接得到比较结果
3.比较规则:
s1 > s2 return 1
s1 < s2 return -1
s1 === s2 return 0
常规做法是,将字符串以 - 分割为数组,这样两个数组按项依次进行比较,最终返回结果。
这样的做法可以得到正确的结果,如果考虑到这个字符串有很多个数字拼接,那么,还有提升的空间,就是使用迭代器。下面是使用生成器实现的示例:
function* walk(str) {
let s = '';
for (let c of str) {
if (c === '-') {
yield Number(s);
s = '';
} else {
s += c;
}
}
if (s) { // last number
yield Number(s);
}
}
// const it = walk('1-2-3-4-5');
// console.log(it.next()); // { value: 1, done: false }
// console.log(it.next()); // { value: 2, done: false }
// console.log(it.next()); // { value: 3, done: false }
// console.log(it.next()); // { value: 4, done: false }
// console.log(it.next()); // { value: 5, done: false }
// console.log(it.next()); // { value: undefined, done: true }
function compare(s1,s2) {
const it1 = walk(s1);
const it2 = walk(s2);
while (true) {
const r1 = it1.next();
const r2 = it2.next();
if (r1.done && r2.done) {
return 0;
}
if (r1.done) {
return -1;
}
if (r2.done) {
return 1;
}
if (r1.value < r2.value) {
return -1;
}
if (r1.value > r2.value) {
return 1;
}
}
}
console.log(compare('1-2-33-41-5', '1-5-21-33-41')); // -1
console.log(compare('1-2-33-41', '1-2-33-41-5')); // -1
console.log(compare('1-2-33-41-5', '1-2-33-41-5')); // 0
console.log(compare('1-3-33-41-5', '1-2-33-41-5')); // 1
console.log(compare('1-2-33-41-5', '1-2-33-41')); // 1
您在访问GitHub时是否经常无法访问?那么除了使用VPN以外,不妨到开源精选(Awesome Top)上逛一逛,这里精选了优秀的开源项目,在这里不仅可以看到项目Readme和官网地址,还可以了解一些开源的热门榜单和趋势!