JS迭代器分析
定义
在JavaScript中,迭代器是一种特殊对象,它提供了一种按顺序访问集合元素的机制,并同时记录当前遍历的位置。迭代器必须实现一个名为 next 的方法,该方法返回一个包含两个属性 value 和 done 的对象。其中,value 是迭代的当前值,done 是一个布尔值,表示是否已达到迭代结束。
内置数据结构
JavaScript 提供了多种内置数据结构,这些数据结构都实现了迭代器,可以用于遍历数据。常见的内置数据结构包括Array、String、Map、Set、WeakMap、WeakSet、TypeArray、NodeList 和 HTMLCollection。此外,arguments 对象也可以用于迭代。
迭代器的应用
for...of
在记忆中,for...of 通常用于数组的遍历。然而,实际上,只要实现了迭代器接口的数据结构都可以遍历,例如字符串、伪数组 arguments 等。甚至可以自定义迭代器来实现对象的遍历:
const iterable = {
from: 1,
to: 5,
[Symbol.iterator]() {
return this;
},
next() {
if (this.from <= this.to) {
return { value: this.from++, done: false };
} else {
return { done: true };
}
}
};
for (const value of iterable) {
console.log(value);
}
另外,值得一提的是,生成器对象本身也实现了迭代器接口。众所周知,生成器对象会在每次调用 next 方法时恢复执行方法体内剩下的代码语句,直到遇到下一个 yield 表达式时暂停执行。这种行为使得生成器可以在每次迭代中产生中间的值(一个包含两个属性 value 和 done 的对象)。当我们使用 for...of 遍历生成器对象时,该循环会自动调用生成器的 next 方法,并且在迭代器指示完成之前会持续遍历。因此,利用 for...of 来遍历生成器对象是最佳的方式。
async function* asyncNumberGenerator() {
yield 1;
yield 2;
yield 3;
}
(async () => {
for await (const value of asyncNumberGenerator()) {
console.log(value);
}
})();
扩展运算符(...)
在ES6中,扩展运算符(...)提供了一种便捷的方式来将可迭代的数据结构(如Set或Map)展开为数组。这背后的机制是扩展运算符会自动调用迭代器接口,从而获取元素并将它们放入一个新数组中。值得指出的是,只有当对象实现了迭代器接口时,扩展运算符才能够发挥作用。如果遇到没有迭代器接口的类数组对象,比如具有数字键和 length 属性的对象,仅凭扩展运算符是无法将其转换为数组的。在这种情况下,我们通常会使用Array.from方法来完成转换。
function doSomething (){
return [...arguments]
}
doSomething('a','b','c'); // ["a","b","c"]
Array.from
Array.from 接受三个参数,但只有 arrayLike 是必须的:
- arrayLike: 你想要转换成数组的类数组对象或可迭代对象
- mapFn: 类似于数组的map方法,用来对每个元素进行处理,将处理后的值放入返回的数组
- thisArg: 绑定 mapFn 中用到的 this
当 Array.from 只传入第一个参数,满足类数组对象或可迭代对象条件都可以转换成数组
let arr = Array.from('juejin');
console.log(arr); // ["j", "u", "e", "j", "i", "n"]
let arr1 = Array.from({ length: 3 });
console.log(arr1); // [undefined, undefined, undefined]
let arr2 = Array.from({ 0: 123, length: 3 });
console.log(arr2); // [123, undefined, undefined]
Array.from 还可以接受第二个参数,作用类似于数组的 map 方法,用来对每个元素进行处理,处理后的值放入返回的数组。
Array.from([1, 2, 3], (x) => x * x)// [1, 4, 9]
// 等同于
Array.from([1,2,3].map(x => x * x))
// 区别在于后者会创建一个中间数组
如果 mapFn 函数中使用了 this 关键字,还可以传入 Array.from 的第三个参数,用于绑定 this。
Array.from 可以将各种值转换为真正的数组,并提供 map 功能。这意味着,只要有一个原始的数据结构,就可以先对它的值进行处理,然后转换为规范的数组结构,从而可以使用数量众多的数组方法。
Array.from({ length: 2 }, () => 'jack')// ['jack', 'jack']
// 下面是实际开发的代码逻辑,匿一下变量哈
const createArray = (record: Record, statusArray: Status[]) => {
const aStatus = statusArray.find((v) => v.id === record.id)?.aStatus ?? [];
const maxLen = Math.max(record.steps?.length ?? 0, aStatus.length);
return Array.from({ length: maxLen }, (_, i) => ({
status: aStatus[i]?.status,
a: record.steps?.[i]?.a,
}));
};