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

O'REILLYの"JavaScript"を読む 第10章 モジュールと名前空間

第10章まできました。まだまだ道半ば。
JavaScriptの言語仕様ではモジュールの作成や管理をする機構が用意されていない。なので再利用可能なモジュールを書くということはあるルールに沿ってモジュールを書くということ。
10章は主にそのルールについて書かれている。

最も重要なこと

モジュールと名前空間の生成

言語仕様で名前空間がサポートされていないので、オブジェクトを使って名前空間を実現する。
以下のコードでのやり方が、JavaScriptモジュールを記述する時に守るべき最初のルールである、「モジュールがグローバルな名前空間にシンボルを追加する場合は最大でも一つにすべきである」ということ。

// 名前空間用の空オブジェクトを生成する
// この一つだけのグローバルシンボルに他の全てのシンボルを格納する
var Class = {}
Class.define = function(data){/*コード記述*/}
Class.provides = function(o,c){/*コード*/}

このルールに関連して以下のルールが挙げられている

  • モジュールがグローバルな名前空間にシンボルを追加する場合は、どのシンボルを追加するのかをドキュメントに明記する
  • モジュールがグローバルな名前空間にシンボルを追加する場合は、シンボル名とモジュールのファイル名に明確な関係を持たせる
名前空間が衝突する可能性は0じゃないよね?どうすんのよ?

Javaのようにドメインの記述順序を逆にしたものを使えば大丈夫!

  • com.davidflanagan.Class みたいな感じ
  • 所有してないドメインを逆さまにした名前空間を定義すると偶然一致するかもなので使わないようにしよう
  • このルールはやり過ぎかもだけどこういうルールが存在することは知っておいてね

モジュールとしてのクラス

モジュールの形式には何の制限もない

  • 関数がひとつだけでもいい
  • 関数じゃなくてクラスでもいい
  • 当然関数とクラスをまとめたものでもいい
  • 複数のクラスから構成されててもいい

モジュール初期化コード

モジュールは単に関数を定義しただけのものではなく、最初の読み込み時に名前空間の設定や名前空間にプロパティを追加するコードが実行される。関数やクラスを定義せずに初期化コードだけ記述することも可能。

重要なこと

グローバルな名前空間を荒らさないこと。匿名関数内にコードを記述し、定義後即呼び出すとこれが実現できる

名前空間からのシンボルのインポート

一意な名前空間を使うと関数名が長くなってしまう!→関数を別の変数やプロパティに保存してしまおう!

// 名前を短くして、入力を楽に
var defineClass = com.davidflanagan.Class.define;

グローバルな名前空間ではなく、もっと短い名前空間にシンボルをインポートするという発想もある

// 単純な名前空間を生成
var Class = {};
// この名前空間にシンボルをインポート
Class.define = com.davidflanagan.Class.define;
注意「インポートできるシンボルは、関数やオブジェクト、配列を参照しているものだけ」

数値や文字列のような基本形の値を持つシンボルをインポートすると値がコピーされるだけ。名前空間中のシンボルのもつ値を変更してもインポートしたコピー側には反映されない。

モジュール開発者が取るべき方法

モジュール内で基本形の値を参照するプロパティを定義するのであれば、インポート可能なアクセサメソッドを定義すべき

// 基本形のプロパティ
com.davidflanagan.Class.counter = 0;
// アクセサメソッド
com.davidflanagan.Class.getCounter = function() {
   return comdavidflanagan.Class.counter;
}
インポートはモジュール利用者側のためのものである

モジュール開発者は常にシンボルの完全修飾名を使わなくてはならない

パブリックシンボルとプライベートシンボル

JavaScriptには名前空間における、あるプロパティだけをパブリックにする方法は用意されていないため、ルールをつくってプライベートプロパティとパブリックプロパティを分ける。ドキュメント書いたりプライベートシンボル名はアンダースコアで始めてみたりとか*1

プライベートな名前空間とスコープとしてのクロージャ

クロージャを使ってローカルなスコープをプライベートな名前空間として使う。

  • メリット
    • 同じ名前空間中の関数からは他のプロパティにアクセスする際に完全修飾名を使う必要がない
    • 名前空間が本当にプライベートになる
      • 関数の外側から関数内で定義されたシンボルにアクセスする方法がなくなる。外部から利用出来るようになるのは、シンボルを含む関数がパブリックな名前空間にシンボルをエクスポートしている場合のみ。
var com;
if(!com) com = {};
if(!com.davidflanagan) com.davidflanagan = {};
com.davidflanagan.Class = {};

// この名前空間には直接シンボルを設定せず、クロージャを生成する匿名関数を定義して呼び出す
(function() {
  function define(data) {/*コードを記述*/}
  function provides(o,c) {/*コードを記述*/}

  var counter = 0;
  function getCounter() {return counter;}

  // パブリックな名前空間にエクスポート
  var ns = com.davidflanagan.Class;
  ns.define = define;
  ns.provides = provides;
  ns.getCounter = getCounter;
})(); // 定義が終了したのですぐに呼び出す

ということで

はてな記法も少しづつ覚えてきた

*1:こんなので分けられるとは思わないし事故が起こると思う