JavaScriptでは、配列を格納した変数を別の変数にpushした際「参照渡し」になる。
この現象を知らず、配列をpushする実装で小一時間時間を費やしたので、学んだことをメモ。
配列の変数を別の変数に格納すると「参照渡し」になる
pushの話をする前に、大前提として、
JavaScriptでは、配列を格納した変数を別の変数に格納すると「参照渡し」になると言うことを覚えておこう。
配列を代入して参照渡しが起きることを確認してみる
・変数xに配列(1, 2, 3) を格納し、変数yにxを代入する。
・その後、代入されたyの変数の一部を変更する。
・最後にx, y両者の変数の配列を確認する。
//変数xに配列(1, 2, 3) を格納し、変数yにxを代入
var x = [1,2,3];
var y = x;
console.log(y); // [1,2,3]
//yの変数の一部を変更
y[0] = 4;
//x, y両者の変数の配列を確認
console.log(x); // [4,2,3]
console.log(y); // [4,2,3]
変数xの配列の中身(1, 2, 3)は変更していないはずなのに、変更したyと同じ配列の値に変更されてしまっている。これが「参照渡し」の挙動である。
参照渡しは名前の通り、値の”参照”を渡しているので、参照先と参照元で同じ配列を共有するような動きとなるのである。
つまり、xとy、どちらも参照している実態は同じものであるため、
yだけを変更したつもりが、xも変更されてしまっていたと言う現象である。
本題:pushで配列を追加しても参照渡しになる
pushメソッドを使用して配列の変数を追加した際も、参照渡しになるので注意が必要である。
pushメソッドとは
pushメソッドを使うと、配列の末尾に値を追加することができる。
var x = [1,2,3];
x.push(4)
console.log(x); // [1,2,3,4] //末尾に4が追加された
pushメソッドで配列を追加する
pushメソッドで末尾に配列の変数を追加する。
渡した後は、push先の変数は多次元配列になる。
var x = [];
x.push([1,2,3])
console.log(x); // [[1,2,3]] 多次元配列
ここで、例によって、
配列を変数yに格納して、pushメソッドで変数xに追加してみる。
var x = [];
var y = [1,2,3];
x.push(y)
console.log(x); // [[1,2,3]]
そして、変数xの値を変更し、変数x,y両者の配列を確認してみる。
x[0].push(4)
console.log(x) // [[1,2,3,4]];
console.log(y) // [1,2,3,4]
配列を代入した時同様、
pushで追加した場合も、片方の変数の変更で、両方の変更が確認できる。
pushでの参照渡しを避ける方法
pushの際に参照渡しを避けるには、concatメソッドを使用する。
concatメソッドは引数に指定した配列と結合して、新しい配列を返すメソッドだ。
この時、引数が空であれば、新しい配列(空)を作成して結合する。
「空の配列をダミー的に使用して、参照を切った新しい配列を作成してpushする」と言う部分が肝だ。
先ほどの処理を例に、変数yをconcatメソッドを使って変数xにpushしてみる。
var x = [];
var y = [1,2,3];
x.push(y.concat()) // 参照が切られた新しい配列(y+空の配列)がpushされる
x[0].push(4)
console.log(x) // [[1,2,3,4]];
console.log(y) // [1,2,3] <- 影響されていない
変数yへの影響がないことが確認できただろう。
concatメソッドを使用したことにより、値自体は同じだが、全く別の参照先を持つ新しい配列をpushしているので、変数yへの影響を避けることができたのである。
pushの際に参照渡しを避けるには、concatメソッドを使用する。
concatメソッドは引数に指定した配列と結合して、新しい配列を返すメソッド。引数が空であれば、新しい配列(空)を作成して結合する。
JavaScriptでは、配列を格納した変数を別の変数に格納すると「参照渡し」になる