slice 方法可以在不修改原始列表的情况下,创建列表子集的浅拷贝。
注意: splice 方法会修改原始数组。
一、slice 工作原理
如 MDN 文档,slice
是数组上的一个方法,它最多有两个参数:
arr.slice([begin[, end]])
begin
从该索引处开始提取原数组中的元素,如果该参数为负数,则表示从原数组中的倒数第几个元素开始提取,slice(-2)
表示提取原数组中的倒数第二个元素到最后一个元素(包含最后一个元素)。
如果省略 begin
,则 slice
从索引 0 开始。
end
在该索引处结束提取原数组元素(从0开始)。slice
会提取原数组中索引从 begin
到 end
的所有元素(包含begin,但不包含end)。
slice(1,4)
提取原数组中的第二个元素开始直到第四个元素的所有元素 (索引为 1, 2, 3的元素)。
如果该参数为负数, 则它表示在原数组中的倒数第几个元素结束抽取。 slice(-2,-1)
表示抽取了原数组中的倒数第二个元素到最后一个元素(不包含最后一个元素,也就是只有倒数第二个元素)。
如果 end
被省略,则slice
会一直提取到原数组末尾。如果 end
大于数组长度,slice
也会一直提取到原数组末尾。
二、基本用法
1.复制
没有任何参数的 slice 执行一个简单的浅拷贝,或者使用展开运算符来实现。
const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9]
const arr2 = arr.slice() // [1, 2, 3, 4, 5, 6, 7, 8, 9]
2.获取从 N 开始的子数组
使用 slice 方法:原始数组从 N 开始抽取的所有元素。不修改原始数组的情况下,返回除第一个以外的剩余的数组,代码如下:
function useOne (arr) {
return arr.slice(1)
}
3.获取从末尾 N 开始的子数组
利用 负索引 从末尾开始计数,从而获取数组的末尾。这种 负索引 使得删除任意数量的元素变得超级简单。
const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9]
const last3 = arr.slice(-3) // [7, 8, 9]
4.获取数组的前 N 个
获取数组的前面的数,这是 slice 方法需要使用两个参数,一个是 begin 开始但不包括 end 的集合。
由于 JS 数组中是从 0 开始(索引从 0 开始),这使得获取前 N 个元素变得很容易,代码如下:
const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9]
const arr2 = arr.slice(0, 4) // [1, 2, 3, 4]
5.获取数组中某段子数组
function pullSegment (arr, begin, length) {
return arr.slice(begin, begin + length)
}
6.将类似数组的对象装好为数组
slice 在类数组(Array-like)对象/集合转换成一个新数组。例如:
const args = Array.prototype.slice.call(arguments)
因为 arguments 实际上不是数组,而是类似数组的对象,为了可以使用 Array 数组中的 slice 方法,代码如下所示:
function addOne() {
return Array.prototype.slice.call(arguments).map(i => i+1);
}
addOne(1, 2, 3) // [2, 3, 4]
Array.prototype.slice.call(arguments) 的调用只是沿着原型链向上找,最终找到 Array 为止,slice 为 Array 原型上的一个方法。slice 内部的实现原理如下:
Array.prototype.slice = function (start, end) {
var result = new Array();
start = start || 0; // 如果不传则取默认值
end = end || this.length; // 如果不传则取默认值
//this指向调用的对象,当用了call后,能够改变this的指向,也就是指向传进来的对象,这是关键
for(var i = start; i < end; i++) {
result.push(this[i]);
}
return result;
}
除了使用 Array.prototype.slice.call(arguments),你也可以简单的使用 [].slice.call(arguments) 来代替。
7.将任意长度多余的参数强制转换为数组
希望可以接受函数的多余参数,组成一个数组。
function myFunc (a, b) {
console.log(a, b) // 1 2
console.log(Array.prototype.slice.call(arguments, 3)); // [3, 4, 5, 6, 7, 8, 9]
}
myFunc(1, 2, 3, 4, 5, 6, 7, 8, 9)
8.修改数组中的特定索引
在函数上下文中,一个强大而常见的用法是:替换数组中特定项的值。在函数的世界中,不能修改原始数组。slice 与扩展运算符一起使用,以返回一个相同,但对于要更新的索引的新数组:
function replaceIdx (arr, index, newVal) {
return [
...arr.slice(0, index),
newVal,
...arr.slice(index + 1),
]
}
三、偏函数应用
函数式编程中的另一种常见模式是所谓的偏函数应用:将函数预先应用于函数,然后返回一个新函数。
这种模式允许你组合函数,通过使用具有不同预应用参数的相同核心函数来创建更大的可重用性。
var partial = function() {
const fn = arguments[0];
const args = Array.prototype.slice.call(arguments, 1);
// Return a function that calls fn
return function() {
var remainingArgs = Array.prototype.slice.call(arguments);
return fn.apply(this, args.concat(remainingArgs));
}
}