JS 中数组常用方法以及它的原理实现(四)

1,279 阅读6分钟

开始

之前三节总结了15个js 中常用数组方法和原理实现,下面继续按照MDN上介绍总结和整理5个方法。

数组方法

Array.prototype.reverse()

作用

reverse() 方法将数组中元素的位置颠倒,并返回该数组。数组的bunn第一个元素会变成最后一个,数组的最后一个元素变成第一个。该方法会改变原数组。

应用

  • console.log([1,2,3,4,5].reverse()) //[5,4,3,2,1]
  • 也可以用于类数组元素
const a = {0: 1, 1: 2, 2: 3, length: 3};
Array.prototype.reverse.call(a)
console.log(a); // { '0': 3, '1': 2, '2': 1, length: 3 }

实现

思路

  • 在数组的原型上定义方法
  • 会改变原数组,不能改变新数组,所以必须在数组本身操作,不能返回新数组。
  • 采用取中值,对称替换的方法。

代码实现

Array.prototype._reverse=function(){
    if(this.length===0)
    {
        return this;
    }
    //一分为2
    // 1,2,3,4,5 需要 2,4换位置,
    let left=Math.floor(this.length/2)-1; //左侧索引
    let y=this.length%2;//取个余数(长度是偶数就是0,否则是1)
    let right=left+y+1; //右侧索引
    while(left>=0 && right<this.length)
    {
        //交换位置
       let value=this[left];
       this[left]=this[right];
       this[right]=value;
       //指针左移
       left--;
       //指针右移
       right++;
    }
    return this;
}

测试

同样也能获得相同的结果。

console.log([1,2,3,4,5]._reverse()) //[5,4,3,2,1]
const a = {0: 1, 1: 2, 2: 3, length: 3};
Array.prototype._reverse.call(a)
console.log(a); // { '0': 3, '1': 2, '2': 1, length: 3 }

Array.prototype.slice()

作用

slice() 方法返回一个新的数组对象,这一对象是一个由 begin 和 end 决定的原数组的浅拷贝(包括 begin,不包括end)。原始数组不会被改变。

应用

begin和end可正可负,如果为负数时候与数组长度相加。

  • 普通数组
console.log([1,2,3,4,5].slice(2)) //[3,4,5]
console.log([1,2,3,4,5].slice('s')) //[1,2,3,4,5] 
console.log([1,2,3,4,5].slice('s','d')) //[]
console.log([1,2,3,4,5].slice(2)) //[3,4,5]
console.log([1,2,3,4,5].slice(0,2)) //[1,2]
console.log([1,2,3,4,5].slice(0,-2))//[1,2,3] 相当于 (0,3)
console.log([1,2,3,4,5].slice(-2,-1))//[4] 相当于 (3,4)
  • 应用于类数组对象

可以arguments 转为数组

function get() {
    return Array.prototype._slice.call(arguments,1);
  }
console.log(get(1, 2, 3)); // [1, 2, 3]

实现

思路

  • 在数组原型上定义。
  • begin 和end 如果不能转为合法数字,begin默认为0,end默认为数组长度
  • begin 和end 为负数要与数组长度相加,取值时候不包括end,比如 (0,3) 取索引0,1,2。
  • 不能改变原数组,要返回新数组。

代码实现

Array.prototype._slice=function(start,end){
    //取整
    start=Math.floor(start-0)||0;
    end=Math.floor(end-0)||this.length;
    if(start>this.length-1) return [];
    if(end>this.length) end=this.length;
    //为负数与长度相加,与长度相加后仍未负数 置为0
    start=start<0?(start+this.length>0?start+this.length:0):start;
    //与长度相加后仍未负数 置为0
    end=end<0?(end+this.length>0?end+this.length:0):end;
    let arr=[];
    for(let i=start;i<end;i++)
    {
       arr.push(this[i])
    }
    return arr;
}

测试

同样可以实现相同的功能。

console.log([1,2,3,4,5]._slice(2)) //[3,4,5]
console.log([1,2,3,4,5]._slice(0,2)) //[1,2]
console.log([1,2,3,4,5]._slice(0,-2))//[1,2,3]
console.log([1,2,3,4,5]._slice(-2,-1))//[4]

Array.prototype.some()

作用

some() 方法测试数组中是不是至少有1个元素通过了被提供的函数测试。它返回的是一个Boolean类型的值。与every 方法对应

实现

代码

与every差不多,不再多言。

Array.prototype._every=function (callback,thisArg) {
    if(typeof callback!='function')
    {
        throw new TypeError('callback is not a function')
    }
    let target=this,res=false;
    let context=thisArg ||this;
    for(let i=0;i<target.length;i++)
    {
        //有一项满足就返回true
        if(callback.call(context,target[i],i,target))
        {
             return true;
        }
    }
    return res;
}

Array.prototype.splice()

作用

splice() 方法通过删除或替换现有元素或者原地添加新的元素来修改数组,并以数组形式返回被修改的内容。此方法会改变原数组。 三个参数 [start.deleteCount,...args]

  • start :起始索引,可为负数
  • deleteCount 删除元素的个数,默认为start索引之后的全部移除。
  • ...args 其余参数 表示替换项

应用

也是数组中一个很重要的API

  • 移除数组某几项,最常见的就是根据索引删除元素
var a=[1,2,3,4,5,6]
console.log(a.splice(1,1)) //[ 1]
console.log(a);//[ 1, 3, 4, 5, 6 ]
  • 替换元素
var a=[1,2,3,4,5,6]
console.log(a.splice(1,1,10)) //[2]
console.log(a);  [1, 10, 3, 4, 5, 6]

实现

思路

  • 在数组的原型上定义方法。
  • 由于改变原数组但是不能出现 this=xxx;这样会报错,所以只能原数组上操作。
  • 举例:[1,2,3,4,5] => (1,2,3,4) 三部分 [1],[2,3]移除 返回的结果, 剩下 [4,5],然后插入[1](start 之前的) [3,4] (替换部分)
  • 方法的返回值,所以要将将移除的元素存入 res,返回。

代码实现

Array.prototype._splice=function(){
    
    let params=Array.prototype.slice.call(arguments);
    let start=params.shift();
    let deleteCount=params.shift();
      //取整
    start=Math.floor(start-0)||0;
    if(start<0)
    {
        start=this.length+start;
    }
    if(start>this.length-1) return [];
    //不是合法数字 默认未删除
    deleteCount=Math.floor(deleteCount-0)|| this.length-start;
    //小于0 默认为0
    deleteCount<0?0:deleteCount;
    deleteCount>this.length-start?this.length-start:deleteCount;
    let i=0,startArr=[],endArr=[],res=[],len=this.length;
  
    for(let i=0;i<start+deleteCount;i++)
    {
        let value=this.shift();
        //之前的缓存
        if(i<start)
        {
            startArr.push(value)
        }
        //这是需要移除的部分
        if(i>=start)
        {
            res.push(value)
        }
    }
    // 注意从头部插入
    //应用队列和栈的思想(params尾部元素出栈,入队)
    while(params.length)
    {
        this.unshift(params.pop());
    }
    while(startArr.length)
    {
        this.unshift(startArr.pop());
    }
    return res;
}

测试

let arr=[1,2,3,4];
arr._splice(0,2,3,5); //[0,2]
console.log(arr); //[3,5,3,4]
//尾部替换
var a=[1,2,3,4,5,6]
console.log(a._splice(-1,1,10)) //[6]
console.log(a); //[ 1, 2, 3, 4, 5, 10 ]

Array.prototype.sort()

作用

sort() 方法用原地算法对数组的元素进行排序,并返回数组。参数为 compareFunction(比较方法),如果为空默认将元素转换为字符串,然后比较它们的UTF-16代码单元值序列时构建的。

应用

数组排序 console.log(([2,3,2,5,3,7,5]).sort((a,b)=>a>b)) // [ 2, 2, 3, 3, 5, 5, 7 ]

console.log(([2,3,2,5,3,7,5]).sort((a,b)=>a<b)) // [ 7, 5, 5, 3, 3, 2, 2 ]

实现

思路

仅处理排序方法必传的情况

  • 在数组原型上定义该方法。
  • 实现一个排序算法(当前使用冒泡排序)
  • 会改变原数组,需要再原数组上排序。

代码实现

Array.prototype._sort=function(compareFunction){
    if(typeof compareFunction!='function')
    {
        throw new TypeError('compareFunction is not a function')
    }

     bubbleSort(this,compareFunction);
     return this
    /**
     * 
     * @param {*} arr 排序数组
     * @param {*} fn 比较的方法
     */
    function bubbleSort(arr,fn){

        for(let i=0;i<arr.length;i++)
        {
            let swap=false;
            for(let j=0;j<arr.length-i;j++)
            {
                if(fn(arr[j],arr[j+1]))
                {
                    swap=true;
                    let value=arr[j];
                    arr[j]=arr[j+1];
                    arr[j+1]=value;
                }
            }

            if(!swap)
            {
                return arr;
            }
        }

    }
}

测试

console.log(([2,3,2,5,3,7,5])._sort((a,b)=>a<b)) //[ 7, 5, 5, 3, 3, 2, 2 ]

结束

至此我们总结了20多个数组中常用的方法,其实还有许多API,可以参看MDN上的介绍 去熟悉使用,下面做一个简单的总结。

  • 会改变原数组的API push,pop,unshift,shift,splice,sort,reverse,七个方法,看起来很熟悉,vue2中需要对数组响应式处理需要重写的就是这七个方法。
  • 返回新数组的 concat,filter,flat,map,slice等。
  • 用于判断的 every,indexOf,includes,some等。
  • 用于查找的 find,filter等。

数组中常用的方法和它原理实现已经基本总结完毕,总结的方法中有些未作严格的参数校验。如有错误,请谅解,欢迎批评指正。

相关链接