JavaScript を「純粋な」プロトタイプベースとして使う

## 経緯

JavaScript での開発中、あるオブジェクトの生成で、新しく専用のクラスを作ってインスタンスとするか、既存のクラスからインスタンスを作ってそのプロパティに色々突っ込むか、で悩み、色々調べた所、Io という面白そうな言語を見つけ、ヒントになったと同時にプロトタイプベースに興味も持ったので書きました。

## Io っぽく JavaScript を書く

### Io とは

[io language](http://iolanguage.org/)
公式サイト。

[プログラミング言語「Io」の参考情報 - JavaScript勉強会](http://jsstudy.hatenablog.com/entry/2017/03/30/214048)
参考情報をまとめてくれている。

今現在、自分の中で最高の言語です。めっちゃ好き。

### 実装

まず核となるオブジェクトを作ります。名前は `Object` が良かったけど被るのでしょうがないです。変数宣言は省略してます。

```js:
Stuff = Object.create(null, {
  super: {
    enumerable: true,
    value: null
  }
});
```

今この `Stuff` には `super` 以外に何もデータがありません。試しに

```js:
Stuff + ""
```

を実行してみると、

```
TypeError: No default value
```

とのエラーを吐きます。文字列変換時に呼ばれる `valueOf` メソッドが `Stuff` に見つからないので当たり前です。では、いろいろメソッドを追加してみます。

```js:
// 自分を継承しているオブジェクトを生成するメソッド
Stuff.clone = function() {
  return Object.create(this, {
    super: {
      enumerable: true,
      value: this
    }
  });
};

// プロパティ・メソッドを追加するメソッド
Stuff.assign = function(...args) {
  return Object.assign(this, ...args);
};

// 継承関係を判定するメソッド
Stuff.kindof = function(obj) {
  return this === obj || (!!this.super && this.super.kindof(obj));
};
```

これでよくある継承やらをやってみます。

```js:
A = Stuff.clone();
A.assign({
  hoge() {
    console.log(0);
  }
});

/* new 的なやつ */
a = A.clone();
a.hoge(); // 0

/* extends 的なもの */
B = A.clone();
B.assign({
  /* メソッド追加 */
  fuga() {
    return "fuga";
  },
  /* オーバーライド的なやつ */
  hoge() {
    /* super.hode の呼び出し */
    B.super.hoge.apply(this, arguments);
    console.log(1);
  }
});

b = B.clone();
b.hoge(); // 0 1

b.kindof(A); // true
a.kindof(B); // false
```

`Object.setPrototypeOf` などを使った場合でも結局は似たような形になると思います。

この例だと、`b` を作るのにわざわざ `B` という「設計図」を作る必要はありません。`new` 的なやつと `extends` 的なやつは実質同じことをしており、クラスにするべきかインスタンスにするべきかを考える必要はなくなります。

また、非標準である（ES2015で標準化された？）`__proto__` を使えば以下のようにも書けます。

```js:
Stuff = {
  __proto__: null
};

A = {
  __proto__: Stuff,
  hoge() {
    console.log(0);
  }
};

B = {
  __proto__: A,
  fuga() {
    return "fuga";
  },
  hoge() {
    B.__proto__.hoge.apply(this, arguments);
    console.log(1);
  }
};
```

### つまり

上の `__proto__` を使った例で、オブジェクトの生成・継承に関数を一切使っていないのがわかるでしょうか？　つまり、JavaScript では特別なことをせずとも継承が容易に出来るのです。
ここまですっきり書けるのは、JavaScript がそもそもプロトタイプベースだからに他ならず、当たり前といえば当たり前です。Io を真似ることで、JavaScript がプロトタイプベースであることを意識しやすくなったと言えます。

## じゃあ JavaScript の「new」とは何なのか

「JavaScript はプロトタイプベースのオブジェクト指向言語である」とよく見ますが、`new` とか `prototype` とか `__proto__` とか `constructor` とか、ついには ES2015 の `class` 構文とか色々な要素がごちゃごちゃしすぎている気がします。

>
```js:
var o = new Foo();
```
これを呼び出したとき、 JavaScript は、
>
```js:
var o = new Object();
o.[[Prototype]] = Foo.prototype;
Foo.call(o);
```
実際にはこれ（あるいはこのような何か）を行っており、
>
```js:
o.someProp;
```
後にこうすると、 `o` が `someProp` プロパティを持っているかどうかを調べ、もし持っていなければ `Object.getPrototypeOf(o).someProp` を、そこにも存在しなければ `Object.getPrototypeOf(Object.getPrototypeOf(o)).someProp` を…と調べていきます。
>
[継承とプロトタイプチェーン - JavaScript | MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Inheritance_and_the_prototype_chain)

そもそも `prototype` と `__proto__` が混在しているのが混乱の原因かと思われます。このような状況から、JavaScript をプロトタイプベースと呼んでいいのか議論があるようです。タイトルの「純粋な」もその意味です。

[JavaScriptはプロトタイプベースのオブジェクト指向プログラミング言語ではない！？ - JavaScript勉強会](http://jsstudy.hatenablog.com/entry/2017/03/29/181613)

## 「純粋な」プロトタイプベースで書くことについて

### メリット
- シンプル。考えることが少ない。
- JavaScript はもともとプロトタイプベースなのでそれを引き出しているとも言える。

### デメリット
- ビルトインオブジェクトが JavaScript 式のプロトタイプベースなので整合性が取れない。
- IDE の型判定などが効かない。

### 未検証
- 速度。プロトタイプいじるのは速度的にどうなんだろう。
- 保守性。結局はうまく設計しないとごちゃごちゃになる。

### その他
- ビルトインオブジェクトとうまくやる方法も考えたい。
- `Proxy` とか使ったらもっと面白くなりそう。
- プロトタイプベースでかける altJS とかあったら面白そう。

### 結論
- JS は闇
- Io はいいぞ