本文参加了由公众号@若川视野 发起的每周源码共读活动, 点击了解详情一起参与。
这是源码共读的第33期 | arrify 转数组
源码仓库
Github仓库地址:arrify
阅读准备
关于迭代器
迭代器本身是一个对象,且需要符合迭代器协议,并提供一个next方法
next方法返回一个具有两个属性的对象:
- done 如果迭代器可以产生序列中下一个值 则为false,如果不能则为true
- value 迭代器返回的任意Javascript值,done为true则为undefined
const names = ["a", "b", "c"];
let index = 0;
const nameIterator = {
next: function () {
if (index < names.length) {
return { done: false, value: names[index++] };
} else {
return { done: true, value: undefined };
}
},
};
console.log(nameIterator.next()); //{ done: false, value: 'a' }
console.log(nameIterator.next()); //{ done: false, value: 'b' }
console.log(nameIterator.next()); //{ done: false, value: 'c' }
console.log(nameIterator.next()); //{ done: false, value: undefined }
生成迭代器的函数
function createArray(arrIterator) {
let index = 0;
return {
next: function () {
if (index < arrIterator.length) {
return { done: false, value: arrIterator[index++] };
} else {
return { done: true, value: undefined };
}
},
};
}
const names = ["a", "b", "c"];
const namesIterator = createArray(names);
console.log(namesIterator.next());
console.log(namesIterator.next());
console.log(namesIterator.next());
console.log(namesIterator.next());
关于迭代对象
当一个对象实现了iterable protocol协议,才是一个可迭代对象,这个对象要求拥有 @@iterator 方法,在代码中它的实现是 [Symbol.iterator] 这个属性上,值为一个迭代器方法
const iterableObj = {
names:['abc','sad'],
[Symbol.iterator]:function(){
let index = 0
return {
next:()=>{
if(index < this.names.length){
return {done:false,value:this.names[index++]}
}else{
return {done:true,value:undefined}
}
}
}
}
}
//通过迭代器生成一个可迭代对象
const iterable1 = iterableObj[Symbol.iterator]()
console.log(iterable1.next()); //{done:false,value:'abc'}
补充:原生迭代对象
大部分Javascript原生对象已经实现了迭代协议,所以他们是具有可迭代的特性,这些对象在被创建的时候,在原型上就实现了这个迭代器属性
常见的已经实现了迭代器协议的对象有这些: Array 、 String 、 Map 、Set 、 Arguments 、NodeList
另外需要提起的还有for...of关键字和 扩展运算符,它的本质就是迭代器的语法糖,只有具有可迭代的特性 才能使用for...of遍历或使用 扩展运算符
一些应用场景
const arrA = [1, 2, 3];
const arrB = [4, 5, 6];
const arrC = [...arrA, ...arrB]; //[1,2,3,4,5,6]
console.log(arrC);
const [one, two, three] = arrC;
console.log(one, two, three); // 1 2 3
Array.from(具有可迭代特性的对象) //将一个可迭代对象转换为数组
- 需要留意的是 Object对象解构 并不是基于迭代特性实现, 它是ES9 新增的特性,只是提供了用于对于对象的解构
const obj = {name:"Harexs",age:22}
const newObj = {...obj}
const {name,age} = obj
源码阅读
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];
}
实现思路
- arrify函数将接受一个value值,如果它是一个 null 或者undefined 那么直接返回一个空数组
- 如果它本身是一个数组 (通过Array.isArray判断) ,那么直接返回它本身不做任何处理
- 如果它是一个字符串, 则返回一个转为含有这个字符的数组 'a' => ['a']
- 如果它具有可迭代特性,即判断它的是否具有 [Symbol.iterator] 属性且是一个function类型,直接通过扩展运算法迭代,返回一个包括所有迭代结果的数组
- 如果以上条件都不符合直接返回一个 含有这个值的数组
package.json拓展
exports
//默认
"exports": "./index.js"
//不同路径的声明
"exports":{
".":"./index.js",
"./foo":"./foo.js"
}
//对应的引入
import { one } from 'arrify' //来源 ./index.js
import { two } from 'arrify/foo' //来源 ./foo.js
const { one } = require("arrify/foo") //来源 ./index.js
const { two } = require("arrify/foo") //来源 ./foo.js
//模块化引入
"exports":{
"import":"./es.js",
"require":"./common.cjs"
}
exports字段定义了不同路径的导出,并且它的优先级大于 main 以及 brower 和 module 字段, 它还支持对于不同模块化情况下的引入
依赖
"devDependencies": {
"ava": "^3.15.0",
"tsd": "^0.14.0",
"xo": "^0.39.1"
}
ava
基于Node.js 的单元测试包,具有简洁的API且快速,并支持Typescript
xo
集成了linter规则的ESLint包,不需要再做额外配置
tsd
通过.test-d.ts 文件做类型定义检查,它们不会被编译测试,只会对类型定义进行静态分析
总结
- 认识可迭代对象以及特性
- 认识原生可迭代对象
- 认识扩展运算符以及for of 的本质
- 认识package.json exports字段的特性