JavaScriptのオブジェクト指向プログラミング(OOP)とは?

2021年3月6日JavaScript

JavaScriptのオブジェクト指向プログラミングについての基礎知識をまとめました。

まずは、オブジェクト指向プログラミングとは何かについて、理解を深めた上で、JavaScript特有の点を理解していきましょう。

オブジェクト指向プログラミングとは?

オブジェクト指向プログラミング(OOP, Object-Oriented Programming)
オブジェクトをベースにしたプログラミングパラダイムのことです。
大きな開発で最も使用されているプログラミングパラダイムです。

パラダイムというのは、どのようにコードを書くのかというスタイルのことです。たとえば、日本語でいうのなら論文調でかく、WEBライティング、小説風にかく、などと似ています。

オブジェクト指向プログラミングのオブジェクト

オブジェクトをリアルの世界のユーザーであったり、アイテムを描写します。
オブジェクトはデータ(プロパティ)とコード(メソッド)をもっています。オブジェクトを使うことで、データとそれに対応したふるまいを一つのブロックに収めることができます。
オブジェクト指向プログラミングのオブジェクトは、必要なものを完備しているコードのブロックのことです。
そのオブジェクトを使って、アプリケーションのブロックを作ったり、その他のブロックと相互作用したりします。
相互作用はAPI(パブリックインターフェース)を通して起こります。オブジェクトの外にあるメソッドは、オブジェクトへアクセスしてコミュニケーションすることができます。

オブジェクト指向プログラミングはなんのためにあるのか?

オブジェクト指向プログラミングはコードをゴールのために組織化して開発を進めることで、スパゲティコードを避けることができ、より柔軟に、より簡単に維持することができます。

伝統的なOOP(クラスとインスタンス)

クラスとインスタンスがOOPの代表的な例になります。
クラスという設計図を基に、実際のインスタンスを作成していきます。

では、OOPには何が重要なのでしょうか?
どのようにクラスをデザインすればいいのでしょうか?
どのようにして現実のデータをクラスに落とし込むのでしょうか?

オブジェクト指向プログラミングの基本4原則

クラスをつくるのは、家を作るようなもので、具体的な方法があるわけではありませんが、4つの基本となる原則があります。
それが、

  • 抽象化(Abstraction)
  • カプセル化(Encapsulation)
  • 継承(Inheritance)
  • ポリモルフィズム(Polymorphism)

抽象化(Abstraction)

抽象化は重要ではない詳細を無視したり隠したりして、そのものの概念を得るだけに十分な情報をもった状態にすることです。
たとえば、スマホで考えてみましょう。
スマホの中には、ホームボタン、ボリュームボタン、スクリーン、バッテリー、イヤホンジャック、カメラなどなどたくさんの部品が備わっています。
抽象化を抽象化すると、ホームボタン、ボリュームボタン、スクリーン程度で伝わると思います。
このように、不要な情報を削ぎ落としていくことを抽象化といいます。

カプセル化(Encapsulation)

プロパティやメソッドをクラスの中だけでアクセスできるようにします。いくつかのメソッドはAPIとして公開されます。
たとえばパスワードなどが偶発的に変更されてしまうのは、とてもまずいことになります。
できるだけ必要なところでしかそのコードにアクセスできないようにすることが、バグを防ぐためにとても役に立ちます。

継承(Inheritance)

継承は、プロパティやメソッドを子クラスが利用できるようにすることです。
クラスの間で親と子といったハイラルキーが形成されます。
継承によって、共通のロジックを利用できるようになったり、現実の関係性を描写することができます。
継承を使わないと、同じか似たようなコードを何度も書く必要がでてきます。

ポリモーフィズム(Polymorphism、多様)

ポリモーフィズム(ポリモルフィズム)は、言葉はギリシャ語からきており、many forms/many shapesという意味で多様性といった意味があります。
ポリモーフィズムの大事な部分としては、親クラスから継承したメソッドを子クラスが上書きすることができるという点です。
同じ名前のcalcAgeというメソッドがあったとして、親では西暦で表示して、子クラスでは和暦で表示するようなことが可能です。
たとえば、ログインという機能を親クラスから継承した複数の子クラスは、それぞれそのログインという機能を上書きして新しいものにすることができます。
継承した個体によってどんどん形が変わっていく、これがポリモルフィズムです。

JavaScriptのオブジェクト指向プログラミング

JavaScriptでこのオブジェクト指向プログラミングはどのように動くのか確認しましょう。
上の4原則が大事な点は同じですが、JavaScriptにはprototypeオブジェクトという存在がありますので、その点を深くみていきましょう。

prototypeオブジェクトにリンクされる

JavaScriptには、プロトタイプ(Prototype、原型)があります。
オブジェクトは、prototypeオブジェクトにリンクされます。

プロトタイプ継承(Prototypal Inheritance)

それぞれのオブジェクトが、prototypeを持っています。
このprototypeはメソッド(methods or behavior)を持っていて、オブジェクトはそのprototypeのメソッドにアクセスすることができます。
これをプロトタイプ継承(Prototypal Inheritance / Delegation)といいます。
これは、上の継承(クラスから他のクラスが継承)とは意味が違うので混同しないようにしましょう。プロトタイプ継承は、インスタンスがクラスを継承するという特別な継承ということです。

ふるまい(behavior)の委譲

ふるまい(behavior)はprototypeオブジェクトに委譲(Delegation)されます。
通常の継承では、ふるまい(behavior,メソッド)はクラスからインスタンスへコピーされます。
しかし、プロトタイプ継承は、オブジェクトがprototypeにアクセスしてそのメソッドを利用している、というイメージになります。継承というとややこしいので、委譲という言葉が使われたりします。

プロトタイプ継承の具体例

MDNのサイトで、lastIndexOf()メソッドやmap()メソッドなどを検索してみましょう。
そうすると、Array.prototype.lastIndexOf(),Array.prototype.map()とかかれていることがわかります。
どういう意味かというと、Array.prototypeは配列オブジェクトのprototypeで、JavaScriptでつくるすべての配列オブジェクトの原型とということです。そのため、すべてのlastIndexOf()やmap()といったメソッドがどの配列でも使えるのです。

実際のプロトタイプ継承の具体例はこちらを参照してみてください。

JavaScriptのプロトタイプ継承を実装する3つの方法

JavaScriptのプロトタイプ継承を実装するには、具体的には3つの方法があります。

  • CUnstructor関数
  • ES6のクラスキーワード
  • Object.create()

Constructor関数

Constructor関数は、関数からオブジェクトをつくる技術です。
Constructor関数で、配列、Maps、Setsといったビルトインオブジェクトが実装されています。

ES6のクラスキーワード

現在ではこのES6のクラスキーワードを使うのがモダンな方法となっています。
Constructor関数の代替方法となります。
ES6のクラスキーワードでは、Constructor関数と全く同じように動きます。
ES6のクラスキーワードはConstructor関数をわやりやすく読み書きしやすいようにしてくれています。そのことを、ES6のクラスキーワードはConstructor関数のシンタックスシュガー (syntax sugar、糖衣構文)であるといったりします。

このクラスキーワードのクラスという意味は、上で使っている継承のクラスとは意味が違うため、混同しないように注意しましょう。

Object.create()

オブジェクトとprototypeオブジェクトをリンクさせるのに一番簡単な方法が、このObject.create()です。
ただ、すべてのケースで使えるわけではないので、上の方法が主流として使われています。