[初学者向け] コアJavaScriptにおける変数の参照について確認してみる


これは [JavaScript Advent Calendar 2014](http://qiita.com/advent-calendar/2014/javascript) の１７日目(12/17)の投稿です。初学者向けの内容です。(ていうか自分が初学者です..)

自分が JavaScript をやり始めたとき、

- 変数に何がはいってるのか、変数の受け渡しでやりとりされるのは何なのか
- 関数の return で返ってくるものが何なのか

が良く分からず困惑したので、その時のことを思い出しながら挙動を再確認してみました。

# 1. 変数へ値を代入した場合の挙動

JavaScript では、変数に型はありませんが、値には **基本型** と **オブジェクト型** の２種類があります。

## A. 基本型

**値そのもの** が渡されます。

### ① 値として「数値」を代入した場合

代入しているのは２行目です。

```javascript:検証用コード
var num1 = 999;
var num2 = num1; // num2 には値そのものが渡されている

num1 = 0; // num1 の値を後から変更

console.log(num1); // 0 が出力される
console.log(num2); // 999 が出力される(num1 の変更の影響を受けていない)
```

### ② 値として「文字列」を代入した場合

代入しているのは２行目です。

```javascript:検証用コード
var str1 = 'あああ';
var str2 = str1; // str2 には値そのものが渡されている

str1 = 'いいい'; // str1 の値を後から変更

console.log(str1); // いいい が出力される
console.log(str2); // あああ が出力される(str1 の変更の影響を受けていない)
```

### ③ 値として「論理値」を代入した場合

代入しているのは２行目です。

```javascript:検証用コード
var flag1 = true;
var flag2 = flag1; // flag2 には値そのものが渡されている

flag1 = false; // flag1 の値を後から変更

console.log(flag1); // false が出力される
console.log(flag2); // true が出力される(flag1 の変更の影響を受けていない)
```

> 基本型には上記以外もありますが割愛。

## B. オブジェクト型

**アドレス(参照)** が渡されます。

### ① 値として「配列」を代入した場合

代入しているのは２行目です。

```javascript:検証用コード
var arr1 = [1, 1, 1];
var arr2 = arr1; // arr2 にはアドレスが渡されている

arr1[0] = 99; // arr1 の中身を後から変更

console.log(arr1); // [99, 1, 1] が出力される
console.log(arr2); // こちらも [99, 1, 1] が出力される(arr1 の変更の影響を受けている)
```

### ② 値として「オブジェクト(key-value)」を代入した場合

代入しているのは２行目です。

```javascript:検証用コード
var obj1 = {name:'山田', age: 32};
var obj2 = obj1; // obj2 にはアドレスが渡されている

obj1.name = '高橋'; // obj1 の中身を後から変更

console.log(obj1); // {name: "高橋", age: 32} が出力される
console.log(obj2); // こちらも {name: "高橋", age: 32} が出力される(obj1 の変更の影響を受けている)
```

### ③ 値として「関数」を代入した場合

代入しているのは４行目です。

```javascript:検証用コード
var func1 = function(){}; // 空の関数を定義
func1.num = 999; // func1 のプロパティ num に値をセット

var func2 = func1; // func2 にはアドレスが渡されている

func1.num = 0; // func1 の中身を後から変更

console.log(func1.num); // 0 が出力される
console.log(func2.num); // こちらも 0 が出力される(func1 の変更の影響を受けている)
```

> オブジェクト型には上記以外もありますが割愛。

# 2. 変数の再定義

上記の例 1-B-①②③ では `arr1[0] = ○○` `obj1.name = ○○` `func1.num = ○○` という風に **中身の要素にアクセスして** 値を変更していました。この場合は、変数 `arr` `obj1` `func1` に格納されている **アドレス(参照)** には何の影響もありません。

ですが、もし `arr1 = ○○` `obj1 = ○○` `func1 = ○○` とすると、それは単に **変数の再定義** なので **アドレス(参照)が新しいものへ上書き** されます。検証用コードを下記に示します。

### ① 配列

```javascript:検証用コード
var arr1 = [1, 1, 1];
var arr2 = arr1; // arr2 にはアドレスが渡されている

arr1 = []; // arr1 に [] へのアドレスが上書きで格納される

console.log(arr1); // [] が出力される
console.log(arr2); // [1, 1, 1] が出力される(arr2 に格納されていたアドレスは維持されている)
```

### ② オブジェクト(key-value)

```javascript:検証用コード
var obj1 = {name:'山田', age: 32};
var obj2 = obj1; // obj2 にはアドレスが渡されている

obj1 = {}; // obj1 に {} へのアドレスが上書きで格納される

console.log(obj1); // {} が出力される
console.log(obj2); // {name: "山田", age: 32} が出力される(obj2 に格納されていたアドレスは維持されている)
```

### ③ 関数

```javascript:検証用コード
var func1 = function(){};
func1.num = 999;

var func2 = func1; // func2 にはアドレスが渡されている

func1 = function(){}; // func1 に function(){} へのアドレスが上書きで格納される

console.log(func1.num); // undefined:未定義エラー(numというプロパティは存在しない)
console.log(func2.num); // 999 が出力される(func2 に格納されていたアドレスは維持されている)
```

こう見ると、JavaScript で変数 `hoge` に `hoge = ○○` と値を代入する際は **基本型とオブジェクト型で、挙動の差異が無い** ということになります。

# 3. 関数の return 値を受け取った際の挙動

> ここでは **配列** の return についてのみ述べますが **オブジェクト(key-value)** 、 **関数** の return についても同じ話です。

配列を return する関数 `rtnArray` を定義し、その関数を用いて `arr1` `arr2` という２つの配列を生成してみます。

```javascript:
// 配列を retrun する関数を定義
function rtnArray(){
  return [1, 1, 1];
}

// 先ほどの関数を用いて配列を生成
var arr1 = rtnArray(); 
var arr2 = rtnArray();

arr1[0] = 99; // １つ目の配列の中身を変更

console.log(arr1); // [99, 1, 1] が出力される
console.log(arr2); // [1, 1, 1] が出力される(つまり、arr1 と arr2 は独立している)
```

つまり、6行目と7行目で次のように書いてある処理は、

```javascript:
arr1 = rtnArray(); 
arr2 = rtnArray();
```

次のように書くのと同義、ということになります。

```javascript:
arr1 = [1, 1, 1];
arr2 = [1, 1, 1];
```

つまり 関数の ruturn を受け取る **変数の再定義** がされています。

ここまで理解すると、次のようなコードで、同じ `count()` という関数を呼び出していても、`conter1` と `counter2` では **別々のメモリ領域にあるローカル変数 `localNum` を参照している** ということが分かります。

```javascript:
// 関数(function)を return する関数を定義
var count = function(initNum) {
  var localNum = initNum;
  return function() {
    return localNum++;
  };
};

// 初期値を 1 として カウンターを作成
var counter1  = count(1);
console.log(counter1()); // 1 が出力される
console.log(counter1()); // 2 が出力される
console.log(counter1()); // 3 が出力される

// 初期値を 9 として カウンターを作成
var counter2  = count(9);
console.log(counter2()); // 9 が出力される
console.log(counter2()); // 10 が出力される
console.log(counter2()); // 11 が出力される

console.log(counter1()); // 4 が出力される(２つの独立したカウンターが実現できている)
```

ちなみに上記の `localNum` は関数(メソッド)経由でしかアクセスできません。 **関数閉包(クロージャ)** と呼ばれるものです。

# おわりに

JavaScript では **変数が何をさしているのか** を頭にいれておかないと、既存のコードを読む際に混乱するので、このあたりは意識していきたいところです。（自戒）

あまり役に立たない記事ですみません.. よろしければこちらもどうぞ。
⇒ [サーバサイドエンジニアでも押えておきたい！コアJavaScriptの基本知識](http://qiita.com/hkusu/items/e06f04e804e130140f17)