美文网首页
js原生数组的Bug和解决方案

js原生数组的Bug和解决方案

作者: 代艳霞 | 来源:发表于2019-08-16 19:39 被阅读0次

    一、项目问题
    二、查找原因
    三、解决思路
    四、实现方案

    一、项目问题

    每次需要操作数组的时候,比如数组遍历,我都毫不犹豫的使用原生已经封装好的数组的方法,比如:filter(),forEach(),every()....,感觉它可以解决我遇到的一切疑难杂症,但是没想到,今天我的问题居然就是原生数组惹的祸。情况是这样的,当我在遍历数组的时候,同时需要对数组进行增删的操作,我用了原生数组的方法,但是结果却不是我想要的。案例如下:

    var arr2 =[0,1,2]
    arr2.filter(function (current,index,arr2) {
        if(current%2){
            arr2.splice(index,0,'add');
        }
        console.log("arr2:", arr2)
        console.log("current:", current)
    
    })
    

    理论上遍历结束以后,得到的数组应该是:arr2: [0, "add", 1, 2]
    但是实际,遍历后得到的结果却是:arr: [0, "add", "add", 1, 2],效果如下:

    错误案例

    二、查找原因

    经研究发现两个问题:

    1. 数组有些元素未遍历;
    2. index 依次递增;

    综合上面的两个问题,我们再看代码的执行过程:

    index==1的时候,arr2[1]=1, 执行arr2.splice(index,0,'add');,数组元素增加,此时数组变为[0,"add", 1, 2]

    index=2的时候,理论上我们希望此时数组的元素为arr2[2]=2 ,但是实际 arr2[2]=1 ,但1的元素已经在index==1 的时候已经遍历过了。此处元素1重复遍历。

    index=3的时候,理论上我们希望此时数组的元素为arr2[3]=3 ,但是实际 arr2[3]=1 ,此处元素1再次重复遍历。

    之后遍历结束,而元素2,3却没有遍历。导致漏数组元素漏遍历。

    如下图:


    漏遍历

    总结错误原因:就是原生数组在进行数组遍历的时候,index依次增加,未考虑数组的长度发生变化(增加或者删除),如上效果,导致此时无法获取到正确的元素而出现了错误的结果。

    三、解决思路

    那既然错误原因也找到了,如何解决这个问题呢,思路很简单,那就是如何保证在使用原生数组方法遍历数组的时候,保证有一份数据是不变的,那我们可用原数组生成一个新的数组作为副本,用副本进行遍历,以此来解决我们的问题。

    四、实现方案

    我们可以封装一个方法,用来扩展数组的原始的方法,这样也方便以后使用,具体实现如下:

    Array.prototype.extendErgodicArray = function (callback, thisValue) {
        //拷贝原始数据作为副本
        var copyArr = this.slice();
        if (arguments.length <= 1) {
            thisValue = this;
        }
        copyArr.filter(function (currentValue,currentIndex,copyArr) {
            currentIndex = this.indexOf(currentValue);
            callback.call(thisValue,currentValue,currentIndex,this);
        },this)
    
    }
    

    这里有个地方需要注意一下:
    在我们的解决方案中,我们将被遍历的数组拷贝了一份,然后遍历该副本,并把原数组传给了回调函数,其实我们也可以遍历原数组,把副本传给回调函数,这两种方案哪个更好呢?

    答: 正确的方案是:遍历副本,把原数组传给回调函数;

    理由:
    原声数组遍历存在问题的原因就是因为在回调函数内删除或增加了原数组的元素导致的,所以即使我们把副本传给了回调函数并且遍历原数组,由于原数组是使用者提供的,所以使用者依然可以访问到原数组,所以依然存在着使用者会更改我们遍历的数组(原数组)的可能性,所以数组遍历的问题依然有可能出现,所以我们应该遍历副本,把原数组传给回调函数,这样使用者无法访问我们的副本,从而保证了能够遍历到每个元素。

    使用方式如下:

    var arr =[0,1,2,3]
    arr.extendErgodicArray(function callback(current,index, oriArry) {
       
        console.log('当前index的值:', index,'当前元素的值:', current,'arr:', arr);
        if(current%2){
            arr.splice(index,0,'add');
        }
    },arr);
    

    测试结果也如我们所料:

    正确结果

    到此一个可以解决数组在进行遍历的时候,如果数据发生变化(增删),依然可以保证数组正常遍历,从而返回正常的结果。

    相关文章

      网友评论

          本文标题:js原生数组的Bug和解决方案

          本文链接:https://www.haomeiwen.com/subject/nxbnjctx.html