本文参加了由公众号@若川视野发起的每周源码共读活动,点击了解详情一起参与。
这是源码共读的第33期,链接:juejin.cn/post/710021…。
源码
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];
}
console.log(arrify('foo')); // ['foo']
console.log(arrify(['foo'])); // ['foo']
console.log(arrify(null)); // []
console.log(arrify(undefined)); // []
console.log(arrify(1)); // [1]
console.log(arrify(Symbol('foo'))); // [Symbol(foo)]
console.log(arrify(new Set(['foo', 'bar']))); // ['foo', 'bar']
console.log(arrify({foo: true})); // [{foo: true}]
console.log(arrify(/foo/)); // [/foo/]
console.log(arrify(new Date())); // [Date]
console.log(arrify(new Promise(resolve => resolve('foo')))); // [Promise]
归纳
Symbol.iterator
说到 Symbol.iterator 不得不提及 JavaScript 遍历器 (Iterator)。
遍历器 (Iterator)是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构,只要部署了 Iterator 接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。
ES6 的有些数据结构原生具备 Iterator 接口,即不用任何处理就可以被 for...of 循环遍历。
-
Array -
Map -
Set -
String -
TypeArray -
函数的
arguments对象 -
NodeList对象
除此之外,其他数据结构(比如:对象)的 Iterator 接口都需要自己在 Symbol.iterator 属性上面部署,这样才会被 for...of 循环遍历。
const obj = Object({name: '张三', age: 23})
obj[Symbol.iterator] = function* () {
for (const key in this) {
yield [key, this[key]]
}
}
for (let item of obj) {
console.log('item', item)
}
调用 Iterator 接口的场合
- 解构赋值:对
Array和Set结构进行解构赋值时,会默认调用Symbol.iterator方法。
const set = new Set([1, 2, 3, 4, 5]);
const [first, ...rest] = set
const array = [1, 2, 3, 4, 5];
const [first, ...rest] = array
- 扩展运算符
...也会调用默认的Iterator接口
const arr1 = [1, 2, 3, 4, 5];
const arr2 = [-1, ...arr1, 6, 7, 8];
只要某个数据结构部署了 Iterator 接口,就可以对他使用扩展运算符将其转为数组。
const obj = Object({name: '张三', age: 23})
obj[Symbol.iterator] = function* () {
for (const key in this) {
yield [key, this[key]]
}
}
const arr = [...obj] // [ [ 'name', '张三' ], [ 'age', 23 ] ]
yield*
yield* 后面是一个可遍历的结构,它会调用该结构的 Iterator 接口。
const generator = function * () {
yield 1;
yield 2;
yield* [3, 4];
}
for (const item of generator()) {
console.log(item);
} // 1 2 3 4