arrify 转数组

55 阅读2分钟

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

这是源码共读的第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 接口的场合

  • 解构赋值:对 ArraySet 结构进行解构赋值时,会默认调用 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