青空コメントアウト

WEBのこと、デザインのこと、ご飯のこと、趣味のこと。青空の向こうの誰かに届きますように。

CommonJSのモジュール管理「require」の使い方が100%わかる話

このエントリーをはてなブックマークに追加

f:id:cocoro27:20160628231143p:plain

わかったつもりよさようなら。
あろえりーな(@aloerina_)です。

かつてJavaエンジニアだったあろえは、まだJavaScriptとあまり仲良くなれていない頃にrequireを使おうとしてハメられたハマったことがありました。そんなことをふと思い出したので、クラスベース100%脳だからこそ(?)ハマったJavaScriptのモジュール化について、あの頃の自分へ向けてまとめようと思います。

問題となったソースがこれです(簡略化しています)。

どこが間違いか分かりますか?
きちんとルールを知っていれば明らかな間違いなのですが、クラスベースの感覚(&JavaScriptのスコープの広さ)で考えると、main.jsListViewを参照できるような気がしてしまいました。ECMAScript 6 を生半可に知ってclass構文を迂闊に使ったことも相まっての失敗でした。愚かなり…。




そもそもモジュール化とは?

サーバサイドを実装するときは当然のようにしているモジュール化ですが、これは簡単に言うと「プログラムを小さな部品に分けてつくること」です。車に例えて、エンジン、ボディ、タイヤと分けて作って最後にひとつに組み上げるのと同じ…なんて説明をよく聞きます。

で、JavaScriptでも同じことをしようという話です。フロントサイドで動かす小さなスクリプトなら、

<script src="list-view.js"></script>
<script src="main.js"></script>

とHTMLに各モジュールを書き並べる(各モジュールの依存関係を管理する)方法もありますが、①サーバサイドでJavaScriptを使う場合、②大規模なプロジェクトをつくる場合、などこの方法だと不都合があります(①に関してはHTMLが存在しないし)。

で、それを解決する方法のひとつが今回のテーマのexports/requireの仕組みです。「方法のひとつ」とあるとおり他にも方法はありますが、Node.jsで利用される手法であるおかげでよく見るようになった構文なので、きちんと理解しておくべきものだったなと痛感しております…。ちなみにその他の方法を学ぶ基礎としても役立ちます(役立ちました)。




exports/require は CommonJS からやってきた

「ときどきrequireとか見かけるから真似して書いてみたけど、そんな関数はないって怒られたよ!(怒)」

なんて経験、最初は誰でもありますよね(あるよね…?)。一体何者なんだと思いましたよあろえは。これはCommonJSというプロジェクトに定められた構文であって、その構文を実行できる環境でないと利用できないものなのです。

CommonJS とは
JavaScriptって自由すぎる言語だから、ある程度使い方の共通認識を持とう!という思想のプロジェクト。勘違いされがちだけどライブラリではない。規定やルールのようなものだと思っておくと6割くらい正しい。

このCommonJSを実行できる環境のひとつがNode.jsです。
上のサンプルコードも$ node main.jsとすると実行できます。
ちなみにブラウザで実行できるようにコンバートする仕組みがBrowserifyです。


閑話休題。

CommonJSによって定められたもののひとつが、モジュール管理のための exports/require という仕組みです。この仕組をフル活用しているNode.jsのリファレンスにはこう書いてあります。

exportsは現在のモジュールの全てのインスタンス間で共有されるオブジェクトで、requireを通じてアクセス可能になります。exportsmodule.exportsと同じオブジェクトです。

ざっくり言うと「exportsのプロパティに追加した関数は、requireを使って呼び出せるよ」ということです。で、これを知らずに見様見真似でrequireを使おうとすると最初のような動かないプログラムが出来上がるわけです。そう、requireはexportsとセットで使うものなのです。




最初の間違ったコードの正解は…

3行目のListViewという関数をexportsへ渡してあげることで、main.js側で無事呼び出せるようになります。
いやーハマったハマった。何が難しいって、newする箇所で落ちるもんだから、コンストラクタの定義とか呼び出し方にばかり目が言ってて、依存関係でコケてるとなかなか気づけなかったのです…(情けない)。




余談 「classはクラスじゃない」

ES6でclass構文が出て、クラスベース脳のあろえは一瞬狂喜したけれど、これは別にクラスベースになったわけではないんですよね。プロトタイプベースのJavaScriptをクラスベースっぽく書けますよってだけのもの。言ってしまえばコンストラクタとprototypeメソッドを定義するための簡単な仕組みってだけです。

だから良いとか悪いとかって話ではなくて、ろくに中身を知らずに見様見真似でソース書くと痛い目見ますよ(見ましたよ)って話でした。
おしまい。


TOP