🏥 Javascriptの値渡し

2021/9/09

🏥 Javascriptの値渡し  Javascript で Web アプリケーションの Undo 機能を作るに際し、すぐに思いつくのは使用している変数を手順ごとの配列として保存し、手順 n を増やしたり、減らしたりして、過去や現在の場面を再現することでしょう。 このとき、非常に変なことが起こります。 つまり、過去のゲームデータを保存したはずの配列が時とともに変化し、全く過去を再現してくれないのです。


 調べていくと、それは Javascript のコピーが配列レベルになると値渡しではなく、参照渡しであることからきているのに気づきました。 「エッ」といった感じです。 私は値渡しがメインで、参照渡しは例外処理という言語がふつうだと勝手に思いこんで、気づくのが遅れました。 例えば、Perl で次のように書いたとしましょう。

my @ary1 = (1, 2);
my @ary2 = @ary1;
$ary2[0] = 9;
$, = ",";
print join(",", @ary1) . "\n";
print join(",", @ary2) . "\n";

 出力は、

1,2
9,2

となります。 やっていることは、配列 ary1 をセットし、ary2 に普通にコピーし、ary2 の第一要素に 9 を代入し、みやすいように , をいれて出力したというだけです。 ary1 と ary2 は中味が違ってきて、まあそうなるよねということです。

 が、同じことを Javascript でやってみましょう。

let ary1 = [1, 2];
let ary2 = ary1;
ary2[0] = 9;
console.log(ary1.join(','));
console.log(ary2.join(','));

 出力は

9,2
9,2

となってしまうのです。 今は ary2 の値を変えましたが、ary1 の値を変えても同じことが起こります。 そういうことなのです。 今まで、大きな配列をコピーするというような事態に遭遇しなかったためにこれに気づかなかったのは幸せなのか、不幸なのかわかりません。 ともかく、今回 Javascript の秘密を知ってしまったわけです。

 これを切り抜けるには、コピー専用の関数を書くか、JSON.parse, JSON.stringify を使うしかないようです。 しかし、配列の要素がきちんとした数字か文字であるならば、JSON の方を使うのが無難です。

function copy(val) {
  const val2 = JSON.parse(JSON.stringify(val));
  return val2;
}

という関数を用意し、これを使って、

let ary1 = [1, 2];
let ary2 = copy(ary1);

とやれば、出力は、

1,2
9,2

となってくれます。 ary1, ary2 が多次元配列だろうが、連想配列だろうがだいじょうぶです。 ただ、十分注意せず、手間をはぶいて配列をコピーしたなら、そのコードに謎のバグが入りこむ恐れが常にあります。 配列などコピーしないよという方をのぞき、くれぐれもご用心を。

参考: 【JavaScript】 参照渡し?値渡し?

twitterシェア Facebookシェア
  コメントの注意点