uhyohyo.net

JavaScript初級者から中級者になろう

一章第三回 配列のコピー

このページの最終更新日:

今回はオブジェクトの扱い方について、配列のコピーを例にとって説明します。前回説明したように、オブジェクトを正しく扱うにはその特徴を正しく理解する必要があります。今回は練習問題みたいなものなので、ぜひ理解して次に進んでください。

配列のコピーとは

配列のコピーとは、読んで字のごとく、配列を複製することです。

しかし、前回解説した通り、配列のオブジェクトの一種なので、以下のようにしただけでは、結局同じ配列が複数の変数に入っている状況になります。

var a = [0,1,2,3,4];
b=a;
a[5]=5;
alert(a);
alert(b);

このコードでは、変数bにaを代入してからaを変更すると、bの中身も同じように変更されてしまっています。これは、aとbに入っているのが同じオブジェクトだからです。

そこで、ちゃんと別のオブジェクトとして新しい配列を作らないと、配列をコピーしたとはいえません。

今回は、ちゃんと新しいオブジェクトを作って配列をコピーしてみましょうという、今までの知識を利用した演習的な回です。

とりあえず、配列をコピーする関数を作りましょう。引数がコピー元の配列で、戻り値が複製された配列というようにします。

function copyArray(arr){
}

「copyArray」が関数名で、引数が「arr」ですね。「Array」とは、配列のことです。ちなみに、関数呼び出しのときに引数としてオブジェクトを渡した場合も、オブジェクトはコピーされません。渡されたのと同じオブジェクトが引数変数に入ります。

正しく配列のコピーを実装するには、新しい配列を作る必要があります。まずはとりあえず新しい配列を作りましょう。新しい配列を作るには、new Array()のようにするか、配列リテラルを使えばよいのでした。配列リテラルを使うほうが一般的ですから、ここでもそれを使うことにします。

function copyArray(arr){
  var newarr = [];
}

結論からいうと、この新しい配列に要素を1つずつコピーしていけばいいのです。まず、配列の要素1つずつに処理するときはどのようにしましたか? そうです。繰り返しの構文を使います。

function copyeArray(arr){
  var newarr = [];
  for(var i = 0;i<arr.length;i++){
  }
}

このようにfor(やwhile)を使うのでした。この形で、iが0から最後の要素の添字まで増加するのを利用してひとつずつ処理します。

処理の中身は簡単です。元の配列のその番号の要素を、新しい配列の同じ番号の要素に代入するだけです。

function copyArray(arr){
  var newarr = [];
  for(var i = 0;i<arr.length;i++){
    newarr[i] = arr[i];
  }
}

最後に、できた配列を戻り値として返します。

function copyArray(arr){
  var newarr = [];
  for(var i = 0;i<arr.length;i++){
    newarr[i] = arr[i];
  }
  return newarr;
}

できたら、実際に使ってみましょう。

function copyArray(arr){
  var newarr = [];
  for(var i = 0;i<arr.length;i++){
    newarr[i] = arr[i];
  }
  return newarr;
}
var a = [0,1,2,3,4];
alert(a);
var b = copyArray(a);
alert(b);

a[5]=5;
alert(b); 

アラートが3回表示されます。

まず最初にcopyArray関数をつくり、次の行でaに[0,1,2,3,4]を代入しています。

そして、次の行でそのaを表示します。ちなみに、アラートで配列変数を直接表示すると、丁寧に要素が「,」で区切られて表示されます。

そして、次の行でbにcopyArray関数でaと同じ配列を代入しています。アラートでbを表示すると、正常にコピーされていることがわかります。

その後、a[5]に5を代入しています。この時点でaは、[0,1,2,3,4,5]になることが分かると思います。その後bを表示すると、aを変えてもbには影響がないことが分かります。

copyArray関数は内部で作成された新しい配列を返しますから、変数bに入るのは明らかにaとは別のオブジェクトです。

copyArrayを使わない以下の場合と比べてみましょう。

function copyArray(arr){
  var newarr = [];
  for(var i = 0;i<arr.length;i++){
    newarr[i] = arr[i];
  }
  return newarr;
}
var a = [0,1,2,3,4];
alert(a);
var b = a;
alert(b);

a[5]=5;
alert(b);

のようにした場合、bにはaと同じオブジェクトが代入されるだけです。

その結果、a[5]に5を代入したときに、bを表示してもaへの変更が反映されてしまっています。aとbは同じオブジェクトを指しているのだから、当然のことです。

今回はcopyArray関数を作ることを通してオブジェクトの扱い方を見ました。オブジェクトを作成するとか、オブジェクトが同じであるとかいう概念を理解することが肝心です。

余談ですが、今回やったような配列のコピーは、ES2015という比較的新しい記法で次のように書けます。


    var b = [...a];
  

これでvar b = copyArray(a);と同じ意味になります。

昔(最初のこのページを書いたとき)はこんなの無かったのですが、便利な世の中になりましたね。

余談その2ですが、今回実装したcopyArray関数では、配列の各要素をnewarr[i] = arr[i];により行っています。懸命な読者はお気づきかもしれませんが、もしarr[i]がオブジェクトだったら、それと同じオブジェクトが新しい配列に入ることになります。

つまり、配列を表面上コピーしても、その中身のオブジェクトは同じであるということです。これが良いのか悪いのかは、場合によりけりです。中身のオブジェクトも全て新しく作るような複製も可能です。いわゆる再帰関数を作ることになるでしょう。少し難しいですが、興味があれば挑戦してみてください。