手写源码2--数组的slice方法

623 阅读1分钟

数组的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方法,实现基本类似。