数组的slice方法可以从原数组中指定的索引位置浅拷贝出一个新数组,在实现slice方法之前需要先了解它的具体用法:
let arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
console.log(arr.slice()); // [0,1,2,3,4,5,6,7,8,9]
console.log(arr.slice(1)); // [1,2,3,4,5,6,7,8,9]
console.log(arr.slice(1, 4)); // [1,2,3]
console.log(arr.slice(1, 20)); // [1,2,3,4,5,6,7,8,9]
console.log(arr.slice(5, 1)); // []
console.log(arr.slice(-1)); // [9]
console.log(arr.slice(-1, -5)); // []
console.log(arr.slice(-1, -22)); // []
console.log(arr.slice(-22, -1)); // [0,1,2,3,4,5,6,7,8]
console.log(arr.slice(-22)); // [0,1,2,3,4,5,6,7,8,9]
console.log(arr.slice(-5, -1)); // [5,6,7,8]
console.log(arr.slice(1, -12)); // []
console.log(arr.slice(1, -2)); // [1,2,3,4,5,6,7]
通过以上代码可以发现:
- 结果返回一个数组;
- 不传入参数则拷贝整个数组;
- 传入两个参数则拷贝
[start,end)这个区间的元素(不包括end位置的元素); - 传入一个参数则拷贝该索引位置到数组末尾的所有元素;
- 参数为负数则该索引位置=参数+数组长度;
- 两个参数同为正、同为负的时候,
start < end才能拷贝元素; - 拷贝最小从索引为0的位置开始,最长到拷贝整个数组。
了解了slice方法的具体用法之后,基本也就知道了大概实现:
Array.prototype.slice = function (start, end) {
// 保存结果
let res = [];
// this指向调用slice方法的数组
let len = this.length;
// 参数默认值
// 没有传入start,从0开始截取
// 没有传入end,截取整个数组长度
start = start === undefined ? 0 : start;
end = end === undefined ? len : end;
// 边界处理
// start小于0,从star+len的位置截取
// 如果start+len依然小于0,从0开始截取
start = start < 0 ? start + len : start;
start = Math.max(0, start);
// end小于0,截取到end+len的位置
// 如果end+len继续小于0,由于start最小位置是0开始,这种情况循环截取条件不会成立
// 如果end大于数组元素长度len,截取整个数组长度
end = end < 0 ? end + len : end;
end = Math.min(end, len)
// 截取[start,end)这个区间的元素
// 隐含了start>end时循环条件不会成立的情况
for (let i = start; i < end; i++) {
res.push(this[i])
}
return res;
}
slice方法除了能浅拷贝数组之外,有时候还可以利用它将类数组对象转为成数组,比如将函数中的arguments转为数组,其实是根据类数组生成一个新的数组:
function test(){
const args=Array.prototype.slice.call(arguments);
// Array.prototype.slice本身是一个函数,
// 它调用call方法之后,其内部的this指向arguments对象
// Array.prototype.slice函数体执行即可生成一个新的参数数组
return args;
}
console.log(test(1,2,3,4)) // [1,2,3,4]
另外字符串也有slice方法,实现基本类似。