JavaScriptで配列を変更すると、配列のコピーに影響するのはなぜですか?


83

私は次のJavaScriptを作成しました。

var myArray = ['a', 'b', 'c'];
var copyOfMyArray = myArray;
copyOfMyArray.splice(0, 1);
alert(myArray); // alerts ['b','c']
alert(copyOfMyArray); // alerts ['b','c']

var myNumber = 5;
var copyOfMyNumber = myNumber;
copyOfMyNumber = copyOfMyNumber - 1;
alert(myNumber); // alerts 5
alert(copyOfMyNumber); // alerts 4        

このコードは変数myArrayを宣言し、それを配列値に設定します。次に、2番目の変数copyOfMyArrayを宣言し、に設定しmyArrayます。で操作を実行してから、copyOfMyArrayとの両方に警告myArraycopyOfMyArrayます。どういうわけか、で操作を実行copyOfMyArrayすると、で同じ操作が実行されているように見えますmyArray

次に、コードは数値を使用して同じことを行いmyNumberます。変数を宣言し、数値に設定します。次に、2番目の変数copyOfMyNumberを宣言し、に設定しmyNumberます。で操作を実行してから、copyOfMyNumberとの両方に警告myNumbercopyOfMyNumberます。ここでは、私が期待される動作を得るための:異なる値myNumberとしますcopyOfMyNumber

JavaScriptの配列と数値の違いは、配列を変更すると配列のコピーの値が変更されるように見えるのに対し、数値を変更しても数値のコピーの値は変更されないということです。

どういうわけか、配列は参照で参照され、数値は値で参照されていると思いますが、なぜですか?他のオブジェクトでどのような動作が予想されるかをどのように知ることができますか?

回答:


112

JavaScriptの配列もオブジェクトであり、変数はオブジェクト自体ではなく、オブジェクトへの参照のみを保持します。したがって、両方の変数は同じオブジェクトへ参照を持っています。

数字の例との比較は正しくありません。に新しい値を割り当てますcopyOfMyNumber。新しい値を割り当ててcopyOfMyArrayも変更されませんmyArray

slice [docs]を使用して配列のコピーを作成できます:

var copyOfMyArray = myArray.slice(0);

ただし、これは浅いコピーのみを返すことに注意してください。つまり、配列内のオブジェクトは複製されません。


+ 1-好奇心からmyArray.slice(0);、そのコンテキストで直接割り当てることによる欠点はありますか?
jAndy 2011

2
@Rice:いいえ、明確にするために編集するだけです。深いコピーが必要な場合は、自分で何かを書く必要があります。しかし、これを行うスクリプトが見つかると確信しています。
Felix Kling 2011

@FelixKling:例はありません。最初にプロトタイプメソッドを適用したので、私はただ尋ねていました。
jAndy 2011

@jAndy:ああ、あなたはそれについて言及しました...私は少し混乱していて、最近は逆のことがよくあります;)
Felix Kling 2011

23

考えられる唯一の答え、そして正しい答えは、実際にはアレイをコピーしてないということですあなたが書くとき

var copyOfArray = array;

同じ配列への参照を別の変数に割り当てています。つまり、両方が同じオブジェクトを指しています。


正確に参照ポインタを割り当てているのではなく、参照のコピーのように割り当てていると思います。objを関数に渡して、関数内の別の新しいオブジェクトに置き換えようとすると、元のオブジェクトは変更されません。
kashesandr 2017年

1
@kashesandrはい、「参照の割り当て」は「参照のコピーの割り当て」を意味します、それは本当です。ただし、数の2つのインスタンスが常に等しいのと同じように、2つの等しい参照は常に等しくなります5
とがった2017年

16

ですから、ここにいる全員が、なぜこれが起こっているのを説明する素晴らしい仕事をしました-私はただ一線を引いて、これをどのように修正できたをあなたに知らせたかっただけです-非常に簡単です:

thingArray = ['first_thing', 'second_thing', 'third_thing']
function removeFirstThingAndPreserveArray(){
  var copyOfThingArray = [...thingArray]
  copyOfThingArray.shift();
  return copyOfThingArray;
}

これは... spread構文を使用しています。

構文ソースの拡散

編集:これの理由について、そしてあなたの質問に答えるために:

JavaScriptの配列と数値の違いは、配列を変更すると配列のコピーの値が変更されるように見えるのに対し、数値を変更しても数値のコピーの値は変更されないということです。

答えは、JavaScriptでは、配列とオブジェクトは可変ですが、文字列と数値、およびその他のプリミティブは不変であるということです。次のような割り当てを行う場合:

var myArray = ['a', 'b', 'c']; var copyOfMyArray = myArray;

copyOfMyArrayは、実際にはmyArrayへの単なる参照であり、実際のコピーではありません。

この記事をお勧めします。不変および可変のデータ構造とは何ですか?、主題をより深く掘り下げるために。

MDN用語集:可変


2
まさに私が探していたもの。
Fabien TheSolution 2017

6

オブジェクトのクローン作成-

Aloop / array.pushは、array.slice(0)またはと同様の結果を生成しarray.clone()ます。値はすべて参照によって渡されますが、ほとんどのプリミティブデータ型は不変であるため、後続の操作で目的の結果( 'クローン')が生成されます。もちろん、これは元の参照の変更を可能にするオブジェクトや配列には当てはまりません(これらは可変タイプです)。

次の例を見てください。

const originalArray = [1, 'a', false, {foor: 'bar'}]
const newArray = [];

originalArray.forEach((v, i) => {
    newArray.push(originalArray[i]);
});

newArray[0] = newArray[0] + 1;
newArray[1] = 'b';
newArray[2] = true;
newArray[3] = Object.assign(newArray[3], {bar: 'foo'});

newArrayインデックスで実行される操作はすべて、最終(オブジェクト)を除いて、目的の結果を生成します。これは、参照によってコピーされるため、originalArray [3]も変更します。

https://jsfiddle.net/7ajz2m6w/

array.slice(0) and array.clone()これと同じ制限があることに注意してください。

これを解決する1つの方法は、プッシュシーケンス中にオブジェクトを効果的に複製することです。

originalArray.forEach((v, i) => {
    const val = (typeof v === 'object') ? Object.assign({}, v) : v;
    newArray.push(val);
});

https://jsfiddle.net/e5hmnjp0/

乾杯


3

JSでは、演算子 "="がポインタを配列のメモリ領域にコピーします。配列を別の配列にコピーする場合は、クローン関数を使用する必要があります。

整数はプリミティブ型であるため、異なります。

S。


1

プリミティブデータ型(文字列と数値IIRC)を除いて、すべてが参照によってコピーされます。


それは真実ではありません。すべての割り当ては参照を割り当てます。文字列と数値は不変です。
SLaks 2011

1

コピーがありません。
同じ配列を保持する複数の変数があります。

同様に、同じ数を保持する複数の変数があります。

を書くときはcopyOfMyNumber = ...、変数に新しい数値を入れています。
それは書くようなものcopyOfMyArray = ...です。

を書くときは、元の配列を変更しているcopyOfMyArray.spliceことになります。 数字は不変であり、変更できないため、これは数字では不可能です。


1

arrayCopyに元の配列のフィルターを作成します。そのため、新しいアレイへの変更は元のアレイに影響を与えません。

var myArray = ['a', 'b', 'c'];
var arrayCopy = myArray.filter(function(f){return f;})
arrayCopy.splice(0, 1);
alert(myArray); // alerts ['a','b','c']
alert(arrayCopy); // alerts ['b','c']

それが役に立てば幸い。


1

シャローコピーの問題は、すべてのオブジェクトが複製されず、代わりに参照を取得することです。したがって、array.slice(0)はリテラル配列でのみ正常に機能しますが、オブジェクト配列ではシャローコピーを実行しません。その場合、1つの方法は..

var firstArray = [{name: 'foo', id: 121}, {name: 'zoo', id: 321}];
var clonedArray = firstArray.map((_arrayElement) => Object.assign({}, _arrayElement));  
console.log(clonedArray);
// [{name: 'foo', id: 121}, {name: 'zoo', id: 321}]  // shallow copy

0

ケースに応じてエラー処理を追加し、次の関数のようなものを使用して問題を解決できます。バグ/問題/効率のアイデアについてコメントしてください。

function CopyAnArray (ari1) {
   var mxx4 = [];
   for (var i=0;i<ari1.length;i++) {
      var nads2 = [];
      for (var j=0;j<ari1[0].length;j++) {
         nads2.push(ari1[i][j]);
      }
      mxx4.push(nads2);
   }
   return mxx4;
}

-1

配列、またはjavascriptのオブジェクトは、複製またはコピーしない限り、常に同じ参照を保持します。ここに例があります:

http://plnkr.co/edit/Bqvsiddke27w9nLwYhcl?p=preview

// for showing that objects in javascript shares the same reference

var obj = {
  "name": "a"
}

var arr = [];

//we push the same object
arr.push(obj);
arr.push(obj);

//if we change the value for one object
arr[0].name = "b";

//the other object also changes
alert(arr[1].name);

オブジェクトクローンの場合、jqueryおよびangular.copy()で.clone()を使用できます。これらの関数は、他の参照を使用して新しいオブジェクトを作成します。それを行うためのより多くの機能を知っているなら、教えてください、ありがとう!

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.