🧊

JavaScriptの値渡しとか参照渡しとか、プリミティブとかイミュータブルだとか

なんてタイトルにしたら良いのかまったくわからんw

とはいえ、最近のハッとした事例。

var arr1, arr2;
arr1 = arr2 = [];

for (var i = 0; i < 3; i++) {
    arr1[i] = 1;
}
arr1 = [];

console.log(arr1.length); // 0
console.log(arr2.length); // 3

わかってる人にはなんてことないこのコード。
でも、いわゆる「プログラミング」初心者にはこう見える・・。

誤解してる例

// arr1, arr2 という変数を2つ用意し、空の配列をいれる
var arr1, arr2;
arr1 = arr2 = [];

// arr1に値をいれる
// 3回繰り返す
for (var i = 0; i < 3; i++) {
    arr1[i] = 1;
}

// arr1を空の配列にする
arr1 = [];

console.log(arr1.length); // さっき空にしたから当然0
console.log(arr2.length); // arr2には何もしてないから0でしょ? え?3?!

なにを誤解してるのか

// arr1, arr2 という変数を2つ用意し、空の配列をいれる
var arr1, arr2;
arr1 = arr2 = [];

まずコレ。
正しくは、arr1, arr2 という変数を2つ用意し、"単一の"配列を参照する

// arr1に値をいれる
// 3回繰り返す
for (var i = 0; i < 3; i++) {
    arr1[i] = 1;
}

コレも正しくは、arr1に値を入れているのではなく、
arr1が"参照している配列に"、値が格納される

// arr1を空の配列にする
arr1 = [];

あとコレ。
正しくは、arr1の"参照先"を、新たな配列に向ける

console.log(arr2.length); // arr2には何もしてないから0でしょ? え?3?!

よって、arr2は以前3回ループした配列を参照してるので、3。

ちなみに

まあObjectでやっても同じ。

var obj1, obj2;
obj1 = obj2 = {};

for (var i = 0; i < 3; i++) {
    obj1[i] = 1;
}
obj1 = {};

console.log(obj1); // {}
console.log(obj2); // {0: 1, 1: 1, 2: 1}

というか配列はそもそもObject(型)なので。

その誤解してる理論でいくと

var str1, str2;
str1 = str2 = 'str'

str1 = null;

console.log(str1); // null
console.log(str2); // str

なぜstr2がnullにならないのか。

これはJavaScriptのデータ型によって挙動が違って、

  • Number
  • String
  • Boolean
  • Null
  • Undefined

にはこの理論が当てはまらなくて、

  • Object

にだけ、当てはまるから。

正確には、

  • Number
  • String
  • Boolean
  • Null
  • Undefined

これらはプリミティブ値で、値が格納されるタイプ。
どっかで聞いた単語を充てるなら、イミュータブル。

  • Object

こいつは値ではなく参照が格納されるタイプ。
どっかで聞いた単語を充てるなら、ミュータブル。

プリミティブ値でイミュータブルなので

var str1, str2;
str1 = str2 = 'str'; // str1 に 'str'、str2 にも 'str' を格納

str1 = null;

console.log(str1); // null
console.log(str2); // str2は最初に'str'を代入してから何もしてないので、 'str'

うーん、初心者的にはそんなこと言われてもまったくピンとこないシリーズよねー。
そもそも個人的にも、これをちゃんと言葉で説明するとどうなるのか、よくわかってないんですよねー・・。

プリミティブとかイミュータブルとか、そもそもそれがわかりにくいというかなんというか。
Objectのソレを参照渡しって言えるっぽいけど、果たして本当の”参照渡し”なのだろうかと考えだすともうね!