【源码阅读】arrify|如何将一个值转换为一个数组?

133 阅读3分钟

本文参加了由公众号@若川视野 发起的每周源码共读活动, 点击了解详情一起参与

这是源码共读的第33期 | arrify 转数组,链接:arrify 转数组

源码解读

export default function arrify(value) {
	// 如果是null或者undefined,返回[]
	if (value === null || value === undefined) {
		return [];
	}

	// 如果是数组,返回原本的value
	if (Array.isArray(value)) {
		return value;
	}

	// 如果是字符串,因为字符串本身也是可迭代对象,为了保证值的完整,且避免进入下面的if判断,在这里返回[value]
	if (typeof value === 'string') {
		return [value];
	}

	// 如果是可迭代对象,通过扩展运算符(...)返回数组(扩展运算符内部是调用了Iterator接口) 
	if (typeof value[Symbol.iterator] === 'function') {
		return [...value];
	}

	// 其他类型包裹在数组下即可
	return [value];
}

疑惑点

1、 为什么类数组不能进行转换。

[issue]('https://github.com/sindresorhus/arrify/issues/2')里作者解释了不转换类数组对象的原因。

2、为什么要判断可迭代对象。

JavaScript原有的表示“集合”的数据结构,主要有数组和对象,ES6又添加了`Map`和`Set`。这就有了四种数据集合,用户还可以组合它们,定义自己的数据结构,比如数组的成员是`Map`,`Map`的成员是对象。这样就需要一种统一的接口机制,来处理不同的数据结构。

遍历器就是这样一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构,只要部署Iterator接口,就可以完成遍历操作(既依次处理该数据结构的所有成员)。

这段概念参考自 [es6]('https://es6.ruanyifeng.com/#docs/iterator')

回到问题为什么要判断可迭代对象,因为任何数据结构,只要部署Iterator接口,就可以完成遍历操作。既然能完成遍历操作,那么就能通过扩展运算符(...)将值转换为数组。

知识点

1、Iterator是什么?

它就是一种接口,为不同的数据类型提供统一的访问机制。

2、Iterator有什么作用?

  1. 为各种数据结构,提供一个统一的、简便的访问接口。
  2. 使得数据结构的成员能够按某种次序排序。
  3. ES6创造了一种新的遍历命令for...of循环,Iterator接口主要提供for...of消费。
  4. 原生具备Iterator接口的数据结构 (Array、Map、Set、String、TypedArray、arguments对象、NodeList对象)。

3、Iterator的使用场景

  1. 解构赋值
const test = [1, 2, 3, 4];
const [x, ...result] = test;
console.log(x); // 1
cnosole.log(result); // [2, 3, 4]

  1. 扩展运算符
const iterator = 'iterator';
console.log([...iterator]); // ['i', 't', 'e', 'r', 'a', 't', 'o', 'r']
  1. yield后面跟着一个可遍历的结构,它会调用该结构的遍历器接口

function* genreator (){
	yield 1;
	yield* [2, 3, 4];
	yield 5;
}

const iterator = genreator();

iterator.next(); // { done: false, value: 1}
iterator.next(); // { done: false, vlaue: 2 }
iterator.next(); // { done: flase, value: 3 }
iterator.next(); // { done: false, value: 4 }
iterator.next(); // { done: false, value: 5 }
iterator.next(); // { done: true, value: undefined }
  1. 由于数组的遍历会调用遍历器接口,所以任何接受数组作为参数的场合,其实都调用了遍历器接口,如
for...of

Array.form

Map()

Set()

WeakMap()

WeakSet()

Promise.all

Promise.race

4、什么是类数组对象?

  1. 不是数组,无法使用Array.prototype上的属性。
  2. 有length属性。
  3. 能通过索引访问。

5、类数组如何转换成数组?

const typedArray = { 0: 0, 1: 1, 2: 2, 3: 3, length: 4 }
console.log(Array.from(TypedArray)); // [0, 1, 2, 3]

6、运行npm run test ( 实际运行 “xo && ava && tsd”)

  1. xo 开箱即用的Linter,默认提供优质的esLint配置。
  2. ava 轻量高效简单易用的测试运行器,优势并行执行测试用例,执行根目录下的test.js
  3. tsd 类型定义文件编写测试,检测TypeScript类型声明定义,执行xxx.test-d.ts

学习所得

  1. Iterator是什么、有什么作用、有什么使用场景
  2. 巩固类数组的定义及数组转换。
  3. 学会通过xo & ava & tsd 实现零配置满足eslint校验、测试用例检测,测试类型定义检测。