前言
- 本文参加了由公众号@若川视野 发起的每周源码共读活动, 点击了解详情一起参与。
- **这是源码共读的第33期,链接:【若川视野 x 源码共读】第33期 | arrify 转数组
实战与阅读源码都是实现技术成长的良好手段。 通过分析源码、阅读相关文章,可以了解知识点的实际应用场景,学习大佬的代码思路,从而能达到夯实基础、查缺补漏的效果。
源代码
本次来分析arrify函数,其主要功能就是将传入的参数进行数组化。
export default function arrify(value) {
// 判断 value 是否为空或者未定义
if (value === null || value === undefined) {
return [];
}
// 判断 value 是否为数组
if (Array.isArray(value)) {
return value;
}
// 判断 value 是否为字符串
// Q1:为什么要单独判断该类型?
if (typeof value === 'string') {
return [value];
}
// value为可遍历对象,value[Symbol.iterator]返回一个遍历器对象
// 若遍历器对象为函数类型,说明 value 为支持迭代器的内置类型,则对 value 进行解构
if (typeof value[Symbol.iterator] === 'function') {
return [...value];
}
return [value];
}
源码分析
1. 迭代器 Iterator
在了解Symbol.iterator前,我们必须先去了解迭代器。
迭代器其实是一种接口,它为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署Iterator接口,就可以完成遍历操作。
Iterator的遍历过程如下:
- 创建一个指针对象,指向当前数据结构的起始位置。
- 第一次调用指针对象的
next方法,可以将指针指向数据结构的第一个成员。 - 第二次调用指针对象的
next方法,可以将指针指向数据结构的第二个成员。 - 不断调用指针对象的
next方法,直到它指向数据结构的结束位置。
每一次调用next方法,都会返回数据结构的当前成员的信息(即返回一个对象),对象具体结构如下:
{
// value 属性为当前成员的值
value: xxx,
// done 属性为布尔值,表示当前遍历是否结束
done: true / false
}
2. Symbol.iterator
ES6 规定,默认的Iterator接口部署在数据结构的Symbol.iterator属性上,即一个数据结构只要具有Symbol.iterator属性,就可以被认为是可遍历的。
它为一个对象定义了默认的迭代器,即该迭代器可以被for...of循环使用。Symbol.iterator属性本身是一个函数,,执行该函数就会返回一个遍历器。
一些支持迭代器的内置类型如:Array/String/Map/Set/TypedArray/NodeList对象/函数的arguments对象.
const arr = [1, 2, 3];
console.log(typeof arr[Symbol.iterator]); // function
let iter = arr[Symbol.iterator]();
iter.next() // { value: 1, done: false }
iter.next() // { value: 2, done: false }
iter.next() // { value: 3, done: false }
iter.next() // { value: undefined, done: true }
对于Symbol.iterator,存在一些语句和表达式专用于它,例如for...of循环、解构、yield*和扩展运算符。
for...of 循环使用迭代器
for...of循环遍历某种数据结构时,该循环会自动寻找Iterator接口。
而我们都知道,对象是没有迭代器的,所以我们可以为对象构造一个迭代器属性,然后使用for...of循环。
const person = {
name: "btqf",
age: 22,
gender: "male"
};
Object.defineProperty(person, Symbol.iterator, {
enumberable: false,
configurable: false,
writable: false,
value() {
const that = this; // that = {name: "btqf", age: 22, gender: "male"}
let index = 0;
const key = Object.keys(that); // key = ["name", "age", "gender"]
return {
next() {
return {
value: that[key[index++]],
done: index > key.length
}
}
}
}
})
for (const item of person) {
console.log(item);
}
// btqf
// 22
// male
解构赋值默认调用迭代器
let set = new Set([1, 2, 3]);
let [x, y] = set; // x = 1, y = 2
let [first, ...rest] = set; // first = 1, rest = [2, 3]
yield* 生成器默认调用迭代器
let generator = function* () {
yield 1;
yield* [2,3,4];
yield 5;
};
var iterator = generator();
iterator.next() // { value: 1, done: false }
iterator.next() // { value: 2, done: false }
iterator.next() // { value: 3, done: false }
iterator.next() // { value: 4, done: false }
iterator.next() // { value: 5, done: false }
iterator.next() // { value: undefined, done: true }
扩展运算符默认调用迭代器
// 例一
var str = 'hello';
[...str] // ['h','e','l','l','o']
// 例二
let arr = ['b', 'c'];
['a', ...arr, 'd']
// ['a', 'b', 'c', 'd']