一、項目問題
二、查找原因
三、解決思路
四、實現方案
一、項目問題
每次需要操作數組的時候,比如數組遍歷,我都毫不猶豫的使用原生已經封裝好的數組的方法,比如:
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]
,效果如下:
二、查找原因
經研究發現兩個問題:
- 數組有些元素未遍歷;
- 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);
測試結果也如我們所料:
到此一個可以解決數組在進行遍歷的時候,如果數據發生變化(增刪),依然可以保證數組正常遍歷,從而返回正常的結果。