ES6
(ECMAScript 2015
)是 JavaScript
语言的一个重要版本,为编写更加简洁、便捷和可读性更高的代码提供了很多新的特性和 API。想了解ES6所有新增API,可以跳转至我的另一篇博客:JS语法 ES6、ES7、ES8、ES9、ES10、ES11、ES12新特性
其中数组相关的 API 也在 ES6 中得到了大大的增强,下面我来详细介绍一下 ES6 中数组的新特性和 API。
Array.of()
方法创建一个新的数组,根据传入参数创建数组元素。
举个例子,可以创建一个包含三个元素的数组,如下所示:
const arr = Array.of(1, 2, 3);
console.log(arr); // output: [1, 2, 3]
在这个示例中,我们使用 Array.of() 方法创建了一个包含三个整数的数组,并将其分配给变量 arr。请注意,由于我们传递了三个参数,因此创建的数组包含三个元素。
Array.of()
方法的主要优点是它可以创建一个指定长度的数组,而不必使用 Array() 方法的特殊语法。例如,如果要创建一个包含五个元素的数组,可以如下所示:
const arr = Array.of(5);
console.log(arr); // output: [5]
在此示例中,我们传递了一个整数 5,这将创建一个包含一个元素的数组,该元素的值为 5。请注意,这与使用 Array() 方法的创建方式不同:
const arr = new Array(5);
console.log(arr); // output: [ , , , , ]
在此示例中,我们创建了一个包含五个元素的数组,并将其分配给变量 arr。请注意,在创建时,这个数组没有给出任何元素值。这与使用Array.of()方法不同,它使用任意数量的参数来初始化一个数组,因此如果要设置值,可以采用以下方式自动列表形式:
const arr = Array.of(1, 2, 3, 4, 5);
console.log(arr); // output: [1, 2, 3, 4, 5]
另外,要注意 Array.of() 方法在创建包含一个字符串元素时的用法。在这种情况下,字符串会被视为单个元素,而不是将字符串拆分为单个字符。
const arr = Array.of("hello");
console.log(arr); // output: ["hello"]
总之,
Array.of()
方法提供了一种简单的方式来创建自定义大小的数组并初始化其中的元素。
Array.from()
方法从类似数组或可迭代对象(如 Set 或 Map)创建一个新的数组实例。该方法还支持隐式映射和过滤器参数。
例如,要从 Set 对象 mySet
中创建一个数组,可以使用以下方式:
const mySet = new Set([1, 2, 3]);
const arr = Array.from(mySet);
console.log(arr); // [1, 2, 3]
可以利用第二个参数完成隐式映射,接收一个函数用来映射元素:
const arr = Array.from([1, 2, 3], x => x * 2);
console.log(arr); // [2, 4, 6]
Array.prototype.fill()
方法会将数组中的所有元素替换为指定的值。它接受三个参数:value
(必填),start
(可选)和end
(可选)。
value
参数为填充数组元素使用的值。start
参数表示开始填充的位置,如果不指定,默认为0。end
参数表示填充的结束位置,但不包括结束位置本身。如果未传递 end 参数,则 default 为数组末尾。举个例子,我们可以使用 fill() 方法将数组中所有的元素设置为相同的值:
// 创建一个5个元素的空数组
const arr = new Array(5);
// 用数字0填充数组
arr.fill(0);
console.log(arr); // output: [0, 0, 0, 0, 0]
如上所示, 我们首先创建一个包含5个元素的空数组,并将其分配给变量 arr。接下来,我们使用 fill()
方法将数组中的所有元素设置为数字 0。
下面的例子演示了使用 start 和 end 参数来填充数组中的一个特定子集:
// 创建一个5个元素的空数组
const arr = new Array(5);
// 用数字1来填充数组的第2个到第4个元素
arr.fill(1, 1, 4);
console.log(arr); // output: [ , 1, 1, 1, ]
如上所示,我们首先初始化了一个包含5个元素的空数组,并将其分配给变量 arr。接下来,我们使用 fill()
方法将数组的第2
,第3
和第4
个元素设置为数字 1
,同时指定了 start 参数为 1(表示第二个元素)和 end 参数为 4(表示第五个元素的索引)。
值得一提的是,
fill()
方法操作的是原数组
,并返回修改后的数组,因此不需要创建新数组。- 如果需要创建一个新的数组,则可以使用
Array.from()
方法和fill()
方法的结合:
const n = 5;
const arr = Array.from({length: n}, () => 0);
console.log(arr); // output: [0, 0, 0, 0, 0]
在上面的示例中,我们使用 Array.from() 方法和一个箭头函数生成一个长度为 n 的数组,并用 0 进行填充,获得一个新的包含 n 个元素的数组。
总之,
Array.fill()
方法提供了一种快速设置大量数组的元素值的方式,它可以轻松地操作原始数组。
Array.copyWithin()
方法用给定数组中的一段内容向另一位置拷贝,用原数组中的值加以覆盖。它接受三个参数:target、start 和 end。
target
表示拷贝到该位置的下标索引,
start
和 end
表示需要拷贝的部分的开始和结束位置(start 位置的元素会包括在拷贝的部分中,end 位置的元素不包括其中)。
如果有需要,方法会在进行拷贝操作前将数组缩小或扩大为所需大小。
下面的例子演示了如何使用 copyWithin()
方法:
const arr = [1, 2, 3, 4, 5];
arr.copyWithin(0, 3, 4);
console.log(arr); // output: [4, 2, 3, 4, 5]
如上所示,我们首先定义了一个包含 5 个整数的数组,并将其分配给变量 arr
。然后,我们使用 arr.copyWithin()
方法,将 从数组下标 3
开始的一项 拷贝到 数组下标 0
,覆盖原始的前三项
。 这个 copyWithin() 方法会在原数组
上操作,这也是为什么拷贝的值从原数组的第一个位置开始的原因。
下面我们来详细说明一下这个方法的参数:
target
: 必填参数。从该位置开始替换数据。如果是负数,则表示从 array.length + target 开始替换(即从数组末尾倒着数的位置)。start
: 必填参数。从该位置开始读取数据,默认是0。如果是负数,则表示从 array.length + start 开始读取(即从数组末尾倒着数的位置)。end
: 可选参数。到该位置前停止读取数据,默认是array.length。如果是负数,则表示从 array.length + end 开始停止读取(即从数组末尾倒着数的位置)。需要注意的是:
target
、start
和end
参数都必须是整数,不然会自动取整。- 该方法无法使用于
const
声明的数组,因为它会修改原数组。
总之,
Array.copyWithin()
方法提供了一种快速交换、移动和复制数组元素的方式。
Array.prototype.find()
方法用于查找符合条件的第一个数组元素,将该元素返回。类似地,Array.prototype.findIndex()
方法也可以查找符合条件的第一个元素,并将该元素的索引返回。如果找不到符合条件的元素,则返回 undefined
。例如,查找数组 [1,2,3,4,5]
中第一个大于 3 的数,可以使用以下方式:
const arr = [1,2,3,4,5];
const index = arr.find(item => item > 3);
const index2 = arr.findIndex(item => item > 3);
console.log(index); // 4
console.log(index2); // 3
Array.prototype.some()
方法检查数组中是否有一个或多个元素符合条件。该方法会返回布尔值 true 或 false。-Array.prototype.every()
方法则检查数组中的所有元素是否都符合条件。如果符合条件,则返回 true,否则返回 false。例如,检查数组 [2, 3, 4, 5]
中是否存在一个大于 3 的数,可以使用以下方式:
const arr = [2,3,4,5];
const hasGreaterThanThree = arr.some(item => item > 3);
const allGreaterThanOne = arr.every(item => item > 1);
console.log(hasGreaterThanThree); // true
console.log(allGreaterThanOne); // true
Array.prototype.flat()
方法用于递归地展开一个数组,将嵌套的数组变成一个一维的数组。该方法的默认展开深度为1
,可以接受一个可选的整数参数,表示展开的深度。如果参数为 Infinity
,则递归将一直执行,直到展开为一维数组。
例如,将嵌套数组 [[1,2,3],[4,5]]
展开为一维数组,可以使用以下方式:
const arr = [[1,2,3],[4,5]];
const flattened = arr.flat();
console.log(flattened); // [1, 2, 3, 4, 5]
如果已经知道要展开多少层,可以将数值作为参数传递给
flat
方法:
例如,将嵌套数组 [[1,[2,3]],[4,[5,6]]]
展开为一维数组:
const arr = [[1,[2,3]],[4,[5,6]]];
const flattenedOnce = arr.flat(1);
console.log(flattenedOnce); // [1, [2, 3], 4, [5, 6]]
const flattenedTwice = arr.flat(2);
console.log(flattenedTwice); // [1, 2, 3, 4, 5, 6]
需要注意的是,
flat()
方法不改变原数组,而是返回一个新的数组,适用于不会改变原有数据的语境中。如果原数组中包含null
或者undefined
,它们会被自动跳过。
还有需要注意的地方是,
flat()
方法虽然方便,但也可能会产生性能问题和逻辑问题。对于嵌套非常深的数组,如果直接使用flat()
方法,会丧失代码的可读性和可维护性。如果有嵌套不确定深度的数组,最好使用递归的方法来展开数组,或使用第三方库,可以避免展开时出现的性能和逻辑问题。
Array.prototype.flatMap()
方法是在ES2019标准下引入的一个新的数组方法,它结合了 Array.prototype.map()
和 Array.prototype.flat()
两个方法的功能。flatMap()
方法可以将数组按照一定的规则“展平”为一个新的数组,与 flat() 方法很相似,但又比 flat()
更加灵活。
flatMap()
方法首先使用 map()
方法根据指定规则进行对数组元素进行操作,然后将处理后的数组“拉平”为一个一维数组。与 map() 和 flat() 方法不同之处在于,flatMap() 方法将这两个操作结合在了一起,避免了使用 map() 和 flat() 时需要写额外的代码。
例如,我们有一个数组,要将每个元素乘以2,然后取出其中偶数值,可以使用以下方式:
const arr = [1, 2, 3, 4];
const result = arr.flatMap(x => x * 2).filter(x => x % 2 === 0);
console.log(result); // [4, 8]
在上述代码中,我们首先使用 map()
方法将数组中每个元素乘以2
,然后使用 flat()
方法将结果展开为一维数组,并使用 filter()
方法过滤出偶数值。
需要注意的是,
flatMap()
方法会跳过值为null
或者undefined
的数组元素,在无需考虑这两种元素的情况下,可以得到更加清晰和简洁的代码。flatMap()
方法在在一些场合下可能会有越界异常的问题
产生,所以在使用时需要适当注意范围越界等问题。
ES6中将三个迭代方法添加到数组的原型中:keys()
、entries()
和values()
。
Array.prototype.keys()
方法返回一个迭代器,该迭代器包含数组中的每个元素的索引。
const arr = ['a', 'b', 'c'];
const iterator = arr.keys();
console.log(iterator.next().value); // 0
console.log(iterator.next().value); // 1
console.log(iterator.next().value); // 2
与之相似,Array.prototype.entries()
方法返回一个包含数组中每个元素的键/值对的迭代器。
const arr = ['a', 'b', 'c'];
const iterator = arr.entries();
console.log(iterator.next().value); // [0, 'a']
console.log(iterator.next().value); // [1, 'b']
console.log(iterator.next().value); // [2, 'c']
Array.prototype.values()
返回一个包含数组中每个元素的值的迭代器。
const arr = ['a', 'b', 'c'];
const iterator = arr.values();
console.log(iterator.next().value); // 'a'
console.log(iterator.next().value); // 'b'
console.log(iterator.next().value); // 'c'
Array.prototype.reduceRight()
方法与reduce()
方法非常相似,但是遍历的顺序是从后往前的。
const arr = ['a', 'b', 'c'];
const result = arr.reduceRight((prev, cur) => prev + cur);
console.log(result); // 'cba'
Array.prototype.sort()
方法用于对数组进行排序,可以传递一个函数以根据一些基准进行排序。在ES6之前,排序从左到右进行,这可能会影响排序的结果。在ES6中,Array.prototype.sort()
方法默认使用稳定的排序算法,并按照Unicode
字符顺序进行排序。
const arr = [1,4,3,8,5,2];
arr.sort((a,b) => a-b);
console.log(arr); // [1, 2, 3, 4, 5, 8]
Array.prototype.includes()
方法用于判断一个数组是否包含某个元素,如果包含则返回 true
,否则返回 false
。与 Array.prototype.indexOf()
方法不同的是,includes()
方法支持检查 NaN
的存在。
例如,判断数组 [1, 2, 3]
是否包含元素 2
和 4
,可以使用以下方式:
const arr = [1, 2, 3];
console.log(arr.includes(2)); // true
console.log(arr.includes(4)); // false
Array.prototype.findIndex()
方法返回数组中第一个符合条件的元素的索引,如果没有符合条件的元素,则返回 -1
。
例如,查找数组 [1, 2, 3, 4, 5]
中第一个大于 3
的元素的索引,可以使用以下方式:
const arr = [1, 2, 3, 4, 5];
const index = arr.findIndex(item => item > 3);
console.log(index); // 3
ES6 中引入了数组解构语法,可以通过将数组的元素分配给变量来快速访问和使用这些元素。
例如,要将数组 [1, 2, 3]
中的元素分别赋值给变量 x
、y
和 z
,可以使用以下方式:
const [x, y, z] = [1, 2, 3];
console.log(x); // 1
console.log(y); // 2
console.log(z); // 3
在此基础上,我们还可以使用扩展运算符来获取数组中的剩余元素:
const [x, ...rest] = [1, 2, 3];
console.log(x); // 1
console.log(rest); // [2, 3]
ES6 中的 Array.from()
和 Array.of()
方法都被用来创建新数组,但它们的行为略有不同。
Array.of()
方法可以根据传入的参数创建一个新的数组实例,并且会将传入的参数作为元素添加到新的数组中:
const arr = Array.of(1, 2, 3);
console.log(arr); // [1, 2, 3]
Array.from()
方法则可以将类似数组或可迭代对象转换成数组实例。该方法还接受一个可选的 map() 函数,可以使用该函数对每个元素进行转换:
const arr1 = Array.from('foo');
console.log(arr1); // ['f', 'o', 'o']
const arr2 = Array.from([1, 2, 3], x => x * 2);
console.log(arr2); // [2, 4, 6]
// push() 方法示例
const arr1 = [1, 2, 3];
arr1.push(4, 5); // 在数组尾部插入多个元素
console.log(arr1); // [1, 2, 3, 4, 5]
// pop() 方法示例
const arr2 = [1, 2, 3];
const last = arr2.pop(); // 弹出数组末尾元素
console.log(last); // 3
console.log(arr2); // [1, 2]
// shift() 方法示例
const arr3 = [1, 2, 3];
const first = arr3.shift(); // 弹出数组头部元素
console.log(first); // 1
console.log(arr3); // [2, 3]
// unshift() 方法示例
const arr4 = [1, 2, 3];
arr4.unshift(-1, 0); // 在数组头部插入多个元素
console.log(arr4); // [-1, 0, 1, 2, 3]
通过传入多个参数,数组方法 push()
、unshift()
可以一次性向数组尾部或头部添加多个元素。相比于多次使用 push() 或 unshift() 方法,这种方式更加高效、简洁。
ES6 中数组的 map()
、reduce()
和 filter()
方法都支持链式调用,能够让我们更加优雅地操作数组。
例如下面的代码就使用 reduce()
和 filter()
方法链式调用,将数组中所有大于 2 的元素相乘:
const arr = [1, 2, 3, 4];
const result = arr.filter(item => item > 2)
.reduce((prev, curr) => prev * curr, 1);
console.log(result); // 12
上面的代码中,我们首先使用 filter()
方法筛选出大于 2 的元素,然后将这些元素使用 reduce()
方法相乘,最终得到了结果 12。链式调用将减少了不必要的中间变量,代码更加简洁高效。
ES6 中新增了一个用于创建派生类的属性 @@species
,它是一个函数对象,用于创建派生类的构造函数。在派生类中,如果重写了 Symbol.species
属性,则它将使用重写后的 @@species
属性来创建新实例。
下面的示例代码中,我们定义了一个名为 MyArray
的派生类,它继承自 Array 类,并重写了 Symbol.species
属性:
class MyArray extends Array {
static get [Symbol.species]() { return Array; }
}
const arr1 = new MyArray(1, 2, 3);
const arr2 = arr1.map(x => x * x);
console.log(arr1 instanceof MyArray); // true
console.log(arr2 instanceof MyArray); // false
console.log(arr2 instanceof Array); // true
上面的代码中,MyArray
类重写了 Symbol.species
属性,将返回 Array
,因此在调用 map()
方法后返回的新数组实例就是 Array 类型,而非 MyArray 类型。
ES6 中的数组 API 带来了很多变化,让我们操作数组的效率更高,代码变得更简洁。在日常开发过程中,可以多加使用这些 API,提高代码的工作效率和开发质量。