Closure Compilerを使う!
extern宣言
最終更新:
aias-closurecompiler
extern宣言
externとは、サードパーティのライブラリなどの外部コードが定義するシンボルの名前を宣言し、 ADVANCED_OPTIMIZATIONS レベルでコンパイルされるコード内でそれらをClosure Compilerのリネーム処理から保護する機能です。externが必要な状況がよく分からない場合は、まずこちらを参照してください。
externはClosure Compiler Application及びClosure Compiler Service APIで利用できます。Closure Compiler Service UIはexternのためのインターフェースを提供していません。
-
このページは公式サイトの以下のページを元に作成しました。
http://code.google.com/closure/compiler/docs/api-tutorial3.html
以下の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をどのように適用しているかをわかりやすくするため、出力結果に改行を挿入しています:
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 パラメータで渡すのはかなり無理があると思われます。このケースでは以下の方法をとるのがよいでしょう:
- 全てのextern宣言を独立したJavaScriptファイルに記述します。
- このファイルをWebサーバにアップロードし、外部からアクセス可能な場所に配置します。
- アップロードしたファイルの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
/**
* あるページに対し注記のリストを追加するシンプルなスクリプト。
* リストには注記のタイトル、その下に本文が表示される。
*/