今天花了一点时间手写了比较常用的一些方法的源码,其中,reduce和splice这两个方法比别的略微复杂点,就分享一下啦~
ps:flat这个方法的源码也蛮有意思的,和我的上一篇博客里面“关于实现数组扁平化”的代码很相似,就不再码一遍了。
splice方法
splice(stratIndex,delCount,ele1,ele2...)
该方法表示从startIndex下标开始,删除delCount个数组元素,然后在startIndex处添加一个或多个元素(该方法会对数组产生影响)
例如
let arr = [10, 20, 30, 40, 50];
let arr1 = arr.splice(2,1);//如果delCount不写,默认删除后面所有
console.log(arr1);// 返回包含被删除元素的数组 [30]
console.log(arr);//对原数组产生影响 [10, 20, 40, 50]
let arr = [10, 20, 30, 40, 50];
let arr1 = arr.splice(2,1,11,22);
console.log(arr1);// [30]
console.log(arr);//返回了插入新元素的数组 [10, 20, 11, 22, 40, 50]
开始实现
我的逻辑分四步:
- 处理startIndex,delCount边缘化的情况(例如初始下标小于0或者大于数组长度,删除数量小于0或删除数量比能删的数量多的情况)
- 获取删除元素,并添加到新数组中(这个比较简单,遍历完事~)
- 移动原数组(主要考虑三种情况:删除数量 大于/小于/等于 添加数量 ;这个关系到原数组是向前还是向后移动了)
- 添加新元素到原数组中(由于上一步“移动原数组”,已经给新元素腾好位了,所以这一步也只是简单的遍历啦
大纲
下面来捋捋顺哈!先给个大纲
Array.prototype.mySplice = function (startIndex, delCount, ...addElements) {
let array = Object(this);//浅拷贝
let len = array.length;
let addCount = addElements.length;
// 处理参数边缘化的情况
startIndex = computedStartIndex(startIndex, len);
delCount = computedDelCount(startIndex, delCount, len);
// 获取删除元素,并添加到新数组中
let delArr = new Array(delCount).fill(null);
sliceDelElements(delArr, array, startIndex);
// 移动原数组
removeArr(array, startIndex, delCount, addCount);
// 添加新元素到原数组中
for (let i = 0; i < addCount; i++) {
arr[startIndex + i] = addElements[i];
}
array.length = len -delCount + addCount;
return delArr;
}
然后就是分别给出上面四点的具体实现代码~
1. 处理startIndex,delCount边缘化的情况
(例如初始下标小于0或者大于数组长度,删除数量小于0或删除数量比能删的数量多的情况)
const computedStartIndex = function (startIndex, len) {
//startIndex为负数
if (startIndex < 0) {
return startIndex + len > 0 ? startIndex + len : 0;
}
//startIndex>len 不删除了
return startIndex > len ? len : startIndex;
}
const computedDelCount = function (startIndex, delCount, len) {
//delCount没写,默认删除后面所有
if (typeof delCount === "undefined") {
return len - startIndex;
}
//delCount太大
else if (delCount > len - startIndex) {
return len - startIndex;
}
//delCountIndex为负数
else if (delCount < 0) {
return 0;
}
return delCount;
}
2. 获取删除元素,并添加到新数组中
(这个比较简单,遍历完事~)
const sliceDelElements = function (delArr, array, startIndex) {
for (let i = 0; i < delArr.length; i++) {
delArr[i] = array[startIndex + i]
}
return delArr;
}
3. 移动原数组
(主要考虑三种情况:删除数量 大于/小于/等于 添加数量 ;这个关系到原数组是向前还是向后移动了
const removeArr = function (array, startIndex, delCount, addCount) {
//删除数量小于增加数量,数组向后移动
if (delCount < addCount) {
// 向后移动几位 addCount - delCount
for (let i = array.length; i >= startIndex + delCount; i--) {
let fromIndex = i;
let toIndex = i + addCount - delCount;
arr[toIndex] = arr[fromIndex];
}
}
// 删除数量大于增加数量,数组向前移动
else if (delCount > addCount) {
// 向前移动 delCount - addCount 位
for (let i = startIndex + delCount; i < array.length; i++) {
let fromIndex = i;
let toIndex = i - delCount + addCount;
arr[toIndex] = arr[fromIndex];
}
// 删除冗余元素
for (let i = array.length - 1; i >= array.length - delCount + addCount; i--) {
delete array[i]
}
}
// 删除数量等于增加数量 ,数组不变
else {
return;
}
}
4. 添加新元素到原数组中
(由于上一步“移动原数组”,已经给新元素腾好位了,所以这一步也只是简单的遍历啦
这一部分的代码写在“大纲”里面了
最后检验一下~(可以自己运行一遍试试看,结果就不贴了)
let arr = [10, 20, 30, 40, 50];
let arr1 = arr.mySplice(-2);
console.log(arr1);
console.log(arr)
ps:这里参考了这篇文章,如果看到这里还整不明白实现splice的思路,可以点进去看看~
reduce方法
数组中用来迭代遍历每一项的。可以把每一次处理的结果都拿到,在第一次处理的基础上,进行二次处理……直到数组遍历完成
reduce(callback(result,item,currentIndex,arr),value)
和callback的同级别中,如果传递了value值,则第一次执行callback,result存储的参数信息value的值。同时item迭代的是数组的第一项;如果不传value,result是数组第一项,同时item是数组第二项(注:value可选)
value没有传值的情况:
let arr = [10,20,30,40,50];
let add = arr.reduce((result,item,i)=>{
console.log(result,item,i);//10,20,1
// 这里result是数组第一项,item是数组第二项,i为当前item的下标
})
value有传值的情况:
let arr = [10,20,30,40,50];
let add = arr.reduce((result,item,i)=>{
console.log(result,item,i);//0 10 0
// 这里result存储参数value的值,item是数组第二项,i为当前item的下标
},0)
这个函数经常被人们用来当作“累加器”,因为数组会迭代每一项,并且每一次处理的结果,都会存储到result里面,下次迭代的时候,可以对上一次处理的结果,进行二次处理
例如这个简单的累加
let arr = [10,20,30,40,50];
let add = arr.reduce((result,item,i)=>{
return result + item;
//第一次迭代:result:10 item:20 返回结果:30(10+20)
//第二次迭代:result:30 item:30 返回结果:60(30+30)
// 。。。
})
console.log(add);//150
开始实现
彻底了解了reduce这个方法后,开始来捋捋思路了
- 方法对原数组是不产生影响的(所以一开始就要把数组拷贝一份)
- value有无传值的影响(有传值,item是第一项,index=0 ;没有传值,则item是数组的第2项,index=1
- 如果原数组只有一项,且没有传值的情况(一般没有传值的话,item是数组第二项,既然没有第二项了,那么就直接返回result
下面直接贴代码了
function myReduce(arr,callback,init){
//这个是为了不对原数组产生影响
arr = arr.slice(0)
// 判断有没有传递初始值
// 这里做了优化,只有第一次迭代的时候需要判断init,后面就不需要了
let result = init;
// 有传值,item是第一项,index=0
for(let i = 0;i<arr.length;i++){
// 没有传值,则item是数组的第2项,index=1 init===>undefined
if(init === undefined && i === 0){
result = arr[0];
// 如果item只有一项,直接返回了
let item = arr[1],
index = 1;
if(!item) return result;
result = callback(result,item,index);
i++;
continue;
}
let item = arr[i],
index = i;
result = callback(result,item,index);
}
return result;
}
测试!
console.log("======没有传值======")
let result1 = myReduce(arr,function(result,item,index){
console.log(result,item,index)
return result +item;
})
console.log(result1)
//=====
console.log("======有传值======")
//=======
let result2 = myReduce(arr,function(result,item,index){
console.log(result,item,index)
return result +item;
},0)
console.log(result2)
希望我把思路给你捋明白了!能看到这里的你,一定很棒!