创建数组
在 ES6
之前创建数组主要存在两种方式:Array
构造器与数组字面量写法。这两种方式都需要将数组的项分别列出,并且还要受到其他限制。将 “类数组对象”(即:拥有数值类型索引与长度属性的对象)转换为数组也并不自由,经常需要书写额外的代码。为了使数组更易创建,ES6
新增了 Array.of()
与 Array.from()
方法。
Array.of() 方法
ES6
为数组新增创建方法的目的之一,是帮助开发者在使用 Array
构造器时避开 JS
语言的一个怪异点。调用 new Array()
构造器时,根据传入参数的类型与数量的不同,实际上会导致一些不同的结果,例如:
let items = new Array(2);
console.log(items.length); // 2
console.log(items[0]); // undefined
console.log(items[1]); // undefined
items = new Array("2");
console.log(items.length); // 1
console.log(items[0]); // "2"
items = new Array(1, 2);
console.log(items.length); // 2
console.log(items[0]); // 1
console.log(items[1]); // 2
items = new Array(3, "2");
console.log(items.length); // 2
console.log(items[0]); // 3
console.log(items[1]); // "2"
当使用单个数值参数来调用 Array
构造器时,数组的长度属性会被设置为该参数;而如果使用单个的非数值型参数来调用,该参数就会成为目标数组的唯一项;如果使用多个参数(无论是否为数值类型)来调用,这些参数也会成为目标数组的项。数组的这种行为既混乱又有风险,因为有时可能不会留意所传参数的类型。
ES6
引入了 Array.of()
方法来解决这个问题。该方法的作用非常类似 Array
构造器,但在使用单个数值参数的时候并不会导致特殊结果。Array.of()
方法总会创建一个包含所有传入参数的数组,而不管参数的数量与类型。下面几个例子演示了 Array.of()
的用法:
let items = Array.of(1, 2);
console.log(items.length); // 2
console.log(items[0]); // 1
console.log(items[1]); // 2
items = Array.of(2);
console.log(items.length); // 1
console.log(items[0]); // 2
items = Array.of("2");
console.log(items.length); // 1
console.log(items[0]); // "2"
在使用 Array.of()
方法创建数组时,只需将想要包含在数组内的值作为参数传入。第一个例子创建了一个包含两个项的数组,第二个数组只包含了单个数值项,而最后一个数组则包含了单个字符串项。这个结果类似于使用数组字面量写法,通常你都可以在原生数组上使用字面量写法来代替 Array.of()
,但若想向函数传递参数,使用 Array.of()
而非 Array
构造器能够确保行为一致。例如:
function createArray(arrayCreator, value) {
return arrayCreator(value);
}
let items = createArray(Array.of, value);
此代码中的 createArray()
函数接受两个参数:一个数组创建器与一个值,并会将后者插入到目标数组中。你应当向 createArray()
函数传递 Array.of()
作为第一个参数来创建新数组;相反,若传递 Array
构造器则会有危险,因为你无法保证第二个参数不是数值类型。
|
Array.from() 方法
在 JS
中将非数组对象转换为真正的数组总是很麻烦。例如,若想将类数组的 arguments
对象当做数组来使用,那么你首先需要对其进行转换。在 ES5
中,进行这种转换需要编写一个函数,类似下面这样:
function makeArray(arrayLike) {
var result = [];
for (var i = 0, len = arrayLike.length; i < len; i++) {
result.push(arrayLike[i]);
}
return result;
}
function doSomething() {
var args = makeArray(arguments);
// use args
}
该方式手动创建了一个 result
数组,并将 arguments
对象的所有项复制到该数组中。这种方式虽然有效,却为一个简单操作书写了过多的代码。开发者最终发现他们可以调用数组原生的 slice()
方法来减少代码量,就像这样:
function makeArray(arrayLike) {
return Array.prototype.slice.call(arrayLike);
}
function doSomething() {
var args = makeArray(arguments);
// use args
}
这段代码的功能与前一段代码等效。它能正常工作是因为将 slice()
方法的 this
设置为类数组对象,slice()
只需要有数值类型的索引与长度属性就能正常工作,而类数组对象能满足这些要求。
尽管这种技巧所用的代码量更少,但调用 Array.prototype.slice.call(arrayLike)
并没有明确体现出 “要将类数组对象转换为数组” 的目的。幸运的是,ES6
新增了 Array.from()
方法来提供一种明确清晰的方式以解决这方面的需求。
将可迭代对象或者类数组对象作为第一个参数传入,Array.from()
就能返回一个数组。这里有个简单的例子:
function doSomething() {
var args = Array.from(arguments);
// use args
}
此处调用 Array.from()
方法,使用 arguments
对象创建了一个新数组 args
,它是一个数组实例,并且包含了 arguments
对象的所有项,同时还保持了项的顺序。
|
映射转换
如果你想实行进一步的数组转换,你可以向 Array.from()
方法传递一个映射用的函数作为第二个参数。此函数会将类数组对象的每一个值转换为目标形式,并将其存储在目标数组的对应位置上。例如:
function translate() {
return Array.from(arguments, (value) => value + 1);
}
let numbers = translate(1, 2, 3);
console.log(numbers); // 2,3,4
此代码将 (value) => value + 1
作为映射函数传递给了 Array.from()
方法,对每个项进行了一次 +1
处理。如果映射函数需要在对象上工作,你可以手动传递第三个参数给 Array.from()
方法,从而指定映射函数内部的 this
值。
let helper = {
diff: 1,
add(value) {
return value + this.diff;
}
};
function translate() {
return Array.from(arguments, helper.add, helper);
}
let numbers = translate(1, 2, 3);
console.log(numbers); // 2,3,4
这个例子使用了 helper.add()
作为映射函数。由于该函数使用了 this.diff
属性,你必须向 Array.from()
方法传递第三个参数用于指定 this
。借助这个参数,Array.from()
就可以方便地进行数据转换,而无须调用 bind()
方法、 或用其他方式去指定 this
值。
在可迭代对象上使用
Array.from()
方法不仅可用于类数组对象,也可用于可迭代对象,这意味着该方法可以将任意包含 Symbol.iterator
属性的对象转换为数组。例如:
let numbers = {
*[Symbol.iterator]() {
yield 1;
yield 2;
yield 3;
}
};
let numbers2 = Array.from(numbers, (value) => value + 1);
console.log(numbers2); // 2,3,4
由于代码中的 numbers
对象是一个可迭代对象,你可以把它直接传递给 Array.from()
方法,从而将它包含的值转换为数组。映射函数对每个数都进行了 +1
处理,因此目标数组的内容就是 2、3、4,而不是 1、2、3。
如果一个对象既是类数组对象,又是可迭代对象,那么迭代器就会使用 |