likes
comments
collection
share

AI 读源码之 arrify

作者站长头像
站长
· 阅读数 9

源码作用

这个迷你的小库,是将传入的值,转化为数组。它的自我介绍是——

Convert a value to an array

源码速览

源码非常简单,只有十来行,非常迷你。

export default function arrify(value) {
	if (value === null || value === undefined) {
		return [];
	}

	if (Array.isArray(value)) {
		return value;
	}

	if (typeof value === 'string') {
		return [value];
	}

	if (typeof value[Symbol.iterator] === 'function') {
		return [...value];
	}

	return [value];
}

AI 解读源码

源码前面都比较好理解,关键是 typeof value[Symbol.iterator] === 'function' 这个判断分支是什么情况呢?我请教了万能的 ChatGPT,并将它的回答翻译并整理如下:

在 arrify 函数中, if (typeof value[Symbol.iterator] === 'function') 行正在检查该值是否具有遍历器接口。

在 JavaScript 中,Symbol.iterator 符号用于创建自定义可遍历对象。 可遍历对象是一个具有遍历器的对象,它允许使用 for...of 循环或扩展运算符 (...) 对其进行循环。

它的工作原理如下:

当您在可遍历对象上使用扩展运算符 (...) 时,它将在内部调用可遍历对象的 Symbol.iterator 方法来获取指针对象。

指针对象是一个具有 next 方法的对象,该方法返回下一个值和指示遍历是否完成的 done 属性。

因此,当 typeof value[Symbol.iterator] === 'function' 计算结果为 true 时,这意味着 value 是一个可遍历对象,您可以使用扩展运算符将其转换为带有 [...value] 的数组。

例如,假设 value 是一个 Set 对象:

const set = new Set([1, 2, 3]);
const array = [...set];
console.log(array); // Output: [1, 2, 3]

在这种情况下,条件 typeof value[Symbol.iterator] === 'function' 将计算为 true,并且扩展运算符将用于将 Set 转换为数组。

arrify 函数中的这种检查允许它处理不同类型的输入,并在需要时将它们转换为数组。 它有助于确保返回值始终是一个数组,无论输入类型如何。

这个回答基本符合我们对遍历器的印象。不过,尽信书不如无书,全靠 AI 就坐等失业。我们还要验证一下 AI 这个回答。

人工解读源码

首先,我查阅关于遍历器的资料,基本验证 AI 的回答正确。

遍历器相关术语有些容易混淆,这里借用阮一峰大佬的中文翻译和 TS 版本规格描述:

interface Iterable { // 遍历器接口
  [Symbol.iterator]() : Iterator, // 注意该方法执行后才返回 Iterator 
}

interface Iterator { // 指针对象
  next(value?: any) : IterationResult,
}

interface IterationResult { // 指针对象的 next 方法返回值
  value: any,
  done: boolean,
}

接着就是重点,自己写一个实现了遍历器接口的对象,看 arrify 能不能很好处理。

const obj = {
  data: [0, 1, 2, 3, 4, 5, 6, 7],
  [Symbol.iterator]() {
    let pointer = 0;
    return {
      next: () => {
        if ((pointer) % 3 === 0) {
          pointer++;
        }
        if (pointer < this.data.length) {
          return {
            value: this.data[pointer++],
            done: false,
          };
        }
        return {
          done: true,
        };
      }
    };
  }
};

这个 obj 是我写的一个对象,它自己有一个 Symbol.iterator 属性,并实现了遍历器接口。为了彰显它自己的个性,每逢 3 的倍数都会跳过遍历。我们先尝尝它的 Symbol.iterator 属性方法返回的指针对象 iterator 能否正常使用。

// 手动遍历,调用指针对象的 next 方法。结果分行输出 1 2 4 5 7
const it = obj[Symbol.iterator]();
let res = it.next();
while (!res.done) {
  console.log(res.value);
  res = it.next();
}

// for of 遍历。结果分行输出 1 2 4 5 7
for (const v of obj) {
  console.log(v);
}

// obj 不是数组,但是可以使用 Array.from 和扩展运算符转换为数组
console.log(Array.isArray(obj)); // false
console.log(Array.from(obj)); // [ 1, 2, 4, 5, 7 ]
console.log([...obj]); // [ 1, 2, 4, 5, 7 ]

运行结果符合预期,说明 obj 对象实现了遍历器接口。接着我们用 arrify 来处理一下。

console.log(arrify(obj)); // [ 1, 2, 4, 5, 7 ]

arrify 也很好地将它转化为数组了。

总结

arrify 是一个用于将值转化为数组的库。解读源码过程中,Symbol.iterator 属性的使用值得学习。对于简单的源码,AI 的解读也基本靠谱,能提升我们学习的效率。