今度こそ理解できる！　JavaScriptの参照が完全に分かる記事

## 仕事でJavaScriptを書くワイ

```js
const arr1 = [1, 2, 3];

// (中略)

const arr2 = arr1;
arr2.push(4);

// （中略）

console.log(arr1); // [1, 2, 3, 4]
```

ワイ「何や、**配列を書き換えたら別の変数に入れた配列も一緒に変化した**で！　**JavaScriptのバグ**やな！」

上司「**バグってんのはお前の頭**やろ！　もう一回勉強し直さんかい！」

※この記事は全編[やめ太郎さん](https://qiita.com/Yametaro)リスペクトでお送りする2次創作的な記事です。

## 参照について調べるワイ

ワイ「なんや調べたら**参照**がどうのとか言ってる記事が出てきよるで……。よく分からんから周りの人に聞いてみよか」

同僚「参照ってのはオブジェクトのことやで（**大嘘**）」

上司「JavaScriptの関数引数は値渡しと参照渡しがあるんや（**大嘘**）」

[ハスケル子](https://qiita.com/Yametaro/items/11dcd3ec26027ff30214)「参照っていうのは書き換え可能なレコードのことですよ」[^ml_refs]

[^ml_refs]: ハスケル子「書き換え不可能な変数を原則とする関数型言語では、書き換え可能な変数を**参照**と呼ぶんです。参照があると関数型言語の魅力が台無しですよ」　ワイ「JavaScriptと関係ないやないかい！」

ワイ「言ってることがバラバラで何の参考にもならんわ！」

ワイ「仕方ないから根気よく**ネット検索**で調べるとするで」

## 一番頼りになるものは

（3時間後）

ワイ「いくら調べても**クソみたいな解説サイト**しか出てこないで……」

ワイ「結局**参照**ってのは何なんや」

ハリー先輩「お困りのようだな」

ワイ「**[ハリー先輩](https://qiita.com/Yametaro/items/831d23536ae1310447e6)**！　**[ワイが再就職しても付いてきてくれたハリー先輩](https://qiita.com/Yametaro/items/2eaa6fd75255c8c2a2bb)**じゃないでっか！」

ハリー先輩「こういうときに役立つのが**仕様書**や」

ハリー先輩「**仕様書**は**JavaScriptとは何かを定義する文書**で、**JavaScriptに関する最も正確な情報源**なんや」

ワイ「仕様書がJavaScriptっていう言語を決めとるんやから、間違った情報が載っとったらおかしいっちゅうわけでんな」

ハリー先輩「ちなみに仕様書はJavaScriptのことを**ECMAScript**って呼んどるけど、**実質同じだから気にせんでもええ**で」

ワイ「ハリー先輩、『JavaScript 仕様書』で検索しても仕様書なんて出てきまへんで」

ハリー先輩「**仕様書は英語で書かれとる**んやから英語で検索するんや。『JavaScript specification』やな」

ハリー先輩「今の最新版はこれや。仕様書は年に1回発行されるんやけど、これは2018年6月に発行された**ES2018**やな」

- [ECMAScript® 2018 Language Specification](https://www.ecma-international.org/ecma-262/9.0/index.html)

## 仕様書で参照を探す

ワイ「いや、仕様書談義で忘れるところやったわ」

ワイ「ワイは**参照**が分からなくて困ってたんでっせ」

ハリー先輩「せやったな、じゃあ**仕様書**から**参照**について書いてあるところを探すで。参照は**reference**や」

ハリー先輩「仕様書は左上に検索欄があって検索しやすいようになっとるから活用しいや」

ハリー先輩「あったで」

[6.2.4 The Reference Specification Type](https://www.ecma-international.org/ecma-262/9.0/index.html#sec-reference-specification-type)

> A Reference is a resolved name or property binding. A Reference consists of three components, the base value component, the referenced name component, and the Boolean-valued strict reference flag. The base value component is either undefined, an Object, a Boolean, a String, a Symbol, a Number, or an Environment Record. A base value component of undefined indicates that the Reference could not be resolved to a binding. The referenced name component is a String or Symbol value.
>
A Super Reference is a Reference that is used to represent a name binding that was expressed using the super keyword. A Super Reference has an additional thisValue component, and its base value component will never be an Environment Record.

ワイ「なるほど、**完全に理解した**で」

ハリー先輩「は？」

ワイ「いや、**よその作者さんのキャラクター**やから**アホキャラにするのは申し訳ない**んや」

ハリー先輩「（どこ向いて喋っとるんやこいつ……）」

ハリー先輩「さよか、じゃあ仕事に戻るわ」

ワイ「（仕様書を完全に理解できるのはこの記事の独自設定なんでご理解のほどよろしく頼むで）」

ワイ「なになに、要するに**参照**は**変数名**とか**プロパティ名**を表す概念なんやな」

ワイ「**代入**とかの時にどこを書き換えればええか指し示すのが**参照**なんや」

ワイ「まずは変数の場合を考えてみるで」

```js
let x = 3, y = 10;

x = y / 2;

console.log(x); // 5
```

ワイ「これは変数`x`と`y`を定義して`x`に`y / 2`を代入するプログラムやな」

ワイ「`y / 2`は`y`に`10`が入っとるから`10 / 2`になってそれを計算するから`5`や」

ワイ「こういう風に変数の中身を持ってきたり式を計算したりすることを**評価** (*evaluation*) と呼ぶで」

ハスケル子「じゃあ`x = y / 2`を評価すると`x`が`3`で`y`が`10`だから`3 = 10 / 2`ですね」

ワイ「んなわけあるかい！　`x = y / 2`は**代入式**やからこれは変数`x`に`y / 2`を代入するっちゅう意味や」

ハスケル子「わかりました、代入演算子`=`は右だけ評価して左は評価しないから最終的に`x = 5`になるんですね」

上司「**ちゃうで**」

ワイ「うおっ！（ワイの出番を奪いよって！　おとなしく茶でも飲んどけや、[タボが！](https://qiita.com/Yametaro/items/dfac23ed9405b1e609b1)）」

上司「こういうプログラムを考えてみるんや」

```js
const obj = { name: "foo" };

function f() {
  return obj;
}

f().name = "bar";

console.log(obj.name); // bar
```

ワイ「今回の代入式は`f().name = "bar"`でんな」

上司「`f()`の返り値は`obj`やから、これは`obj.name`に`"bar"`を代入してることになるで」

上司「だから最後の`obj.name`は`"bar"`や」

ワイ「`f()`という関数呼び出しを実行するのも**評価**やねん」

ワイ「つまり`=`の左もちゃんと**評価**されとるっちゅうわけやな」

ハスケル子「今度こそ分かりました。`=`の左の`x`とか`f().name`を**評価**した結果が**参照**なんですね」

ワイ「せや」

ワイ「代入演算子`=`っちゅうのは、**左辺を評価して得た参照**に**右辺を評価して得た値**を**代入**する働きをするんや」

上司「`x = y / 2`はまず左辺の`x`を評価して**`x`への参照**が得られるで」

ワイ「右辺を評価すると`5`やから、**`x`への参照**に`5`が代入されることによって変数`x`の中身が`5`になるんや」

ハスケル子「`f().name = "bar"`の場合は`f().name`が**評価**されて**`obj.name`への参照**になるんですね」

ワイ「その通り」

ワイ「`x`みたいに単体の変数はそのまま`x`への**参照**に評価されて、`obj.name`とか`f().name`みたいな**プロパティアクセス**の式は**プロパティへの参照**に評価されるで」

## 変数は常に参照に評価される

ハスケル子「でも変数の**評価**が**都合良すぎ**じゃないですか？」

ワイ「どういうことや？」

ハスケル子「`x = y / 2`の`x`は**`x`への参照**に評価されるんですよね」

ワイ「せやな」

ハスケル子「でも`y`は中身が`10`なので`10`に評価されてますよね」

ハスケル子「`x`も`y`も変数なのに何で片方は**参照**で片方は**変数の中身**に評価されるんですか？」

ワイ「ええ質問やな」

ワイ「実は**変数は常にその変数への参照に評価される**んや」

ワイ「**仕様書**のここに書いてあるで」

[12.1.6 Runtime Semantics: Evaluation](https://www.ecma-international.org/ecma-262/9.0/index.html#sec-identifiers-runtime-semantics-evaluation)

> *IdentifierReference* : *Identifier*
    1. Return ? ResolveBinding(StringValue of *Identifier*).

ワイ「*Identifier*が`x`とかの変数のことなんやけど」

ワイ「その評価結果は ResolveBinding(StringValue of *Identifier*) や」

ワイ「ResolveBindingは[8.3.2 ResolveBinding (name, [env])](https://www.ecma-international.org/ecma-262/9.0/index.html#sec-resolvebinding)に書いてあるで」

ワイ「細かいことは省略するけど、最後にこう書いてあるからまあ参照を返すんやろなあってことが分かるで」

> 4​. Return ? GetIdentifierReference(env, name, strict).

ハスケル子「つまり`y / 2`の`y`も**`y`への参照**に評価されるんですね」

ワイ「せや」

ワイ「でも次に`/`の計算をするときは**参照やなくて値が必要**や」

ワイ「そういうときは**参照から値を取り出して値に変換される**で」

ハスケル子「仕様書のここですね」

ワイ「（こいつ……もう仕様書を……）」

[6.2.4.8 GetValue (V)](https://www.ecma-international.org/ecma-262/9.0/index.html#sec-getvalue)

> 1. ReturnIfAbrupt(V).
2. If Type(V) is not Reference, return V.
3. Let base be GetBase(V).
>
（後略）

ハスケル子「詳細は省略しますけど、3以降がVが**参照**のときの処理ですね」

ハスケル子「変数への参照のときは6で、プロパティへの参照のときは5です」

ワイ「せやな（**震え声**）」

ワイ「まとめとして仕様書の**代入式の評価**の部分を見てみるで」

[12.15.4 Runtime Semantics: Evaluation](https://www.ecma-international.org/ecma-262/9.0/index.html#sec-assignment-operators-runtime-semantics-evaluation)

ハスケル子「1から6までありますけど、`x = y / 2`とか`f().name = "bar"`の処理は1で行われていますね。1の処理はこうなっています」

> a. Let lref be the result of evaluating LeftHandSideExpression.
b. ReturnIfAbrupt(lref).
c. Let rref be the result of evaluating AssignmentExpression.
d. Let rval be ? GetValue(rref).
e. （略）
f. Perform ? PutValue(lref, rval).
g. Return rval.

ワイ「まずaで`=`の左を**評価**しとるで。bはエラー処理やからまあ気にせんでええわ」

ワイ「そしてcで`=`の右を**評価**しとるんや」

ハスケル子「ポイントはdですね。さっき出てきたGetValueを使っています」

ワイ「せや。`=`の右は**評価**した後にGetValueによって**値に変換**されとるで」

ワイ「逆に`=`の左は値に変換せずに**参照**のまま使っとるんや」

ハスケル子「実際の代入処理はfで行なわれてるんですね」

ワイ「こういう風に、普通の計算だとほとんどGetValueで**参照**は**値**に変換されてまうけど」

ワイ「代入式とかでは**参照**のまま使うようになっとるんや」

ワイ「これが全部**仕様書を見れば一目瞭然**やな！　仕様書さまさまやで！」

ハスケル子「ちなみに[PutValueの定義](https://www.ecma-international.org/ecma-262/9.0/index.html#sec-putvalue)を見れば分かりますけど、`=`の左を評価しても**参照**にならなかった場合はfでエラーになるんですね」

ワイ「あとeは省略したけど[筆者の別記事](https://qiita.com/uhyo/items/2a2d67abd56419ea21b8)で解説しとるから見てってや！」

ワイ「ワイは出てこないけどな！」

[3歳娘](https://qiita.com/Yametaro/items/99cc1c8ebcfc703b1410)「C++とか分かる人は、**参照**と**値**の関係は**左辺値**と**右辺値**をおもいうかべると分かりやすいよ。」

ワイ「**うおっ！**（C++に詳しそうな既存キャラが居ないからって娘を使うなや！）」

ワイ「（あと筆者もCとかC++には詳しくないけど3歳娘に免じて許してくれや！）」

## 参照の他の使い道を知る

（数日後）

ハスケル子「[やめ太郎](https://qiita.com/Yametaro)さん」

ワイ「おう（今は筆者ちゃうんやけど2人称これでええんやろか）」

ハスケル子「何ですかその**人格が2つ同居してそうな顔**は」

ハスケル子「**参照**が役に立つのは代入のときだけなんですか？」

ハリー先輩「そんなことはないで」

ワイ「ハリー先輩、仕事はええんでっか」

ハリー先輩「**`delete`演算子**の解説をしたろ思ってな」

ワイ「（`delete`演算子か……。聞いたことはあるけど使ったこと無いで）」

ハリー先輩「`delete`演算子を使うコードなんて**9割クソコード**やからな」

ハリー先輩「**闇の深い案件**に関わらん限りはなかなかお目にかからないで」

ワイ「（**こいつワイの心を……！**）」

```js
const obj = { name: "foo" };

console.log(obj.name);

// obj.nameを削除
delete obj.name;

console.log(obj.name); // undefined
```

ハリー先輩「`delete`はオブジェクトのプロパティを**削除**する演算子や」

ハリー先輩「この例では`obj.name`を削除したから`obj.name`が無くなっとるで」

ワイ「`delete obj.name`ではまず`obj.name`が**評価**されて**`obj.name`への参照**になるんやな」

ハリー先輩「そうや」

ハリー先輩「`delete`は**参照**を見て削除するプロパティを決める」

ハリー先輩「**参照**は**どのオブジェクト**の**どのプロパティ**かって情報を持っとるのがポイントやな」

ワイ「`obj.name`への参照ってことが分かれば`delete`演算子くんが`obj`というオブジェクトをいじって`name`というプロパティを消せばええって分かるちゅうことや」

ハスケル子「`obj.name`を**値**に評価して`"foo"`になってしまうとどのプロパティを削除すればいいか分からない」

ハスケル子「だから**参照**が必要なんですね」

---

ワイ「あともう1つ**参照**の大事な使い道があるで」

ワイ「**メソッド呼び出しのときに`this`の値を決める**のも**参照**の役目や」

```js
const cat = {
  name: "ねこ",
  mew() {
    console.log(`${this.name}「にゃーん」`);
  }
};

cat.mew(); // ねこ「にゃーん」
```

ワイ「このコードの動作を説明してみい」

ハスケル子「`cat.mew()`でメソッドを呼び出しているので、`mew`の中では`this`が`cat`になるんですね」

ハスケル子「だから`this.name`は`"ねこ"`です」

ワイ「せや」

ワイ「`cat.mew()`という関数呼び出しを**評価**するときはまず`cat.mew`が**評価**されるで」

ハスケル子「呼び出そうとしている関数が**プロパティへの参照**に**評価**されたらメソッド呼び出しになるんですね」

ワイ「繰り返しやけど、**プロパティへの参照**は**どのオブジェクト**の**どのプロパティ**への参照かって情報を持っとるで」

ワイ「**どのオブジェクト**の部分がメソッド内の`this`に反映されるんや」

3歳娘「**参照**は変数に入れられるの？」

ワイ「ええ質問やな！　さすが我が娘や！」

ハスケル子「（何で会社に居るんですかこの子）」

ワイ「変数とかに入れられるのは**値**や、**参照は入れられない**で」

```js
const cat = {
  name: "ねこ",
  mew() {
    console.log(`${this.name}「にゃーん」`);
  },
};

const mew = cat.mew;
mew(); // エラーが発生（strictモードの場合）
```

ワイ「`cat.mew`は**参照**に評価されるけど」

ワイ「変数に入れるときはGetValueで**値**に変換されるんや」

ハスケル子「ということは変数`mew`の中身はただの関数で自分がどのオブジェクトのプロパティなのかは知らないんですね」

ハスケル子「だから`mew`を呼び出しても`this`が`cat`にならないんですね」

ワイ「せや」

ハリー先輩「**参照**が役に立つのは大体これくらいやな」

ハスケル子「代入、`delete`、そしてメソッド呼び出しですね」

ワイ「それ以外の場合は大体**参照**は**値**に変換されて使われるで」

ハスケル子「（この人**仕様書を全文検索するのが面倒くさい**から大体でごまかしてますね）」

## スーパーな参照

（1週間後）

ハスケル子「仕様書のReferenceの説明に気になるところがあるんですけど」

ハスケル子「ここに書いてある**スーパーリファレンス**ってなんですか？」

> A **Super Reference** is a Reference that is used to represent a name binding that was expressed using the super keyword. A Super Reference has an additional thisValue component, and its base value component will never be an Environment Record.

ワイ「それは**スーパーな参照**や」

ワイ「**スーパーな参照**を使うと処理速度が10倍になるで」

ハリー先輩「（息を吐くように**嘘**を……）」

ハリー先輩「スーパーリファレンスは`super.foo`のように**`super`のプロパティに対する参照**や」

ワイ「`super`ってのは**クラスの継承**をする時に使えるやつでんな」

ワイ「例を作ったから見てくれや」

```js
class Person {
  greet() {
    return "こんにちは";
  }
}
class Wai extends Person {
  greet() {
    return super.greet() + "やで";
  }
}

const yametaro = new Wai();
yametaro.greet() // "こんにちはやで"
```

ハリー先輩「`Person`クラスとそれを**継承**した`Wai`クラスを定義しとるな」

ハスケル子「`Wai`の`greet`メソッドの中で`super.greet`が出てきましたね」

ワイ「せや、`super`は**継承元のクラスのメソッドを呼び出したいとき**に使えるで」

ワイ「`Wai`の`greet`メソッドは`Person`の`greet`メッセージの結果に`"やで"`を追加するようになっとるんや」

ハリー先輩「まあ`super`が`Person.prototype`を指し示しとるみたいなもんやな」

ハスケル子「（`prototype`って何だろう）」

ワイ「（クラスを使えば`prototype`は触らんでもええようになってるから気にせんでもええで）」

ハリー先輩「`super`の参照は普通の参照とはちょっと違う動作をするで、`delete`できなかったりとか」

ワイ「一番大事なのは`super`の参照を**メソッド呼び出し**に使用した時でんな」

ハリー先輩「せや、**呼び出されたメソッド内での`this`は今の`this`と同じになる**で」

ハスケル子「**`this`が`super`になるわけではない**のが普通のプロパティへの参照との違いですね」

## `typeof`

ハスケル子「これは余談なんですけど」

ワイ「いきなりワイの席に来て何言ってんねん」

ハスケル子「`typeof`演算子もオペランドを**評価**して**参照**として扱うんです」

ワイ「オペランドってのは演算子に渡される引数みたいなやつのことやで」

```js
const x = 123;

console.log(typeof x); // "number"
```

ハスケル子「こういう場合はまずオペランドの`x`を**評価**して**参照**を得て」

ハスケル子「**参照**を**値**に変換したら`123`なので結果が`"number"`になります」

ワイ「なんや**参照**として扱っとらんやんけ！」

ハスケル子「では次の例を見てください」

```js
console.log(typeof y); // "undefined"
console.log(y); // エラーが発生
```

ハスケル子「この変数`y`は**宣言されていない変数**なので`y`の**値**を取得しようとすると**エラー**になります」

ハスケル子「でも実は`typeof`で調べるだけならエラーにならないんです」

ワイ「これが`y`の**値**を取得しとらん証拠っちゅうわけやな」

ハスケル子「`y`みたいな存在しない変数は**評価**すると**未解決の参照**になるんです」

ハリー先輩「仕様書でいうとここやで」

[8.1.2.1 GetIdentifierReference](https://www.ecma-international.org/ecma-262/9.0/index.html#sec-getidentifierreference)

> The abstract operation GetIdentifierReference is called with a Lexical Environment lex, a String name, and a Boolean flag strict. The value of lex may be null. When called, the following steps are performed:
>
1. If lex is the value null, then
>
    a. Return a value of type Reference **whose base value component is undefined**, whose referenced name component is name, and whose strict reference flag is strict.
>
１（後略）

ワイ「base value componentが**undefined**な参照が未解決の参照ってことやな」

ハスケル子「未解決の参照は**値**を得ようとしたらエラーになるんですけど」

ハスケル子「`typeof`演算子はオペランドを**評価**した結果が未解決の参照のときは`"undefined"`を返すのでエラーにならないんです」

[12.5.5.1 Runtime Semantics: Evaluation](https://www.ecma-international.org/ecma-262/9.0/index.html#sec-typeof-operator-runtime-semantics-evaluation)

> 1. Let val be the result of evaluating UnaryExpression.
2. If Type(val) is Reference, then
>    
    a. **If IsUnresolvableReference(val) is true, return "undefined".**
> 3. Set val to ? GetValue(val).
> 4. Return a String according to Table 35.

## まとめ
ワイ「なんか仕様書ばっかり読んで疲れたやで」

ハリー先輩「でもJavaScriptの**参照**の何たるかは分かったやろ？」

ワイ「**変数**とか**プロパティアクセス**を**評価**した結果が**参照**になって、代入式のとか**評価**に使われてるんやな」

ハスケル子「でも」

ハスケル子「**参照って仕様書にしか出てこないですよね？　実務の役に立たないですよね？**」

ワイ「**せやで**」

社長「記事冒頭の配列の話は何だったんだね？」

```js
const arr1 = [1, 2, 3];

// (中略)

const arr2 = arr1;
arr2.push(4);

// （中略）

console.log(arr1); // [1, 2, 3, 4]
```

ワイ「**あれは釣り餌や**」

社長「お前明日から来んでええわ」

〜完〜