【源码共读】arrify转数组

47 阅读3分钟

614295.jpg

前言

实战与阅读源码都是实现技术成长的良好手段。 通过分析源码、阅读相关文章,可以了解知识点的实际应用场景,学习大佬的代码思路,从而能达到夯实基础、查缺补漏的效果。

源代码

本次来分析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的遍历过程如下:

  1. 创建一个指针对象,指向当前数据结构的起始位置。
  2. 第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员。
  3. 第二次调用指针对象的next方法,可以将指针指向数据结构的第二个成员。
  4. 不断调用指针对象的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']