轉:JS中陣列的拷貝方法
在園子裡看到一篇非常好的關於js資料拷貝的文章,把他轉過來,給自己做個筆記。原文出自:JS中陣列的拷貝方法
之前在寫一個vue的計算屬性時,大概是這樣:
computed: { updateList () { let newList = this.List /*do something*/ return newList }, }
本想的是設定箇中間變數newList,將它和原來的List相比做一些操作,最後返回這個newList,後來發現原List也改變了。才意識到這個newList只是個引用型別,改變了它原來的物件也會變。
查資料的過程中,注意到了還有深拷貝淺拷貝的區別:
淺拷貝是拷貝一層,深層次的物件級別的就只拷貝引用;
深拷貝是拷貝多層,每一級別的資料都會拷貝出來。
一.Array的拷貝
1.淺拷貝
如上,直接賦值的方式
2.深拷貝
(1)slice()方法
對於array物件的slice函式,返回一個數組的一段。(仍為陣列) arrayObj.slice(start, [end]) 引數: arrayObj 必選項。一個 Array 物件。 start 必選項。arrayObj 中所指定的部分的開始元素是從零開始計算的下標。 end可選項。arrayObj 中所指定的部分的結束元素是從零開始計算的下標。 說明: slice 方法返回一個 Array 物件,其中包含了 arrayObj 的指定部分。 slice 方法一直複製到 end 所指定的元素,但是不包括該元素。 如果 start 為負,將它作為 length + start處理,此處 length 為陣列的長度。 如果 end 為負,就將它作為 length + end 處理,此處 length 為陣列的長度。 如果省略 end ,那麼 slice 方法將一直複製到 arrayObj 的結尾。 如果 end 出現在 start 之前,不復制任何元素到新陣列中。
對於本例,let newList = this.List.slice()即可
(2)concat()方法
concat() 方法用於連線兩個或多個數組。該方法不會改變現有的陣列,而僅僅會返回被連線陣列的一個副本。 語法:arrayObject.concat(arrayX,arrayX,......,arrayX) 說明:返回一個新的陣列。該陣列是通過把所有 arrayX 引數新增到 arrayObject 中生成的。如果要進行 concat() 操作的引數是陣列,那麼新增的是陣列中的元素,而不是陣列。
對於本例,let newList = this.List.concat()即可
但是,這兩種方法都有侷限性,如:
var arr1 = [{"name":"weifeng"},{"name":"boy"}];//原陣列 var arr2 = [].concat(arr1);//拷貝陣列 arr1[1].name="girl"; console.log(arr1);// [{"name":"weifeng"},{"name":"girl"}] console.log(arr2);//[{"name":"weifeng"},{"name":"girl"}]
var a1=[["1","2","3"],"2","3"],a2; a2=a1.slice(0); a1[0][0]=0; //改變a1第一個元素中的第一個元素 console.log(a2[0][0]); //影響到了a2 var b1=[["1","2","3"],"2","3"],b2; b2=b1.slice(0); b1[0][0]=0; //改變a1第一個元素中的第一個元素 console.log(b2[0][0]); //影響到了a2
從上面兩個例子可以看出,由於陣列內部屬性值為引用物件,因此使用slice和concat對物件陣列的拷貝,整個拷貝還是淺拷貝,拷貝之後陣列各個值的指標還是指向相同的儲存地址。
因此,slice()和concat()這兩個方法,僅適用於對不包含引用物件的一維陣列的深拷貝
(3)使用JSON.stringify和JSON.parse實現深拷貝:
JSON.stringify把物件轉成字串,再用JSON.parse把字串轉成新的物件
對於本例, let newList = JSON.parse(JSON.stringify(this.List))
JSON.stringify()有一些侷限,比如不能拷貝function,詳見:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify
(4)當然,如果是不那麼複雜的陣列,你可以宣告一個新陣列,自己寫一個for迴圈拷貝過去。