前端|arrify 转数组 源码学习

82 阅读4分钟

我正在参与掘金会员专属活动-源码共读第一期, 点击参与

【初心】

学习源码不是为了面试,想提高代码思维,提升编码水平,开拓写代码的思路,想通过源码学习更能很好的理解原理方面的知识。

仓库代码

入口文件

从仓库的 package.json 文件的 exports 字段可以看到入口文件为 index.js , main 字段也可以定义入口文件,但是 main 能力有限,只能定义一个主入口。在支持 exports 字段的 Node.js 版本中,exports 的优先级要高于 main

1705310970344.png

源码分析

export default function arrify(value) {
	// 如果输入值 value 为 null 或 undefined,返回空数组
	if (value === null || value === undefined) {
		return [];
	}

	  // 使用 isArray 方法检查输入值 value 是否为数组,true: 则直接返回 value
	if (Array.isArray(value)) {
		return value;
	}

	 // 使用 typeof 操作符来检查输入值是否 为字符串。是 则将 value 包装在数组中并返回
	if (typeof value === 'string') {
		return [value];
	}

	 // 检查输入值是否具有迭代器方法,是  则使用扩展运算符 ... 将可迭代对象转换为数组并返回
	//  迭代器方法是一个特殊的函数,执行迭代器方法会返回一个迭代器对象,可以使用 for...of 来遍历迭代器对象
	if (typeof value[Symbol.iterator] === 'function') {
		return [...value];
	}

	return [value];
}

源码中用到es6中的Symbol.iterator属性。
Iterator是一个遍历器接口,是部署在数据结构上。es6规定,默认的iterator接口部署在数据结构的Symbol.iterator属性,或者说,一个数据结构只要具有Symbol.iterator属性,就可以认为是可以遍历的,也就是说我们自定义的类,只要部署了Symbol.iterator属性就可以遍历了,就可以通过 for...of 进行遍历。

Iterator 的作用有三个:一是为各种数据结构,提供一个统一的、简便的访问接口;二是使得数据结构的成员能够按某种次序排列;三是 ES6 创造了一种新的遍历命令for...of循环,Iterator 接口主要供for...of消费。

arrify示例

arrify('foo') // ['foo']
arrify(1);    // [1]
arrify([1]);  // [1]
arrify(null) // []
arrify(undefined) // []
arrify(new Map(['1', '2'])) // ['1', '2']
arrify(new Set([1, 2, 3]));  // [1, 2, 3]

【总结】

  • 1、任何数据结构只要部署 Iterator 接口,就可以完成遍历操作。
  • 2、对象(Object)是没有默认的 Iterator 接口,直接使用for...of会报错;ArrayMapSetStringTypedArray,函数的 arguments 对象,NodeList 对象是具有默认的 Iterator 接口。,可以直接使用for...of循环
let onj = {
  edition: 6,
  committee: "TC39",
  standard: "ECMA-262"
};

// 对于普通的对象,for...in循环可以遍历数组的键名,for...of循环会报错。
for (let e in obj) {
  console.log(e);
}
// edition
// committee
// standard

for (let e of obj) {
  console.log(e);
}
// TypeError: obj is not iterator



// 普通的对象,for...in循环可以遍历数组的键名一种解决方法是,使用Object.keys方法将对象的键名生成一个数组,然后遍历这个数组
for (var i of Object.keys(obj)) {
  console.log(i + ': ' + obj[i]);
}
// edition: 6,
// committee: "TC39",
// standard: "ECMA-262"

  • 3、ArrayMapSetStringTypedArray,函数的 arguments 对象,NodeList 对象是具有默认的 Iterator 接口。,可以直接使用for...of循环
    Set 结构遍历时,返回的是一个值,而 Map 结构遍历时,返回的是一个数组,该数组的两个成员分别为当前 Map 成员的键名和键值。
var engines = new Set(["Gecko", "Trident", "Webkit", "Webkit"]);
for (var e of engines) {
  console.log(e);
}
// Gecko
// Trident
// Webkit


let map = new Map().set('a', 1).set('b', 2);
for (let pair of map) {
  console.log(pair);
}
// ['a', 1]
// ['b', 2]

for (let [key, value] of map) {
  console.log(key + ' : ' + value);
}
// a : 1
// b : 2
  • 4、最原始是for循环-->数组内置的forEach-->因为循环中出现问题forEach无法跳出循环,break和return不奏效,又出现for...of循环得到键值、for...in循环得到键名
// 1.
var arr = ['a', 'b', 'c', 'd'];
for (let a in arr) {
  console.log(a); // 0 1 2 3
}
for (let a of arr) {
  console.log(a); // a b c d
}

// 2.
let arr = [3, 5, 7];
arr.foo = 'hello';

for (let i in arr) {
  console.log(i); // "0", "1", "2", "foo"
}

for (let i of arr) {
  console.log(i); //  "3", "5", "7"
}

致自己:像for...of与for...in区别、集合数组遍历的方法都有哪些、Iterator 接口与 Generator 函数等都需要拓展了解。尤其Iterator还需要理解的有好多,现在经验不足也了解 不了那么深,吸收不了那么多知识,对于Iterator只能理解浅层次的知识,学一点是一点,慢慢积累,或许现在看不懂的,一段时间后又看懂了。看不懂不必焦虑,允许自己有盲区

【参考】