Edited at

「型」がないからJavaScriptはイケてないとかって言ってるヤツが本当にイケてない件

JavaScript2 Advent Calendarの23日目の記事です。

「例外」がないからGo言語はイケてないとかって言ってるヤツが本当にイケてない件を読んで、衝動的にJavaScriptのことを書いてみたくなりました。元記事より低クオリティでネタ性も薄いですが、悪しからず。批判等も歓迎です。


はじめに

よくJavaScriptを「型がない」という表現でdisっている人がいますが、「型がない」という表現には誤解と誇張が含まれています。


誤解: JavaScriptには型がない

JavaScriptにはれっきとした型があります。大事なことなのでもう一度言います。JavaScriptには型はあります。ただそれが他の(静的型付けの)言語の型と少し違っていたりするだけです。


静的型付けと動的型付け

まず、静的型付けと動的型付けについてですが、JavaScriptはご存知のように動的型付けです。静的型付けの言語では型は変数に結び付けられていますが、動的型付けの言語では一般的に、型は値(データ)が持っています。


JavaScriptの型

型があると言った以上、どんな型があるのか示さないといけませんね。

以下がJavaScriptの型です。


  • Undefined

  • Null

  • Boolean

  • String

  • Number

  • Symbol

  • Object

以上の7種類が、そしてこの7種類だけがJavaScriptの型です。そして驚くべきことに、これらのうち6つがプリミティブ型で、Object型のみが複合型1なのです。他の言語のように新しい型を定義することもできません。しかし、JavaScriptのオブジェクト指向においては、これだけで十分なのです(詳しくは後述)。


JavaScriptの暗黙の型変換

JavaScriptでは、暗黙的に型変換が行われます。これは確かに初心者には難しいかもしれません。

しかし、同じスクリプト言語で、より型に厳しいPythonと比べても、積極的に型変換を行っているだけで、いくつかの基本的な変換が分かれば、大抵は簡単に理解できます。PHPのような意味不明な変換は行われません。理解した上で使えば、逆に便利な場合もあります。


型と型変換を意識するために

JavaScriptの型変換は型を意識していれば、難しくはありません。では型を意識するにはどうすればいいのかというと、変数名を適切に命名2すればいいのです。意味や役割が分かるような命名をすれば、型を取り違えることが減り、暗黙の型変換に悩まされることも少なくなるでしょう。さらには、コード自体の質の向上にも繋がります。


事実: JavaScriptにはクラスがない

Javaにはクラスがあります。そして全てのクラスはjava.lang.Objectを継承して定義された型です。しかしJavaScriptにはそもそも「クラス」という概念がありません3

JavaScriptのオブジェクト指向の根幹には「クラス」がないため、そもそもクラスを使って新しい型を派生させていく必要がないのです。


プロトタイプという概念

「クラス」がない代わりに、JavaScriptには「プロトタイプ」という概念があります。これはオブジェクト指向プログラミング言語の中では少数派なのですが、JavaScript固有のものではありません4


クラスとプロトタイプ

クラスベースのオブジェクト指向では、オブジェクトを「クラス」から作成します。一方、プロトタイプベースのオブジェクト指向では、オブジェクトを「プロトタイプ」から作成します。それでは、「クラス」と「プロトタイプ」の違いは何なのでしょうか。私なりにまとめてみようと思います。


  • クラス

オブジェクトのプロパティやメソッドの定義がまとめられた、オブジェクトの鋳型のようなものです。クラスは静的なデータであり、そのデータに基づいて実体(インスタンス)が生成されます。クラス自体はオブジェクト(インスタンス)ではありません5。異なるプロパティやメソッドを持つオブジェクトを生成するには、異なるクラスを定義する必要があります。


  • プロトタイプ

オブジェクトを生成するコピー元となるオブジェクトです。プロトタイプベースの場合、既存のオブジェクトを複製(クローン)することによって新しいオブジェクトを作成します。プロトタイプ自身も単なるオブジェクトであり、プロトタイプと生成されるオブジェクトの間には、クラスとインスタンスのような関係はありません。オブジェクトのプロパティやメソッドは動的に書き換え可能で、クラスのような静的なデータに依存しません。(クラスが必要ないということは、新しく型を定義する必要がないということでもあります。)


プロトタイプの長所

クラスベースの場合、クラスAのオブジェクトaの振る舞いの一部を変更したオブジェクトbを生成したいとき、クラスAを継承した6クラスBを作る必要があります。一方、プロトタイプベースの場合、オブジェクトaをプロトタイプとして複製したオブジェクトbに適当な振る舞いを実装するだけで良いのです。

また、クラスの継承には様々な問題があり、移譲の方が優れていると言われることがありますが、プロトタイプベースでは、プロトタイプチェーンという仕組みにより移譲に似た機構を手軽に利用することができます。

さらに、プロトタイプベースの場合オブジェクトは動的であり、静的なデータに依存しないので、スクリプト言語のような動的な言語との相性が良いです。


プロトタイプの短所

静的な(コンパイルを必要とする)言語との相性が悪い、という一点に尽きます。

プロトタイプベースだと記述が冗長になって使いにくい、と主張する人もいますが、それは多くの場合プロトタイプベースの言語で無理にクラスの継承や移譲のような構造を実現しようとしていることによります。プロトタイプベースの言語においては、クラスの枠組みにとらわれた考え方はもはや足枷です。プロトタイプのアドバンテージを活かしたプログラミングを心掛けるべきです。


結論

JavaScriptには型がないというのは誤解です。JavaScriptには型はあります。変数に型を設定したり、静的な型付けを行ったりしないだけです。暗黙の型変換の多くは直感的に理解できます。上手く利用すれば、余計な型変換のコードを減らすことができます。少ない記述量で済むのは、スクリプト言語としては大きなアドバンテージです。

そして、動的型付けはプロトタイプベースのオブジェクト指向と非常に相性が良く、適切に使えばクラスベースの言語に見られる冗長性を排除した簡潔なコードが書けます。

JavaScriptには「型」がないのではなく、型付けを弱くすることと引き換えにコードの簡潔さを手に入れ、クラスの冗長性や複雑な継承関係を捨て去ってプロトタイプベースの柔軟な仕組みを採用したのです。

JavaScriptにはJavaScriptのパラダイムがあり、その上で動的な弱い型付けが採用されています。静的型付け言語とは方向性が全く違うこの言語を、どうして静的型付け言語の基準で測って批判するのでしょうか。

「型」がないからJavaScriptはイケてない、と主張するのは、日本語には敬語があるが英語にはないから英語のほうが劣っている、と言い張るようなものです。それぞれの言語にそれぞれのものの見方があり、方向性も様々です。それを理解せずに頭ごなしに否定することこそが、本当にイケていません。





  1. Javaの影響で「プリミティブ型」の対義語として「参照型」が使われることが多いですが、元々「プリミティブ型」は(自身を含む)他の型に依存せずに定義される型を表し、「参照型」は単に他のデータへの参照を保持する型を表します。例えば、JavaScriptにおいては、String型は「プリミティブ型」であり「参照型」でもあります。区別のために、ここでは他の型を使って定義される型として「複合型」という言葉を使っています。 



  2. 型を意識するとは言っても、型名が分かる命名をしろというわけではありません。間違ってもシステムハンガリアンは使わないようにしてください。 



  3. ES6にはclass構文がありますが、それは単なるシンタックスシュガーに過ぎません。 



  4. 最初に取り入れられたのはSelfだったと思います。 



  5. 言語によってはリフレクションがサポートされていて、クラスの情報にアクセスするオブジェクトを扱うこともできます。 



  6. 継承ではなく移譲で解決する場合もありますが、どちらにしても新しいクラスが必要になります。