4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

知らないとはまってしまうJavaScriptの配列コピー

Last updated at Posted at 2020-01-04

概要

JavaScriptで配列コピーをする際、
「=(イコール)」によるコピーでは意図した動作にならない場合があります。
意外にはまりやすい落とし穴だと思ったので、
初心者にもわかりやすいよう、図も載せておきました。

配列コピーによる動き

まずは、動きを確認しましょう。

変数aの配列を宣言し、変数bにコピーします。

test.js
var a = ['','','','',''];
var b = a;

その後、変数b[0]の内容を書き換えます。

test.js
b[0] = '';

この時、書き換えたいのは変数bの値ですが実際は

test.js
  console.log('変数a: ' + a); // ['か','い','う','え','お']
  console.log('変数b: ' + b); // ['か','い','う','え','お']

と、変数aの値も書き換わってしまいます。
書き換えたのは変数bだったのに、なぜ変数aも一緒に書き換わってしまったのでしょうか。

コピー元の値が書き換わる理由

では、なぜ変数bの値を書き換えたにも関わらず、変数aの値も書き換わってしまったのか
図を交えて説明していきます。

まず、この変数aに格納されている値は、配列の情報ではなく、
配列aのインスタンスを参照するアドレスが格納されています。

その為、変数bに変数aをコピーした際、
配列ではなくアドレスの情報が渡されているのです。

実際に図にすると、以下の状態です。
配列コピー.jpg

次に、変数b[0]に「か」という文字を代入しましたが、
この時、変数b[0]は変数bの値を書き換えるのではなく、
配列の値を保持したメモリを直接書き換えることになります。

配列値変更.jpg

つまり、このb=aという式では、配列は複製されず
同じアドレス先を共有しているだけなのです。

この動作を理解していないと、
まるで両方の変数で値が書き換わったように感じてしまうのです。

配列を複製する方法

では、実際に配列を複製するためにはどうすればいいか、いくつか例を挙げておきます。
※上記の説明でコピーという表現が適切ではないと分かったので、ここからは「複製」という表現に変えます

for文による複製

for文を使って、配列要素文のループにより値を代入します。
この時、変数bは空の配列で宣言します。

test.js
  var a = ['','','','',''];
  var b =[];

  for( var i =0; i < a.length; i++){
    b[i] = a[i];
  }

  b[0] = '';
  console.log('変数a: ' + a); // ['あ','い','う','え','お']
  console.log('変数b: ' + b); // ['か','い','う','え','お']

Array.concat()による複製

Arrayオブジェクトのconcatメソッドは指定された配列を連結して値を返すメソッドですが
引数を指定しない場合は、元の配列の複製を返します。

test.js
  var a = ['','','','',''];
  var b = a.concat();

  b[0] = '';
  console.log('変数a: ' + a); // ['あ','い','う','え','お']
  console.log('変数b: ' + b); // ['か','い','う','え','お']

Array.slice()による複製

Arrayオブジェクトのsliceオブジェクトは配列の一部を取り出して値を返すメソッドですが、
開始を指定し、終了を省略すると元の配列の複製を返します。

test.js
  var a = ['','','','',''];
  var b = a.slice(0);

  b[0] = '';
  console.log('変数a: ' + a); // ['あ','い','う','え','お']
  console.log('変数b: ' + b); // ['か','い','う','え','お']

...演算子による複製

ES6(ES2015)で追加されたスプレッド構文を使用すると、配列をそのまま複製してくれます。

ただし、ES6をサポートしたブラウザのみ対応です。
※ 2020年1月時点では、IE11は非サポートです。

test.js
  var a = ['','','','',''];
  var b = [...a]

  b[0] = '';
  console.log('変数a: ' + a); // ['あ','い','う','え','お']
  console.log('変数b: ' + b); // ['か','い','う','え','お']

まとめ

「=(イコール)」を使って変数の値をコピー(代入)する際には
その変数がなんの値を保持しているのか注意しましょう。

4
3
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?