前言
本文参加了由公众号@若川视野 发起的每周源码共读活动,点击了解详情一起参与。
arrify是什么
arrify是将传入的值转化为数组,如果只是想接受单个或多个值,可以使用[singleValurOrArray].flat()
代替
arrify如何使用
npm install arrify
import arrify from 'arrify';
arrify('🦄');
//=> ['🦄']
arrify(['🦄']);
//=> ['🦄']
arrify(new Set(['🦄']));
//=> ['🦄']
arrify(null);
//=> []
arrify(undefined);
//=> []
自己尝试去实现
看完arrify的功能后,感觉也挺简单的,就如同它的定义,只要在函数内做好各种数据类型的校验即可,自己尝试动手去写,后来发现我能想到的场景也只有常见的基本类型和引用类型,es6新增的Map、Set没能考虑进去...
后来在翻看源码的时候,发现源码里有一个很精妙的判断typeof value[Symbol.iterator] === 'function'
,非常巧妙的解决了更多类型的校验,另外单独把string
类型单独拿出来做了判断,心里就很好奇为啥是string,而不是number、boolean等,这个也触及到了自己的知识盲区。
arrify源码解读
export default function arrify(value) {
// 如果value为null、undefined,返回[]
if (value === null || value === undefined) {
return [];
}
// 如果是数组,直接返回value
if (Array.isArray(value)) {
return value;
}
// 如果是字符串,返回[value]
// 单独校验string是因为其原型上有迭代器,是返回每个字符
if (typeof value === 'string') {
return [value];
}
// 如果value的原型上有迭代器,则使用...解构其值
if (typeof value[Symbol.iterator] === 'function') {
return [...value];
}
return [value];
}
Symbol.iterator
Symbol.iterator 为每一个对象定义了默认的迭代器。该迭代器可以被for...of
循环使用。
定义
JavaScript原有的表示“集合”的数据结构,主要是数组(Array)和对象(Object),ES6 又添加了Map和Set。这样就有了四种数据集合,用户还可以组合使用它们,定义自己的数据结构,比如数组的成员是Map,Map的成员是对象。这样就需要一种统一的接口机制,来处理所有不同的数据结构。
遍历器(Iterator)就是这样一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。
Iterator 的作用有三个:一是为各种数据结构,提供一个统一的、简便的访问接口;二是使得数据结构的成员能够按某种次序排列;三是 ES6 创造了一种新的遍历命令for...of循环,Iterator 接口主要供for...of使用。
内部数据构成
在JavaScript中,迭代器是能调用next
方法实现迭代的一个对象,该方法返回一个具有两个属性的对象。
value
:可迭代对象的下一个值done
:表示是否已经取出所有的数据了。false
表示还有数据,true
表示后面已经没有数据了。
手写迭代器
在写迭代器之前,我们先来看看数据的迭代器是什么样子
我们可以看到,数组的迭代器是其原型上的一个可执行函数。我们执行一下这个方法,看看执行器返回的结果
迭代器的执行结果是一个Array Iterator
实例对象,其原型上有个next
方法,我们再执行一下这个方法,看看next的执行结果是啥
我们可以看到next
返回了数组每一项的值value
和是否有下一项的标识done
知道了迭代器的原理和返回值,那我们再实现其原理就会得心应手了
class MyArrayIterator {
constructor (arr) {
this.arr = arr;
this.index = 0;
}
next() {
var length = this.arr.length;
if(this.index < length) {
return { value: this.arr[this.index++], done: false }
} else {
return {value: undefined, done: true }
}
}
}
Array.prototype[Symbol.iterator] = function () {
return new MyArrayIterator(this)
}
var arr = ['a', 'b', 'c'];
var myArrIterator = arr[Symbol.iterator]();
myArrIterator.next(); // { value: 'a', done: false }
myArrIterator.next(); // { value: 'b', done: false }
myArrIterator.next(); // { value: 'c', done: false }
myArrIterator.next(); // { value: undefined, done: true }
目前,原生JavaScript支持Iterator的数据结构如下
- Array
- Map
- Set
- String
- TypedArray
- 函数的arguments对象
- NodeList对象
使用场景
- 解构赋值
对数组和Set结构进行解构赋值时,会默认调用Symbo.irerator方法
- 扩展运算符
-
其他场景
- for...of
- Array.form()
- Map(),Set(),WeakMap(),WeakSet()
- Promise.all()
- promise.race()
我们都知道,Object并不支持for..of来遍历对象的每个属性值,我们可以给对象的原型对象设置迭代器来实现,感兴趣的可以尝试一下。
参考:
blog.csdn.net/chilanzi/ar… juejin.cn/post/716467… juejin.cn/post/716130… blog.csdn.net/qq_19901795…