2017年6月24日土曜日

モジュールの仕組みと JavaScript

長いこと JavaScript を利用していると、モジュール化したい、と思うシーンが度々でてきます。

今回は、そのモジュールを調べてわかったこと、思ったことについて書かせて頂こうと思います。



一般的なモジュールとは

モジュールとは、組み立てることで一つのものを作るための部品です。分かりやすく言うなら積み木やレゴブロックの各パーツを想像して下さい。また、レゴブロックにはそれらを接続するための凹凸があるように、モジュールには接続のための規格が必要となります。

プログラミング用語としてのモジュールとは

一般的なモジュールという意味を、プログラミングに当てはめると、プログラムのモジュールというのは、下記の2つの性質を備えていることだと思います。


  • 独立性
    • プログラムを独立させ、中身を混同させないこと
    • モジュールは一つにつき一つのオブジェクト
    • そのオブジェクトの中にデータ等が入っている
  • 接続性
    • 作ったオブジェクトを、他のオブジェクトから参照する仕組みがある


例:C++ 言語でのモジュールとは

さて、モジュールというのは「機能ごとに独立していて、接続性があるもの」というかなり幅広い意味を持っています。ですので、一つの言語でプログラミングをしているからといってモジュールという言葉の意味が一つとは限りません。

例えば C++ 言語では、ソースレベル、オブジェクトレベルで「モジュール化」がなされます。また、一般的にモジュールと言った場合、アプリケーションレベルでのモジュールのことを指していることが多いと思います。

ソースレベルでのモジュール化

ソースレベルでのモジュールを実現しているのは、「ソースファイルとインクルード」です。

C++言語では、一つのソースファイルに書かれたものは特別な記述をしない限りはそのファイル内でのみ利用可能な状態になります。つまり、機能ごとにソースファイルを分ければそれだけで独立性が担保され、ヘッダファイルや extern 構文などを利用してそれらを接続する方法を公開します。この仕組みはコンパイラが提供しているので、接続性も常に維持されます。

しかし、ソースレベルでのモジュールというのはあくまで次のオブジェクトレベルでのモジュールを実現するためのコンパイラへの指示でしかありません。このことを指してモジュールと呼ぶことは少ないでしょう。

オブジェクトレベルでのモジュール化

オブジェクトレベルでのモジュールを実現しているのは、「オブジェクトとリンク」です。これはビルド時に開発環境が自動で行ってくれるので、 lib や dll を利用しない限りは開発者でもあまり意識することがありません。

ソースファイルはコンパイラによりコンパイルされてオブジェクトになり、それをリンカーが依存関係通りにリンク(接続)して実行可能形式やライブラリ形式に出力します。このときのオブジェクトは一種のモジュールです。

また、ライブラリ形式のものは通常はそのままライブラリと呼ばれることが多く、特にプログラムの実行時に動的にリンクするものはプラグインなどと呼ばれることが多いため、モジュールと呼ぶことはあまりありません。

アプリケーションレベルでのモジュール化

上記とは少し意味が違い、かつ最も一般にモジュールと呼ばれるのがアプリケーションレベルでのモジュールです。これを実現するのは、「アプリケーションと通信」です。

アプリケーション同士が連携を取って一つのことをなす場合、それらの個々のアプリケーションのことをモジュールと呼びます。連携には共有メモリや、プロセス間通信、ウィンドウメッセージ、ソケット通信などの方法を使います。

JavaScript (ブラウザ上)のモジュールとは

JavaScript の場合、 C++ 言語のようなビルドの手順がありません。すべての JavaScript ファイルは、ブラウザに読み込まれた順番でファイルの先頭から一行ずつ実行されていきます。

C++ 言語を理解している人になんとなくイメージで伝えるなら、読み込まれた順番でソースファイルが結合されていくと思えばわかりやすいかもしれません。全体を通して一つのプログラムとなるので、独立性がありません。

そのため、後述する ES Modules 等の仕様が策定されるまでの長い間、それぞれのソースファイルには独立性が皆無で、接続性が高すぎる状態でした。HTML に JavaScript を記述すれば、定義されたデータは他のソースファイルからも使うことができたし、同じ名前のデータがかぶって不具合を起こすこともありました。

しかし、JavaScript を利用した Web 技術は需要が高まり、規模の大きいプログラムを作る上で生産性を高めるためには、やはりモジュールの仕組みが必要でした。

独立性皆無で、接続性が高すぎる JavaScript でのモジュール化とは、「独立性を高め、接続性を程よいところまで制限すること」にありました。

最もシンプルなモジュールパターン

最もシンプルな手段は、モジュール化したい処理をオブジェクトにまとめ、それをグローバルスコープ上に置くというものです。有名なライブラリで言えば、 jQuery なんかがそのような方法を取っています。

また、内部処理用のデータや関数は外部から利用されたくないですし、勝手に上書きされても困ってしまいます。そのため、内部処理を隠蔽する方法として、即時関数という技術を利用することが多いです。また、こうやって内部処理だけを隠蔽することをカプセル化といいます。

これにより、オブジェクトをある程度独立させた上で、接続性を程よいバランスに制限することができました。

モジュールを一括管理するライブラリを使用する

さて、それ以外にも、モジュールを一括管理するための仕組みを提供しているライブラリものもあります。require.js がその筆頭だと思います。いろいろなメリットがありますが、モジュールそのものだけではなく依存モジュールの読み込み機能を備えていることが多く、単純なモジュールパターンよりも使い勝手が良くなっています。

require.js について

require.js では、 define 関数を利用してモジュールの処理を書き、外部に公開したいものだけをまとめて戻り値に指定します。他のモジュールは、 require 関数を利用してそれを取得することができます。

どちらかというと C 言語等に近いように設計されていて、まずはエントリーポイント(一番最初に実行されるソースファイル)を指定し、require.js に読み込ませるという方法を取ります。

ウェブページを作る人は、エントリーポイントに指定するソースファイルをまず作って、その中で利用したいモジュールを列挙し、必要に応じてそれらのモジュールを利用するスクリプトを書きます。すると、そこから連鎖する形で順々にモジュールを読み込んで処理されていき、それらが揃うと最後にエントリーポイントに書いた処理が実行されます。

言語仕様 ES Modules を使用する

ES2015 で、 ES Modules という新しい仕様が策定されています。2017年現在ではまだ標準対応しているブラウザがほぼありません。現状では、 ES Modules の記述形式て書いたものを、対象ブラウザ向けにコードを変換する仕組み(Babel 等)や一つのソースファイルにまとめる仕組み(webpack等)を利用して、非対応ブラウザで動作するように変換して使用するのが普通のようです。

正直、トランスパイルを使った開発は私はやったことがないので、そこまで詳しくは知りません。

さて、この JavaScript の新しい仕様で何ができるようになるかというと、関数やオブジェクトではなく構文を使ってモジュール化することができるようになります。

import 〇〇 from ▲▲
export 〇〇 ◆◆◆◆

などのような構文でインポートとエクスポートが可能になります。ファイルの動的読み込みもでき、個人的にはとても期待している仕様です。

ただし、script タグに type="module" と書くとかなんとかいう情報も見かけますし、もしかしたら通常の type="text/javascript" としては使えない可能性があります。

個人的に思うこと

モジュールという仕組みは、そこそこな規模のプログラミングをする上でとても重要なものだと思っています。でも、いろんなやり方があって、どうしようか正直迷ってしまいます。

ES Modules が誕生する以前からモジュールという仕組みを欲していた人たちが、 AMD という「みんなこの形でモジュール作って、公開し合おうぜ」という仕様の声掛けを始め、コミュニティができ、多くのモジュールが作られ、公開され、もう事実上の標準とも言えるほど利用されています。

たぶんですが、言語仕様としての登場が遅すぎて「今さらになって ES Modules に乗り換えるのはなあ……」と思っている人も、かなりの数いると思います。

でも、今からさらに数年後の開発者たちにとっては言語仕様としてモジュールが対応していることは大きなメリットとなると思います。今の時代、古い時代の人のためというよりは、将来の開発者のためにある仕様なのかもしれません。

これから多くのブラウザが ES Modules に対応するようになって、有志の誰かが既存の方法と ES Modules の共存を可能にし、それから「新しく作るなら ES Modules」という時代がやってきて、徐々に ES Modules に一本化されていくんじゃないかなーと想像しています。

現段階からモジュール開発を始めるなら、まあ require.js か何かのライブラリを使っておいた方が無難かなーと思います。トランスパイルを行うのはメリットもたしかに大きいのですが、やっぱりライトユーザーや初心者には敷居が高いものだと感じてしまいます。

アレコレ気にせず気楽に開発できるのが JavaScript のいいところだと思います。

ES Modules が主要ブラウザにあらかた対応してくれれば、それが一番いいと思うんですけどね。

0 件のコメント :

コメントを投稿