翻译:道奇
作者:Dmitri Pavlutin
原文:5 Handy Applications of JavaScript Array.from()
任何编程语言都有一些超出基础应用的函数,这归因于成功的设计和它试图解决广泛领域的问题。
JavaScript中也有这种函数,Array.from()就是其中一个:允许在JavaScript集合上进行大量有用的转换(数组,类数组的对象,直接量(iterable)如字符串、map、set等)。
本文会介绍5种Array.from()比较有用和有趣的使用案例。
1. 快速简介
在开始前,回顾一下Array.from()是做什么的。下面代码展示了如何调用这个函数:
Array.from(arrayLikeOrIterable[, mapFunction[, thisArg]]);
第一个必填参数arrayLikeOrIterable是个类数组对象或是直接量iterable,第二个可选参数mapFunction(item, index) {...}是集合中每个项都会调用的函数,返回值会插入到新的集合中。
最后,第三个可选参数thisArg是在mapFunction被调用时作为this值使用的。
例如,给一个类数组对象的数值做乘2运算:
const someNumbers = { '0': 10, '1': 15, length: 2 };
Array.from(someNumbers, value => value * 2); // => [20, 30]
2. 将类数组转换成数组
第一个有用的技巧就是Array.from()本身的直接应用:将类数组转换成数组。
一般情况下,这个奇怪的生物 “类数组对象” 是以参数的形式存在函数内部的,或者和DOM集合结合起来进行处理。
下面这个例子是对函数的参数进行求和:
function sumArguments() {
return Array.from(arguments).reduce((sum, num) => sum + num);
}
sumArguments(1, 2, 3); // => 6
Array.from(arguments)将类数组arguments转换成一个数组。新数组是参数元素的和。
此外,可以将Array.from()应用在任何对象上,任何实现了iterable协议的原始对象,下面看几个例子:
Array.from('Hey'); // => ['H', 'e', 'y']
Array.from(new Set(['one', 'two'])); // => ['one', 'two']
const map = new Map();
map.set('one', 1)
map.set('two', 2);
Array.from(map); // => [['one', 1], ['two', 2]]
3. 克隆数组
在JavaScript中有大量克隆数组的方法。
正如所料,Array.from()可以轻松浅拷贝一个数组:
const numbers = [3, 6, 9];
const numbersCopy = Array.from(numbers);
numbers === numbersCopy; // => false
Array.from(numbers)创建了numbers数组的浅拷贝副本。numbers === numbersCopy这个等式等于false,意味着就算有相同的子项,但它们还是不同的数组对象。
有没有可能使用Array.from()创建一份包括数组所有嵌套的备份?看下面的回答:
function recursiveClone(val) {
return Array.isArray(val) ? Array.from(val, recursiveClone) : val;
}
const numbers = [[0, 1, 2], ['one', 'two', 'three']];
const numbersClone = recursiveClone(numbers);
numbersClone; // => [[0, 1, 2], ['one', 'two', 'three']]
numbers[0] === numbersClone[0] // => false
recursiveClone()创建了一份给定数组的深拷贝,这是通过在子项也是数组时循环调用recursiveClone()实现的。
4. 给数组填充值
如果需要给数组填充相同值,也可以使用Array.from()。
定义一个函数,它创建了具有相同默认值的数组:
const length = 3;
const init = 0;
const result = Array.from({ length }, () => init);
result; // => [0, 0, 0]
result数组有3个项,它们全部初始化为0。通过调用Array.from()时传入类数组对象{length}和返回初始化值的map函数来完成的。
另外也可以使用array.fill()达到同样的效果:
const length = 3;
const init = 0;
const result = Array(length).fill(init);
result; // => [0, 0, 0]
fill()方法正确地向数组填充初始化值,并且会全部填满不会有空槽。
4.1 向数组填充多个新对象
如果初始化数组的每个项都是新的对象,使用Array.from()会更好:
const length = 3;
const resultA = Array.from({ length }, () => ({}));
const resultB = Array(length).fill({});
resultA; // => [{}, {}, {}]
resultB; // => [{}, {}, {}]
resultA[0] === resultA[1]; // => false
resultB[0] === resultB[1]; // => true
由Array.from()创建的resultA,它被初始化成不同的空实例对象{},这是因为映射函数() => ({})每次调用时返回一个新对象。
但fill()创建的resultB方法是初始化成同一个空对象实例。
4.2 array.map()
用array.map()能得到同样的结果吗?下面就试一下:
const length = 3;
const init = 0;
const result = Array(length).map(() => init);
result; // => [undefined, undefined, undefined]
map()看起来不正确,预期的是三个0的数组,结果是3个空槽。
这是因为Array(length)创建了3个空槽的数组,但是map()执行的时候跳过了这些空槽。
5. 生成范围内的数值
可以使用Array.from()生成在一个范围内的数值。例如,下面的range函数生成一个数组,其中的项从0开始,直到结束- 1:
function range(end) {
return Array.from({ length: end }, (_, index) => index);
}
range(4); // => [0, 1, 2, 3]
在range()函数内部,Array.from()提供了类数组{ length: end },映射函数就简单的返回当前的索引,这种方式可以生成范围内的值。
6. 数组项去重
Array.from()可以接受直接量类型(iterable)对象,这使得它可以快速移除数组中的重复项,这种操作可以通过set数据结构实现:
unction unique(array) {
return Array.from(new Set(array));
}
unique([1, 1, 2, 3, 3]); // => [1, 2, 3]
首先,new Set(array)创建了一个包含所有数组项的集合,set内部会自动移除重复项。
因为set是直接量类型(iterable)的,Array.from()将唯一项抽到新的数组上。
7. 总结
Array.from()静态方法接受类数组对象或直接量类型(iterable)对象,还有一个映射函数。此外,该函数不会跳过空的直接量类型(iterable)对象。这些特性的组合给了Array.from()很多可能性。
正如上面介绍的,可以很简单的将类数组对象转换成数组,克隆数组,为数组填充初始值,生成范围内的值,移除重复项。
确实,Array.from()是良好的设计和灵活性的结合,允许广泛集合的转换。