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: 你想要转换成数组的类数组对象或可迭代对象
    • 可迭代对象(例如 MapSet 对象)
    • 如果对象是不可迭代的,类数组对象(带有 length 属性和索引元素的对象)
  • 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,
  }));
};
posted @ 2024-01-04 22:22  晨米酱  阅读(26)  评论(0编辑  收藏  举报