2013-01-23 10 views
22

javascript関数に配列を送るための 'by value'と 'by reference'に関する多くの答えをここで読んでいます。私はしかし、関数に配列を送信し、元の配列を変更しないままにする問題があります。この例では、問題を説明しています。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-d配列に割り当てようとしても、元の配列はまだ変更されました。

私はまた、サブ要素を文字列に結合し、それらを処理し、それらを配列に分割して元の配列を変更します。

配列の値を関数に渡して、渡された配列の変更をどのようにすることができますか?

+0

申し訳ありませんが、私はスライス方法も違いはないことを忘れていました。私はこれを参照して、それを試してみましたが、元の配列の変更を停止しません(配列の内容をプリミティブに減らすはずの.valueOf()もありません) –

+0

私は今slice問題はそれが2-d配列であることにあります。外側の配列をスライスしても差はなく、元の配列は変わりません。しかし、もし私が各サブアレイをスライスすれば、それは動作します!私は外側の要素ごとにループを設定し、そのスライスを新しい配列の外側の要素に割り当てました。 –

答えて

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?

、私はfuncArrayvarを追加しました。そうすることで、グローバル変数ではなく関数のローカルになります。

+0

2番目のコメントのとおり、slice()はループして2次元配列の各外側の要素に適用した場合にのみ動作します。 –

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をたくさん使用していて、基本配列が同じ方法で構築されていないことを再確認しなければなりませんでした。テストサンプルの出力でエラーを特定できませんでした。これで配列のコピー全体が正しく出力されるようになりました。 –

    関連する問題