JS數組的深淺拷貝

javascript數組在使用時,時常會遇到數組備份的情況,之後對數組做些修改,再同原數組進行比對,查看數組的變化,這裏就涉及到一個數組拷貝的問題。

淺拷貝只複製一層對象的屬性;深拷貝遞歸複製了所有層級。


數組的拷貝,通常可以使用一個新的數組,指向現有數組

var arr = [el1, el2, el3...];
var arr2 = arr;

這種寫法,待arr2做改變時,我們查看arr會同步做修改

ex 

var arr = ['liuche', 'zhouyafu', 'huoqubing', 'weiqing'];
var arr2 = arr;
arr2.push('liguang');
alert(arr); // 'liuche', 'zhouyafu', 'huoqubing', 'weiqing', 'liguang'
alert(arr2); // 'liuche', 'zhouyafu', 'huoqubing', 'weiqing', 'liguang'

這裏我們看到,待修改數組arr2時,arr同時做了改變,這顯然不是我們想要的結果。

示例中,這種直接將數組引用複製的方式就是淺拷貝。


那我們想要對數組進行備份的話,該如何操作呢,可以藉助於Array對象的slice()方法和concat()方法。

slice方法

slice() 方法可從已有的數組中返回選定的元素。

arrayObject.slice(start,end)

其中:

start,必需。規定從何處開始選取。如果是負數,那麼它規定從數組尾部開始算起的位置,從0開始。也就是說,-1 指最後一個元素,-2 指倒數第二個元素,以此類推。

end,可選。規定從何處結束選取。該參數是數組片斷結束處的數組下標。如果沒有指定該參數,那麼切分的數組包含從 start 到數組結束的所有元素。如果這個參數是負數,那麼它規定的是從數組尾部開始算起的元素。

slice方法返回一個新的數組,包含從 start 到 end (不包括該元素)的 arrayObject 中的元素。

若start爲0,end 缺省,則相當於截取了整個數組的元素值,即我們這裏要說的數組拷貝。

ex2

var arr = ['liuche', 'zhouyafu', 'huoqubing', 'weiqing'];
var arr2 = arr.slice(0);
arr2.push('liguang');
alert(arr); // 'liuche', 'zhouyafu', 'huoqubing', 'weiqing'
alert(arr2); // 'liuche', 'zhouyafu', 'huoqubing', 'weiqing', 'liguang'
但是,如果遇到多維數組,slice方法並不奏效

ex3

var arr = [['liuche', 'zhouyafu', 'weiqing'], ['chengajiao', 'weizifu', 'liupiao']];
var arr2 = arr.slice(0);
arr2.push('liguang');
alert(arr); // liuche,zhouyafu,weiqing,chengajiao,weizifu,liupiao
alert(arr2); // liuche,zhouyafu,weiqing,chengajiao,weizifu,liupiao,liguang

ex4

var arr = [['liuche', 'zhouyafu', 'weiqing'], ['chengajiao', 'weizifu', 'liupiao']];
var arr2 = arr.slice(0);
arr2[0][3] = 'liguang';
alert(arr); // liuche,zhouyafu,weiqing,liguang,chengajiao,weizifu,liupiao
alert(arr2); // liuche,zhouyafu,weiqing,liguang,chengajiao,weizifu,liupiao

上面兩個示例中,ex3中,arr2修改後,arr並未跟着一起修改,因爲arr2的修改,是給arr2新增了arr[3]元素;但是ex4中,arr2修改後,arr跟着一起修改了,原因在於arr中arr[0] 元素是個數組對象,並非單的數值。

concat方法

concat() 方法用於連接兩個或多個數組。

該方法不會改變現有的數組,而僅僅會返回被連接數組的一個副本。

arrayObject.concat(arrayX,arrayX,......,arrayX)

其中

arrayX,必需。該參數可以是具體的值,也可以是數組對象。可以是任意多個。

concat方法返回一個新的數組。該數組是通過把所有 arrayX 參數添加到 arrayObject 中生成的。如果要進行 concat() 操作的參數是數組,那麼添加的是數組中的元素,而不是數組。

使用concat() 方法時,若arrayX爲空,則相當於原數組與一個空數組拼接,即返回原數組,做到原數組的拷貝。

那使用concat方法再來看看剛纔的示例

ex5

var arr = [['liuche', 'zhouyafu', 'weiqing'], ['chengajiao', 'weizifu', 'liupiao']];
var arr2 = arr.concat();
arr2[0][3] = 'liguang';
alert(arr); // liuche,zhouyafu,weiqing,liguang,chengajiao,weizifu,liupiao
alert(arr2); // liuche,zhouyafu,weiqing,liguang,chengajiao,weizifu,liupiao
結果同ex4相同,並未得到我們想要的結果

實在無解之際,諮詢了下一個專職做前端的同事,瞭解到一個萬能的JS拷貝方法,無論是數組還是對象均可以實現深拷貝

JSON.parse(JSON.stringify(arr));
這個方法其實比較簡單,先把所有的對象屬性解析爲簡單數值,再將數值拼接解析爲JS對象。

看之前的例子

ex6

var arr = [['liuche', 'zhouyafu', 'weiqing'], ['chengajiao', 'weizifu', 'liupiao']];
var arr2 = JSON.parse(JSON.stringify(arr));
arr2[0][3] = 'liguang';
alert(arr); // liuche,zhouyafu,weiqing,chengajiao,weizifu,liupiao
alert(arr2); // liuche,zhouyafu,weiqing,liguang,chengajiao,weizifu,liupiao

解決上面的問題。


經過上面的例子和分析,可以看出來,簡單數組的拷貝可以通過slice方法和concat方法來實現,對於多維數組的實現,必須通過JSON.parse(JSON.stringify(obj))方法來實現。初次學習,有不當地方還望大家批評斧正。謝謝。






發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章