javascript:参照とコピー

参照とコピー

インターネットで調べると、jsにおいて参照渡しとコピーがあると明言されているものが多いですが、実際にはすべて参照渡しです。参照渡しとコピーで話をしたほうが理解が容易(振る舞いを覚えやすい)ということがありますが、このページでは正しい認識(参照のみで説明)をもつことを目指します。

まずは以下のコードを読んで実行結果を把握してください。

var a = 1; // aという変数がメモリ上のどこかに確保され、変数1がメモリ上のどこかに確保される。aには1への参照先が保存されている。
var b = 2; // bという変数がメモリ上のどこかに確保され、変数2がメモリ上のどこかに確保される。bには2への参照先が保存されている。
a = b;     // aはbの参照先である2を参照するように変更。結果としてaにbの値を代入したように見える。a->2, b->2
console.log(a); // 2

var oa = {id:1}; // oaという変数がメモリ上のどこかに確保され、変数(細かくはオブジェクト型){id:1}がメモリ上のどこかに確保される。oaには{id:1}への参照先が保存されている。
var ob = {id:2}; // obという変数がメモリ上のどこかに確保され、変数(細かくはオブジェクト型){id:2}がメモリ上のどこかに確保される。obには{id:2}への参照先が保存されている。
oa = ob;         // oaはobの参照先である{id:2}を参照するように変更。結果としてoaにobの値を代入したように見える。oa->{id:2}, ob->{id:2}
console.log(oa); // {id: 2}

長いコメントが付いていますが、よく読んで理解してください。上記のメモリ空間での話を理解しやすいように図も示しておきました。

では次のコードと実行結果をよく読んでみてください。

var a = 1;
var b = 2;
a = b; // aの参照先がbの参照先、つまり2への参照となる a->2, b->2
b = 3; // bの参照先が2から3の場所へ変更された。aには影響なし。なぜならaはbを参照しているのではなく、2を参照しているから。a->2, b->3
console.log(a); // 2

var oa = {id:1};
var ob = {id:2};
oa = ob;   // oaの参照先がobの参照先、つまり{id:2}への参照になる。oa->{id:2}, ob->{id: 2}
ob.id = 3; // ob.id、つまり {id: 2} のidの参照先が新しくメモリ上に確保された3になる。oaは{id: 2}を参照しているので、oa.idも3を参照することになる
console.log(oa); // {id: 3}

ポイントは、最後のoaに関してob.idで変更したはずの値がoa.idにも反映されてしまっている、という点です。コメントで説明している通りですが、参照先の値が変更された為、oa.idも変更した値になっている。ということです。例えばob.idは3で、oa.idはob.idの初期値である2のままにしたかった場合は、次のようなコードになります。

var oa = {id:1};
var ob = {id:2};
oa = Object.assign({},ob);
ob.id = 3;
console.log(oa); // 結果:{id: 2}

このようにすることで、oaの参照先をobの初期値である{id:2}にはせず、新しく作成した{id:2}となるため、ob.idで値を変更してもoa.idが変更されることはありません。

インターネットに流布している「値渡しはコピー、オブジェクト私は参照」という考え方の根っこはどれも参照渡しになっていることの結果に過ぎません。変数に変数を代入する場合は、代入する変数が参照している先を代入される変数に渡しているわけです。ただしコピーもとのObjectのデータ構造が2層、3層と深くなっている場合、結局参照先を見に行ってしまうので、一旦JSONに変換してから改めてデータ構造を作り直すようにするのが間違いないです。

var oa = {id:1};
var ob = {id:2};
oa = Object.assign({},JSON.parse(JSON.stringify(ob)));
ob.id = 3;
console.log(oa); // 結果は先程と同じく {id: 2}

では理解ができたところで、少し練習してみましょう。

var inputs = {x:1,y:2}
var a = inputs;
inputs.x = 10;
inputs.y = 11;
var b = inputs;
console.log(a, b);

上記のコードを修正して、a = {x:1,y:2}, b = {x:10, y:11}となる結果に変更してください。ただしa,bへの代入には必ずinputs変数を用いてください。b = {x:10, y:11}等として直接代入してはいけません。

【正解】

var inputs = {x:1,y:2}
var a = Object.assign({}, JSON.parse(JSON.stringify(inputs)));
inputs.x = 10;
inputs.y = 11;
var b = inputs;
// 後の処理を考えて、bもaと同様にObjectをコピーするようにしておくとよいでしょう。
// var b = Object.assign({}, JSON.parse(JSON.stringify(inputs)));
console.log(a, b);
  • /home/users/2/lolipop.jp-4404d470cd64c603/web/ws/data/pages/javascript/参照とコピー.txt
  • 最終更新: 2022/12/20 09:16
  • by baba