arrify 一个很实用的工具函数

LeonMay 26, 2022| #JavaScript

介绍

一个由 sindresorhus 开发的数组转换工具 arrify , 可以将任意值转换为数组

安装

npm install arrify

使用

import arrify from 'arrify'

// string
arrify('🦄')
// => ['🦄']

// array
arrify(['🦄'])
// => ['🦄']

// iterable objects
arrify(new Set(['🦄']))
// => ['🦄']

// null
arrify(null)
// => []

// null
arrify(undefined)
// => []

源码

类型声明

export default function arrify<ValueType>(
 value: ValueType
): ValueType extends (null | undefined)
 ? [] // eslint-disable-line  @typescript-eslint/ban-types
 : ValueType extends string
  ? [string]
  : ValueType extends readonly unknown[]
   ? ValueType
   : ValueType extends Iterable<infer T>
    ? T[]
    : [ValueType];

核心源码

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];
}
  1. 如果传入参数是 nullundefined 返回 []
  2. 如果传入参数是 数组, 直接返回原来的参数
  3. 如果传入参数是 string类型, 则返回 [value]
  4. 如果传入参数是 可迭代对象 , 则使用 扩展运算符 转换为数组

一些问题

1. 能否自定义一个迭代器来实现以上效果呢?

试试看:

var myIterable = {}
myIterable[Symbol.iterator] = function* () {
    yield 1;
    yield 2;
    yield 3;
};

arrify(myIterable)
// output: [1, 2, 3]

完全可以的哈😁

2.能否调换这些逻辑判断的顺序?

试试看:

export default function arrify(value) {
 if (value === null || value === undefined) {
  return [];
 }

 if (Array.isArray(value)) {
  return value;
 }
  
  // 判断上移
 if (typeof value[Symbol.iterator] === 'function') {
  return [...value];
 }
  
  // 判断下移
 if (typeof value === 'string') {
  return [value];
 }

 return [value];
}

arrify('foo')
// output: [ 'f', 'o', 'o' ]
// expect: [ 'foo' ]

结果显示, 上述代码不能达到预期效果, 查阅到相关文档:

String、Array、TypedArray、Map 和 Set 都是内置可迭代对象,因为它们的原型对象都拥有一个 Symbol.iterator 方法。

如果上移则提前进入 Symbol.iterator 类型判断, 对其解构 , 偏离预期效果.

3. 可否将一个 类数组 转换为数组 ?

“类数组” 意味着 arguments 有 长度 属性 并且属性的索引是从零开始的, 但是它没有 Array的 内置方法, 例如 forEach() 和 map()都是没有的.

翻了一下仓库 GitHub Issues 还真有人遇到了这个问题:

image.png

作者是这样回复的:

image.png

大致意思是:

特意这样限制的,如果想要达到这种效果,自己动手实现吧.

Just do it ~

export default function arrify(value) {
 //...

 if (typeof value.length != 'number') return [value]

 const arr = []
 
  for (var i = 0; i < value.length; i++) {
    if (Object.prototype.hasOwnProperty.call(value, i) || i in value) {
      arr.push(value[i])
    }
  }

  if (!arr.length) return []

 return arr

}

const pseudoArray = {
 0:'a',
 1:'b',
 length:2
}

arrify(pseudoArray)
// output -> [ 'a', 'b' ]

疑惑

在判断传入参数是否为 可迭代对象的时候, 可否将 [...value] 替换为 Array.from(value) ?

 if (typeof value[Symbol.iterator] === 'function') {
  return [...value];
    // replace ->  return Array.from(value) ?
 }

我尝试替换为 Array.from(value) , 运行 npm run test 跑一下测试用例, 报错了: image.png 通过搜索引擎找到相关资料 Prefer the spread operator over Array.from() #120image.png 看了一遍, 还是不明白. 之后又去了解到 Array.from()[...xxx]相比, Array.from() 性能更佳 , 所以为什么用 扩展运算符呢?

如果你知道, 请在评论区分享你的看法和观点 , 非常感谢 !

看了一下 提 issue 的时间是 2015 年 3月 8 日 , 而 ES6 正式发布在 6 月 , 会不会… ? 😂

参考资料

  1. https://github.com/sindresorhus/arrify

  2. (sindresorhus / eslint-plugin-unicorn) - Prefer the spread operator over Array.from() #120

  3. (sindresorhus / arrify) - Doesn’t work with array-like objects

  4. Convert Iterables to Array using Spread - Samantha Ming

  5. 迭代器 - MDN

  6. 展开语法 - MDN

  7. Array.from() - MDN