2012-12-07 54 views
29

可能重複:
What is the most efficient way to clone a JavaScript object?創建多維數組的副本,而不是引用 - JavaScript的

這也被稱爲「深度複製」,我已經發現了一些文章。最近似乎是this one,但它是爲jQuery - 我試圖做到這一點,沒有一個庫。

我也看到了,在兩個地方,它是可能做這樣的事情:

arr2 = JSON.decode(JSON.encode(arr1)); 

但是,這顯然是低效的。也可以單獨循環和複製每個值,並遍歷所有數組。這看起來很累,效率也不高。

那麼什麼是最有效的,非庫方式來複制JavaScript多維數組[[a],[b],[c]]?如果有必要,我對「非IE」方法感到非常滿意。

謝謝!

+0

你需要它有多高效?你是否在客戶端一遍又一遍(或者像Node這樣的服務器端)? JSON stringify - > parse方法非常靈活,即使效率不高。 –

+0

否則,深度複製意味着遞歸循環... –

+0

您的結構將保存哪些類型的數據?它僅僅是數組還是其他對象?它是否知道你的結構有多深? –

回答

38

由於它聽起來像是在處理某個未知深度的陣列陣列,但是您只需要在任何給定時間在一個深度級別處理它們,那麼它會變得簡單快速使用.slice()

var newArray = []; 

for (var i = 0; i < currentArray.length; i++) 
    newArray[i] = currentArray[i].slice(); 

或者用.map()代替for循環:

var newArray = currentArray.map(function(arr) { 
    return arr.slice(); 
}); 

所以這個遍歷當前陣列,並建立嵌套數組的淺拷貝一個新的數組。然後當你進入下一個深度時,你會做同樣的事情。

當然,如果存在數組和其他數據的混合,則需要在分片之前測試它是什麼。

+0

我猜想,帶有額外函數調用的地圖在大多數瀏覽器中速度要慢得多,儘管它可以通過內聯進行優化,然後可能執行得更好。 – Bergi

+0

@Bergi:是的,地圖版本會受到一些打擊,但它很好,很乾淨。 –

+0

哇,地圖真的很乾淨 –

5

我不知道怎麼也比encodedecode更好JSON.stringyJSON.parse,但你可以嘗試:

JSON.parse(JSON.stringify(array)); 

別的東西,我發現(雖然我修改了一點):

http://www.xenoveritas.org/blog/xeno/the-correct-way-to-clone-javascript-arrays

function deepCopy(obj) { 
    if (typeof obj == 'object') { 
    if (isArray(obj)) { 
     var l = obj.length; 
     var r = new Array(l); 
     for (var i = 0; i < l; i++) { 
     r[i] = deepCopy(obj[i]); 
     } 
     return r; 
    } else { 
     var r = {}; 
     r.prototype = obj.prototype; 
     for (var k in obj) { 
     r[k] = deepCopy(obj[k]); 
     } 
     return r; 
    } 
    } 
    return obj; 
} 
+0

這兩個元素的缺點方法是他們不處理自引用循環 - 這可能是也可能不是重要的(不確定JSON版本將如何處理它,但第二個示例將無限循環) – Jeff

+0

@Jeff我相信你,但可以你給一個(小)的例子?只是想了解 – Ian

+0

'var x = {}; xy = x;'每次調用'deepCopy'時都會碰到'else'語句,而JSON只是普通的不能處理自引用 - 它天生就是基於樹的 – Jeff

3

任何遞歸算法不訪問同一個節點兩次將有約與使用javascript(至少在瀏覽器中)一樣高效 - 在某些情況下,您可能會用其他語言複製內存,但JavaScript顯然不具備這種能力。

我建議找個人already done it並使用他們的實現來確保你做對了 - 它只需要定義一次。

4

正如你問的表現,我想你也會用非通用的解決方案。要複製具有已知級別的多維數組,您應該使用最簡單的解決方案,即一些嵌套for循環。爲了您的雙dimensioanl陣列,它只是看起來像這樣:

var len = arr.length, 
    copy = new Array(len); // boost in Safari 
for (var i=0; i<len; ++i) 
    copy[i] = arr[i].slice(0); 

天然slice method比for循環的自定義更有效,但它不產生深層副本,所以我們可以在只使用它最低級別。