JavaScriptのnewを探検する

## JavaScriptのnewを探検する

`new`：それは新しいオブジェクトとの出会い。
つまり、クラスからインスタンスを生成する演算子です。

ただ、噂によるとJavaScriptのクラス・オブジェクトの関係は他の言語のオブジェクトとちょっと違うらしい。

ちょっと違うって何が違うんだ。
`new`よ、お前はその短い３文字の中にどんな意味をはらんでいるんだ？

そんなことを考えていると眠れない夜が続いたので、自分で実験してみることにした。

## クラスの作り方の歴史

JavaScriptでクラスを作るとき下記の様に書く。
他はどうか知らないが、私はこう書いている

```javascript:ES2015以降のクラスの書き方
class Taiyaki {
    constructor (material, ingredient) {
        this.material = material
        this.ingredient = ingredient
    }

    getName () {
        return this.material + this.ingredient + 'たいやき'
    }
}

const croissantCreamTaiyaki = new Taiyaki('クロワッサン', 'クリーム')
console.log(croissantCreamTaiyaki.name)  // 'クロワッサンクリームたいやき'
```

ただ、噂によるとJavaScriptはもともと関数からクラスを作っていたらしい。
やってみたら本当にできた。

```javascript:ES5以前の書き方
function Taiyaki (material, ingredient) {
    this.material = material
    this.ingredient = ingredient
}

Taiyaki.prototype.getName  = function () {
    return this.material + this.ingredient + 'たいやき'
}

const croissantCreamTaiyaki = new Taiyaki('クロワッサン', 'クリーム')
console.log(croissantCreamTaiyaki.getName())  // 'クロワッサンクリームたいやき'
```

上下を見比べて、私の様な凡人にでも理解できたことは

* 関数に`new`をつけて呼び出すと`class`で書いた時の`constructor`として振る舞う
* 関数の`prototype`というプロパティににメソッドを設定していたら、インスタンスから呼び出せる

ということ。

関数の`prototype`というプロパティににメソッドを設定していたら、インスタンスから呼び出せる・・・？
なんでや。

われわれは謎を解明するためアマゾンの奥地へ向かった。

## プロトタイプチェーン 〜JavaScript内を流れるアマゾン川〜

さらに調査をすすめたところ、prototypeに関する驚きの話を耳にした。
人々はprototypeに関して、口を揃えて次の様にいうのである。

* JavaScriptではクラスが`ptorotype`、インスタンスが`__proto__`というプロパティを持っている
* `__proto__`は`prototype`を参照渡ししたもの
* `__proto__`プロパティ内にあるメソッドやプロパティは親オブジェクトから直接呼び出せる

今のところ、クラスに`ptorotype`というプロパティがある、ということしか発見していないぞ、順番に試していこう。

```javascript:インスタンスに__proto__は実在した！
/* クラス定義は省略 */
const croissantCreamTaiyaki = new Taiyaki('クロワッサン', 'クリーム')
console.log('__proto__' in croissantCreamTaiyaki)                    // true
```

```javascript:クラスのprototypeとインスタンスの__proto__が辿り着く先は同じな様だ
/* クラス定義は省略 */
const croissantCreamTaiyaki = new Taiyaki('クロワッサン', 'クリーム')
console.log(croissantCreamTaiyaki.__proto__ === Taiyaki.prototype)   // true
```

```javascript:インスタンスでなくても、__proto__内のメソッド・プロパティは親オブジェクトから直接呼び出せる
const obj = {
    __proto__: {
        method() {
            console.log('Method was called.')
        },
        text: 'Some text.',
    },
}

obj.method()             // 'Method was called.'
console.log(obj.text)    // 'Some text.'
```

この事実から次のことが見えてきた。

* クラスは`prototype`というメソッドを持ったオブジェクトである
* インスタンスは`prototype`の参照を`__proto__`プロパティに持った、新しく生成されたオブジェクトである
* `__proto__`プロパティは親オブジェクトから中身を呼び出せる

なるほど、、これらの性質を組み合わせてクラスとインスタンスの関係を再現しているわけか
トリッキーなのは`__proto__`の中身が親オブジェクトから呼び出せるというくらいなので、そんなに理解は難しくなさそうだ。

ただ、ここでいくつかの疑問が湧いてくる

* `__proto__`に`prototype`の参照を渡しているだけなのであれば、`new`を用いずに再現できるのでは？
* もし、`new`を用いずに再現できるのであればブラウザは何を持ってインタンスと判断している？
* ていうか、継承どうやるん？

今回は`new`を探検するのが目的なので上の２つの疑問を解決すべく、引き続き探検を続けていこうと思う。
（継承に関してはまたいずれ・・・）

## ついにnewの内部に潜入

とりあえず、関数オブジェクトの`prototype`の参照を`__proto__`に渡せばいいんだ。
と半ばやけくそに次の様なコードを書いてみた。

```javascript:prototypeしか渡さない
function Foo() {}
console.log({__proto__: Foo.prototype})
```

こちらがその実行結果だ

![Foo!?](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/185790/b30de51c-fbdd-9b81-0971-99ac99de3fe3.png)

我々はその光景に目を疑った、そこにはちゃんとFooのインスタンスが表示されていたのである。

あとは、この空っぽなインスタンスにプロパティを設定すべく、コンストラクターを呼び出せる様にしたら`new`を再現することができるぞ！

```javascript:newっぽい関数
function newInstance (ClassObject, ...args) {
    const instance = {__proto__: ClassObject.prototype}
    ClassObject.call(instance, ...args)
    return instance
}
 
function Foo(message) {
    this.message = message
}
 
Foo.prototype.win = function () {
    console.log(this.message)
}
 
const foo = newInstance(Foo, 'Haha! We overcame "new".')
foo.win() // 'Haha! We overcame "new".'
```

ってなわけで、無事`new`っぽい関数を作って`new`と同じっぽいことをすることができました。

![foo2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/185790/e5b9c318-a278-383f-aa8a-24637513e699.png)

表示もちゃんと`Foo`になってますね。

ん、ちょっとまて、コンストラクターでもメソッドが使えないと困ると思って、
勝手に事前に`this`に`__proto__`を仕込んでしまったが、本当に本物の`new`もそうなっているのだろうか？

最後にそれだけ確認して、この記事を終わろう

```javascript:普通にnewしてもちゃんとthisに__proto__は入っていた
function Foo() {
    console.log('__proto__' in this) // true
}

new Foo()
```

よかった、今夜はぐっすり眠れそうだ。