Closure Compilerを使う!

extern宣言

最終更新:

aias-closurecompiler

- view
管理者のみ編集可

トップページ > 高度なトピック >

extern宣言

externとは、サードパーティのライブラリなどの外部コードが定義するシンボルの名前を宣言し、 ADVANCED_OPTIMIZATIONS レベルでコンパイルされるコード内でそれらをClosure Compilerのリネーム処理から保護する機能です。externが必要な状況がよく分からない場合は、まずこちらを参照してください。

externはClosure Compiler Application及びClosure Compiler Service APIで利用できます。Closure Compiler Service UIはexternのためのインターフェースを提供していません。


以下のJavaScriptは、extern宣言を必要とするコードを含んでいます:

/**
 * あるページに対し注記のリストを追加するシンプルなスクリプト。
 * リストには注記のタイトル、その下に本文が表示される。
 */


/**
 * 注記のためのDOM構造を作成し、それをドキュメントに追加する。
 */

function makeNoteDom(noteTitle, noteContent, noteContainer) {
   // 注記を表すDOM構造を作成する。
  var headerElement = textDiv(noteTitle);
  var contentElement = textDiv(noteContent);
  
  var newNote = document.createElement('div');
  newNote.appendChild(headerElement);
  newNote.appendChild(contentElement);
  
   // 注記のDOM構造をドキュメントに追加する。
  noteContainer.appendChild(newNote);
}

/**
 * 注記データオブジェクトのリストに対し反復処理を行い、DOMを作成する。
 */

function makeNotes(data, noteContainer) {
  for (var i = 0; i < data.length; i++) {
    makeNoteDom(data[i].title, data[i].content, noteContainer);
  }
}

function main() {
  var noteData = [
      { title: 'Note 1', content: 'Content of Note 1' },
      { title: 'Note 2', content: 'Content of Note 2' }];
  var noteListElement = document.getElementById('notes');
  makeNotes(noteData, noteListElement);
}

main();

textDiv() 関数はサードパーティがメンテナンスする textops.js という別のファイルに宣言されているものと想定します。それは次のような関数です:

function textDiv(text) {
  var divElement = document.createElement('div');
  var textElement = document.createTextNode(text);
  divElement.appendChild(textElement);
  return divElement;
}

textDiv() 関数はコンパイル対象外のファイルに定義されておりClosure Compilerによってリネームされたくないので、extern宣言することにします。

extern宣言は、以下のように関数を定義する単純なJavaScriptです。関数の内容は空で構いません:

function textDiv(text){};

このコードを直接パラメータに設定するか、またはコードを記述したJSファイルをパラメータに設定することで、宣言をClosure Compilerに渡すことができます。具体的な方法は「Closure Compiler Service APIでexternを宣言するには」、「Closure Compiler Applicationでexternを宣言するには」を参照してください。

このJavaScriptをClosure Compilerに与えたとしても、それ自体が出力結果に含まれることはありません。このJavaScriptの唯一の目的は、Closure Compilerにこう伝えることです:"我々のコードはお前の知らないどこかで定義されている textDiv() という名前の関数を使っている。だから textDiv() への呼び出しをリネームしてはならない。"

上のJavaScriptとexternをClosure Compilerに送ると、次のような出力が得られます。尚Closure Compilerがexternをどのように適用しているかをわかりやすくするため、出力結果に改行を挿入しています:

for(var a=document.getElementById("notes"),b=[{title:"Note 1", content:"Content of Note 1"}, {title:"Note 2", content:"Content of Note 2"}], c=0;c
  var d=textDiv(b[c].title),e=textDiv(b[c].content),f=document.createElement("div");
  f.appendChild(d);
  f.appendChild(e);
  a.appendChild(f)
};

Closure Compilerは元のプログラムを劇的に変化させましたが、 textDiv() 関数の呼び出しはオリジナルの名称のままで残っています。従ってこのコードは textDiv() が定義されている、コンパイルされていない textops.js と連携して動作させることができます。

externをエクスポートの代用にしないでください!

externはシンボルをリネームから保護するとても便利な方法なので、"エクスポート"の代わりにこれを使いたいと思ったかもしれません。外部コードがその中の関数を呼び出せるようなAPIをコンパイルして公開したいとあなたが考えているなら、公開されるAPIを全てextern宣言すればよいのではないでしょうか。

しかし、そうすべきではありません。コンパイル済みファイル内の関数を外部公開する場合には、残しておきたいシンボルをエクスポートするで説明されているエクスポートによる手法をとってください。

エクスポートはexternよりも高い圧縮率を実現します。Closure Compilerはextern宣言されたシンボルを、それがコード内に何度出現するかに関係なく絶対にリネームしません。つまりexternされた長い名前がコンパイルされたコードの中に多く残るということです。対照的にエクスポートでは、シンボルのフルネームはコード内にひとつしか含まれません。このフルネームのシンボルは短縮名の別名となっているので、コード内では常に短縮名を使うことができます。

デフォルトのextern宣言

Closure Compilerは以下に示すシンボル群を内部的にextern宣言しており、デフォルトではそれを使用してシンボル名の保護を行います。

  • Math RegExp のようなJavaScriptの組み込みオブジェクトとそのメンバ
  • eval() parseInt() のようなJavaScriptの組み込み関数
  • window location のようなブラウザの標準的な組み込みオブジェクトとそのメンバ
  • document をはじめとするDOM APIに含まれる全てのオブジェクトとそのメンバ

Closure Compiler service APIのexclude_default_externsパラメータ、Closure Compiler Applicationの--use_only_custom_externsオプションを指定すると、これらデフォルトのextern宣言を使用させないようにすることができます。

Closure Compiler Service APIでexternを宣言するには

Closure Compiler Service APIでextern宣言を行う方法は2つあります。

  • js_externsパラメータでJavaScriptコードをClosure Compilerに渡す
  • externs_urlパラメータでJavaScriptファイルのURLをClosure Compilerに渡す

js_externs externs_url の違いは、サービスがJavaScriptを取得する手段だけです。 js_externs externs_url は1つのリクエスト内で同時に使用でき、またそれぞれを複数指定することが可能です。

js_externsパラメータでJavaScriptコードをClosure Compilerに渡す

宣言するexternの数がそれほど多くないのであれば、サービスへのリクエスト内で js_externs パラメータの値としてexternを宣言するJavaScriptを指定するのがよいでしょう。以下に例を示します:

#!/usr/bin/python2.4


import httplib, urllib, sys

# Define the parameters for the POST request and encode them in
# a URL-safe format.

params = urllib.urlencode([
    ('code_url', sys.argv[1]),
    ('compilation_level', 'ADVANCED_OPTIMIZATIONS'),
    ('output_format', 'text'),
    ('output_info', 'compiled_code'),
    ('js_externs', 'function textDiv(text){}'), # <-- New parameter!
    ('formatting', 'pretty_print')
  ])

# Always use the following value for the Content-type header.

headers = { "Content-type": "application/x-www-form-urlencoded" }
conn = httplib.HTTPConnection('closure-compiler.appspot.com')
conn.request('POST', '/compile', params, headers)
response = conn.getresponse()
data = response.read()
print data
conn.close

リクエストには複数の js_externs パラメータを含めることができます。また1つのパラメータ内にカンマ区切りで複数のextern宣言を含めることもできます。

externs_urlパラメータでJavaScriptファイルのURLをClosure Compilerに渡す

宣言すべきexternの数が多い場合、それらを全て js_externs パラメータで渡すのはかなり無理があると思われます。このケースでは以下の方法をとるのがよいでしょう:

  1. 全てのextern宣言を独立したJavaScriptファイルに記述します。
  2. このファイルをWebサーバにアップロードし、外部からアクセス可能な場所に配置します。
  3. アップロードしたファイルのURLを externs_url パラメータの値としてClosure Compiler Service APIに渡します。

例を示します:

params = urllib.urlencode([
    ('code_url', sys.argv[1]),
    ('compilation_level', 'ADVANCED_OPTIMIZATIONS'),
    ('output_format', 'text'),
    ('output_info', 'compiled_code'),
    # Different parameter:
    ('externs_url', 'http://www.myserver.com/myexterns.js'),
    ('formatting', 'pretty_print')
  ])

リクエストには複数の externs_url パラメータを指定できます。Closure Compilerは js_externs のJavaScriptコードおよび externs_url のファイルに含まれる全てのextern宣言を結合して処理します。

Closure Compiler Applicationでexternを宣言するには

externファイルの名前をexternsオプションに指定し、Closure Compiler Applicationに渡してください。 externs オプションの指定は、以下のように1ファイルずつ行います:

java -jar compiler.jar --compilation_level ADVANCED_OPTIMIZATIONS --js makeallnotes.js --externs extern1.js --externs extern2.js


目安箱バナー