持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第1天,点击查看活动详情
介绍
一个由 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];
}
- 如果传入参数是
null
或undefined
返回[]
- 如果传入参数是 数组, 直接返回原来的参数
- 如果传入参数是
string
类型, 则返回[value]
- 如果传入参数是 可迭代对象 , 则使用 扩展运算符 转换为数组
一些问题
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 还真有人遇到了这个问题:
作者是这样回复的:
大致意思是:
特意这样限制的,如果想要达到这种效果,自己动手实现吧.
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
看了一遍, 还是不明白. 之后又去了解到
Array.from()
和 [...xxx]
相比, Array.from()
性能更佳 , 所以为什么用 扩展运算符呢?
如果你知道, 请在评论区分享你的看法和观点 , 非常感谢 !
看了一下 提 issue 的时间是 2015 年 3月 8 日 , 而 ES6 正式发布在 6 月 , 会不会...... ? 😂