配列を空にする方法と注意点(値渡しと参照渡し)【JavaScript】

値渡しと参照渡し

本日、丸一日つまづきました

現在『RPGツクールMV』でゲームを作りつつJavaScriptの勉強も進めているのですが、配列の扱いが思うようにいかず、丸一日頭を悩ませました。

それは、配列を空にする事。

これ、すごく簡単そうに見えて意外な罠があったんです。

先ほどようやく原因が分かったので記事にしてみます!(詳しい人には当然の事だと思うので先に結論を書くと、値渡しと参照渡しの違いについてです)

配列を空にするには

たとえば、以下のような配列を作ったとします。

var array = [0, 1, 2, 3];

この配列を空にする方法はいくつかあるのですが、よく使われるのは以下の二通りだと思います。

array = [];
array.length = 0;

一番目は、新たに空の配列を宣言し直す方法。二番目は、配列の長さを0にしてしまう事で空にする方法ですね。

どちらの方法を使っても、arrayの中身を空にする事ができます。

しかし、次のような配列の場合はどうなるでしょうか?

var array = [0, 1, 2, 3];
var array2 = array;

先ほどと同じように4つの要素を持った配列「array」を、「array2」に代入しています。

これら二つの配列の中身を見てみると……。

array → [0, 1, 2, 3]
array2 → [0, 1, 2, 3]

全く同じ中身の配列が二つできていますね。

さて、それでは「array2」だけを空にしてみましょう。

array2 = [];

結果は……

array → [0, 1, 2, 3]
array2 → []

ちゃんと「array2」だけが空になっていますね!

では、もう一つの方法、「length = 0」ではどうでしょう?

array2.length = 0;

結果は……

array → []
array2 → []

あれれ??

確かに「array2」だけを空にしたはずなのに、「array」まで空になってしまいました。

どちらの方法も配列を空にする方法としてネットでよく紹介されているのに、どうしてこんなに違った結果になってしまうのでしょうか?

値渡しと参照渡し

JavaScriptで変数に何かを代入する時、プリミティブ型なら値渡し、オブジェクト型は参照渡しになるというルールがあります。

プリミティブ型とは、数値、文字列、真偽値(trueとfalse)など。オブジェクト型はプリミティブ型以外のもので、「配列」もオブジェクト型にあたります。

つまり、先ほどの

var array = [0, 1, 2, 3];
var array2 = array;

というコードは「参照渡し」であり、実は array2 に代入されているのは [0, 1, 2, 3] という本来の配列ではなく、「array」に代入された配列への参照(アドレスのようなもの)だったんですね。

なので、「array2」に対して何かを行うと、参照元である「array」にも同じように反映されてしまうわけです。

array2.length = 0;

というコードは、「array2」を通して参照元である配列の長さをゼロにしていたようです。

それとは違って以下の場合だと、

array2 = [];

参照元に働きかけるのではなく、新しく生成した配列オブジェクトを「array2」に代入し直すという処理(new Array()と同様の処理)になります。

この時点で参照元だった配列への参照が切れるので、「array」の中身は変わらずに残っていたというわけです。

 

ところで……そもそもプリミティブ型以外は全てオブジェクト型の参照渡しという事は、一番最初の

var array = [0, 1, 2, 3];

の時点で、「array」の中に配列そのものが入っているわけではなく「配列への参照」を代入している事になりますよね。

その参照を今度は「array2」にも渡しただけという事になりますから、「array」も「array2」も、結局は「同じ配列への参照」を格納しているに過ぎません。

なので、「array2」ではなく「array」の方を空にしてみても……

array = [];
array → []
array2 → [0, 1, 2, 3]

「array2」は残ります。

「array2」に代入されていたのも、「arrayへの参照」ではなく「配列への参照」だったわけですね。

まとめ

今回は理解するのにかなり苦労しましたが、何となくJavaScriptの特徴をつかめてきたような気がします。

とりあえず

  • 参照元配列への参照を切って空にしたい場合は array = [];
  • 参照元配列そのものを空にしたい場合は array.lenght = 0;

といった感じで使い分ければいいんじゃないでしょうか。

値渡しと参照渡しについては日頃から意識しておかないと原因が分からず悩んでしまいそうなので気をつけようと思います。