読者です 読者をやめる 読者になる 読者になる

ES6はクラスベース?プロトタイプベース?

こんにちは丸山@h13i32maruです。

ES6(ES2015)にclassシンタックスが入って、あー、すごい書きやすくなったなぁ、読みやすくなったなぁと思ってclassシンタックスのありがたみを噛み締めながらコードを書いています。

そんなときに

ES6にclassシンタックスが導入されたけど、
クラスベースになるわけではなくて所詮ただのプロトタイプベースを使ったシンタックスシュガーだ

というような話をちょいちょい耳にします。こういう話を聞くたびになんだかもやもやしていたので、ちょっと自分の中の考えを整理してみようと思います。

注意: 以下はあくまで僕個人の考えということでよろしくお願いします。

OOPとは

「OOP」「クラスベース」「プロトタイプベース」について順番に整理します。まずはOOPについて。

僕はオブジェクト指向というのは以下のどちらかまたは両方を指すものだと考えています。

  • オブジェクト間のメッセージングにより相互作用を起こしながら処理を行うプログラミングモデル
  • ユーザ定義型のデータ構造を元にしたオブジェクト生成を行いながら処理を行うプログラミングモデル

以降では後者の考えを中心に整理を行っていきます。

クラスベースとプロトタイプベースの違いは

OOPを実現するためには「ユーザ定義型のデータ構造」を元にして「オブジェクト生成」を行う必要があると書きました。ではクラスベースとプロトタイプベースではそれぞれどのように実現しているのでしょうか?ここではクラスベースの言語としてJava、プロトタイプベースベースの言語としてJavaScriptを例としてみてみます。

Java

public class Foo {
  public int num = 0;
  public void bar(){
  }
}

Foo foo = new Foo();


JavaScript

function Foo(){}
Foo.prototype.num = 0;
Foo.prototype.bar = function(){};

var foo = new Foo();

JavaもJavaScriptも細かいシンタックスは違えどやっていることは非常に似ています。ユーザ定義型のデータ構造Fooを定義し、それを元にオブジェクトを生成new Foo()しています。ではクラスベースとプロトタイプベースは根本的には一体どこが違うのでしょうか?

僕はクラスベースとプロトタイプベースの根本的な違いは「ユーザ定義型のデータ構造が静的か動的か」という点にあると考えています。つまりクラス静的なユーザ定義型データ構造プロトタイプ動的なユーザ定義型データ構造ということです。一つ注意しないといけないのは各言語の内部実装については考慮しないということです。内部実装はあくまでクラスベースやプロトタイプベースを実装する手段であり、その手段によってそれぞれの違いを説明するというのは本質ではないと考えているからです。

  • クラスベース
    • 静的なユーザ定義型データ構造を元にオブジェクトを生成するOOP
  • プロトタイプベース
    • 動的なユーザ定義型データ構造を元にオブジェクトを生成するOOP

ここで、クラスベースとプロトタイプベースの関係性について面白い考えをすることができます。それは

プロトタイプベースに「データ構造を静的にする」という制約を加えることで、あたかもクラスベースのように振る舞うことができる。

ということです。こう考えるとプロトタイプベースはクラスベースを内包する概念になります(登場時期でいうとクラスベースのほうがプロトタイプベースよりも早いですが)。このような考えを他の人もしているかなと思って調べてみたところ以下のサイトで同じような考えを書かれている方がいらっしゃいました(2006年ぐらいにかかれているようです)。

プロトタイプベース・オブジェクト指向

ES6のclassをどう捉えるか?

ES6のclassシンタックスはやはりFunction.prototypeの糖衣構文であるということは真実だと思います(実際にはFunction.prototypeだけでは実現できないことも含まれていますが)。

ですが、先程も書いたとおりプロトタイプベースに「データ構造を静的にする」という制約を加える事でクラスベースのように振る舞うことができると考えています。なのでES6のclassシンタックスを使うということを僕は以下のように捉えています。

JavaScriptのプロトタイプベースに「データ構造を静的にする」という制約を課し、classで宣言されたデータ構造を動的に変更しないという宣言を行う

ここで言う制約宣言はプログラム的なものではなく、あくまでもコードを書く人の共通認識や慣習というものです。上でも書きましたが、クラスベースやプロトタイプベースの実装方法によってその区別を行うことは本質でないと言いました。つまりclassという糖衣構文とプログラマーの共通認識というゆるい手段を使ってJavaScriptにクラスベースが実装されたと言えます(感覚的にはコード中に書くドキュメントに近いものです)。

僕の考えをまとめると「ES6のclassを使うということはクラスベースOOPである」ということになります。

余談

話は変わって、Rubyについて少し考察してみます。Rubyは一見クラスベースのように思いますが、classで宣言されたユーザ定義型のデータ構造を実行時に動的に変更することができます。これは静的か動的かという点に関して言うとプロトタイプベースのように思います。しかし実際にRubyで書かれたアプリケーションコードにおいてclass宣言されたデータ構造を実行時に書き換えることはあまりないように思うので、クラスベースと言ってしまっても良さそうです。ですが、フレームワークやライブラリなどのコードにおいてモンキーパッチやメタプログラミングを行うときは動的に書き換えられるというプロトタイプベースの力が発揮されます。つまりRubyはクラスベース、プロトタイプベースの両方を兼ね備えており、扱う問題において適切な仕組みを使うことができる非常に柔軟な言語と言えます(他にもそういう性質の言語はあると思います)。実際にRubyとはではクラスベースともプロトタイプベースとも書かれておらず、単にオブジェクト指向としか書かれていません。

こう考えると、classが導入されたES6はプロトタイプベースでもクラスベースでも書けるという点においてRubyに非常ににていると感じています。誤解がないように補足しておくと、その他の点で大なり小なり色々違うところがあるのは承知しており、クラスベースかプロトタイプベースかという点についてということです。

以上、ES6について僕なりの考察でした。