介绍
一个由 sindresorhus (opens in a new tab) 开发的数组转换工具 arrify (opens in a new tab) , 可以将任意值转换为数组
安装
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]
}
- 如果传入参数是
null
或undefined
返回[]
- 如果传入参数是 数组,直接返回原来的参数
- 如果传入参数是
string
类型,则返回[value]
- 如果传入参数是 可迭代对象 (opens in a new tab) , 则使用 扩展运算符 (opens in a new tab) 转换为数组
一些问题
- 能否自定义一个迭代器来实现以上效果呢?
试试看:
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
类型判断,对其解构 , 偏离预期效果。.
- 可否将一个 类数组 (opens in a new tab) 转换为数组 ?
“类数组”意味着 arguments 有 长度 属性 并且属性的索引是从零开始的,但是它没有 Array 的 内置方法,例如 forEach() 和 map() 都是没有的。
翻了一下仓库 GitHub Issues (opens in a new tab)还真有人遇到了这个问题::
作者是这样回复的:
大致意思是:
特意这样限制的,如果想要达到这种效果,自己动手实现吧。
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
跑一下测试用例,结果显示未通过::
通过搜索引擎找到相关资料 Prefer the spread operator over Array.from() #120 (opens in a new tab)
看了一遍,还是不明白。之后又去了解到 Array.from()
和 [...xxx]
相比, Array.from()
性能更佳,所以为什么用 扩展运算符呢?
如果你知道,请在评论区分享你的看法和观点,非常感谢 !
看了一下提 issue 的时间是 2015 年 3 月 8 日 , 而 ES6 正式发布在 6 月 , 会不会...... ? 😂
参考资料
- (sindresorhus / eslint-plugin-unicorn) - Prefer the spread operator over Array.from() #120 (opens in a new tab)
- (sindresorhus / arrify) - Doesn't work with array-like objects (opens in a new tab)
- Convert Iterables to Array using Spread - Samantha Ming (opens in a new tab)
- 迭代器 - MDN (opens in a new tab)
- 展开语法 - MDN (opens in a new tab)
- Array.from() - MDN (opens in a new tab)
阅读更多
在 Electron 中实现下载文件实时显示进度条2021/05/08