2013-01-23 77 views
22

我已經閱讀了許多有關'按值'和'通過引用'傳遞給javascript函數的答案。然而,我有一個問題發送一個數組到一個函數,並保持原來的數組不變。這個例子說明了這個問題:JavaScript將數組傳遞給函數的值,使原始數組保持不變

function myFunction(someArray) 
{ 
// any function that makes an array based on a passed array; 
// someArray has two dimensions; 
// I've tried copying the passed array to a new array like this (I've also used 'someArray' directly in the code); 

funcArray = new Array(); 
funcArray = someArray; 

var i = 0; 

    for(i=0; i<funcArray.length; i++) 
    { 
    funcArray[i].reverse; 
    } 

return funcArray; 

} 

我不明白爲什麼這個函數中的任何東西都應該改變原始數組。

調用如果函數調用被分配到一個新的數組此功能直接改變原始數組:

myArray = [["A","B","C"],["D","E","F"],["G","H","I"]]; 
anotherArray = new Array(); 

anotherArray = myFunction(myArray); 
// myArray gets modified!; 

我嘗試使用.valueOf()發送原始:

anotherArray = myFunction(myArray.valueOf()); 
// myArray gets modified!; 

我甚至試圖按元素和子元素按子元素拆分數組,並將所有元素分配給新的2維數組,並且原始數組仍然被修改。

我也加入了一個字符串的子元素,處理它們,將它們拆分回數組,並且原始數組仍然被修改。

請問,有沒有人知道我可以傳遞數組值到一個函數,而不是通過數組更改?

+0

對不起,我忘了提及slice方法也沒有區別。我已經看到了這個提到並嘗試了它,但它並沒有停止原來的數組更改(也沒有.valueOf()這應該是減少數組內容爲原始) –

+0

我現在已經探索了slice()的一些想法,問題在於它是一個二維數組。如果我切片外部數組,它沒有區別,原始數組仍然被改變。但是,如果我切片每個子陣列,它的作品!我爲每個外部元素設置一個循環,並將其切片分配給新數組的外部元素。 –

回答

32

裏面你的函數有這樣的:

funcArray = new Array(); 
funcArray = someArray; 

事實上這並不會複製someArray而是引用它,這就是爲什麼原始數組被修改。您可以使用Array.slice()創建一個所謂的數組淺表副本。

var funcArray = someArray.slice(0); 

原始陣列將是不變,但它的每個元素仍然會引用原始陣列中它們的對應的條目。對於「深度克隆」,您需要遞歸執行此操作;最有效的方法是在以下問題討論:

What is the most efficient way to deep clone an object in JavaScript?

順便說一句,我funcArray前加入var。這樣做使得它對函數是局部的,而不是全局變量。

+0

謝謝,根據我的第二條評論,slice()使其工作,但只有通過循環應用於2-d數組的每個外部元素。 –

1

指向數組的變量是對它的引用。當你傳遞一個數組時,你正在複製這個引用。

您可以使用slice()製作淺拷貝。如果你想要一個完整的深度副本,那麼在子對象中進行遞歸,同時要記住複製某些對象時的注意事項。

+0

謝謝,是的,我發現如果我依次切片每個外部元素,它現在可以工作。 –

0

一個通用的解決辦法是......

// Use the JSON parse to clone the data. 
function cloneData(data) { 
    // Convert the data into a string first 
    var jsonString = JSON.stringify(data); 

    // Parse the string to create a new instance of the data 
    return JSON.parse(jsonString); 
} 

// An array with data 
var original = [1, 2, 3, 4]; 

function mutate(data) { 
    // This function changes a value in the array 
    data[2] = 4; 
} 

// Mutate clone 
mutate(cloneData(original)); 

// Mutate original 
mutate(original); 

本工程爲對象和數組。

當您需要深度克隆或者您不知道類型是什麼時非常有效。

深克隆例子...

var arrayWithObjects = [ { id: 1 }, { id: 2 }, { id: 3 } ]; 

function mutate(data) { 
    // In this case a property of an object is changed! 
    data[1].id = 4; 
} 

// Mutates a (DEEP) cloned version of the array 
mutate(cloneData(arrayWithObjects)); 

console.log(arrayWithObjects[1].id) // ==> 2 

警告

  • 使用JSON解析器克隆不是最高效的選擇!

  • 它並不克隆功能不僅JSON支持的數據類型

  • 無法複製循環引用

+0

這看起來很全面,但我不熟悉json。看起來我應該投資於發現更多。問題現在通過循環每個外部元素並將其.slice()分配給一個新數組來解決。 –

+0

在你的例子中,cloneData'不應該只是'clone'(或者相反)? – neemzy

+0

@neemzy正確!改變了它。 –

7

讓你可以使用數組的一個副本。

一個簡單的方法做,這是通過使用var clone = original.slice(0);

+0

謝謝。我發現切片只適用於通過循環應用於每個外部元素:newArray [i] = array [i] .slice() –

0

如果你需要一個對象要做到這一點,試試這個花哨的技巧......

MY_NEW_OBJECT = JSON.parse(JSON.stringify(MY_OBJECT)); 
-1
var aArray = [0.0, 1.0, 2.0]; 
var aArrayCopy = aArray.concat(); 
aArrayCopy[0] = "A changed value."; 
console.log("aArray: "+aArray[0]+", "+aArray[1]+", "+aArray[2]); 
console.log("aArrayCopy: "+aArrayCopy[0]+", "+aArrayCopy[1]+", "+aArrayCopy[2]); 

這個答案已經被編輯。最初我提出new運營商處理該解決方案,但不久之後發現該錯誤。相反,我選擇使用concat()方法來創建副本。原來的答案沒有顯示整個陣列,所以錯誤被無意中隱藏了。下面顯示的新輸出將證明此答案按預期工作。

aArray: 0, 1, 2 
aArrayCopy: A changed value., 1, 2 
+0

該代碼不會做你的想法,用'new Array(aArray)'創建一個新的數組中的第一個元素是'aArray',而不是複製原始數組,記錄完整的數組而不是第一個元素,您將看到不同之處。 –

+0

謝謝阿爾貝託。你很快回復,並且正確。使用'new Array(someArray);'會將長度設置爲1,並將第一個元素設置爲傳遞的arrray的引用,而不按每個元素賦予適當的值賦值,這與傳遞值的預期相同。最近我一直在使用Float32Array,並且必須重新學習,基本數組的構造方式不同。我的測試示例輸出沒有識別錯誤。現在它正確地導致整個陣列副本。 –