(〜・◇・)〜 o O ( 闇色 JavaScript イディオム〜
(ε・◇・)з o O ( 最近ブログ書いてないなー、そろそろ書かないとなー
(ε・◇・)з o O ( JavaScriptイディオム集 が大人気かぁー、もうすぐブクマ1000個とかすごいなー
(ε・◇・)з o O ( よーし。便乗して、ボクも手持ちの闇色な奴を幾つか紹介するよー
window.onload をもっと使い倒したい?
(ε・◇・)з o O ( window.onload には、コールバック関数を1つしか設定できないという昔からの制限があるよね?
(ε・◇・)з o O ( そこを中央突破ですよ!
__defineSetter__("onload", function(callback) { addEventListener("load", callback); });
(ε・◇・)з o O ( はい、これで何個でも設定できちゃうね
onload = function() { alert(1) } onload = function() { alert(2) } onload = function() { alert(3) }
(ε・◇・)з o O ( 動かないブラウザもあるよ!
(ε・◇・)з o O ( 自分で調べてみてね!
js にも (なんちゃって) begin ... end ブロックがほしい?
(ε・◇・)з o O ( はい、これで、begin と end できちゃうね
__defineGetter__("begin", function() { return 0; }); __defineGetter__("end", function() { return 0; }); begin alert("hello"); end
(ε・◇・)з o O ( びっくりだね! グローバル変数を闇色に染めれば色々夢が広がるね!
特定のコードブロックをコンパイル時に削除したい? (#ifdef 〜 #endif したい?)
(ε・◇・)з o O ( 副作用の無い形でコードにメタ情報(例: ここモバイルでは不要 とか)を埋め込んでおき、
(ε・◇・)з o O ( 状況に応じてコードブロックをバッサリと消せると助かるよね!
(ε・◇・)з o O ( C言語のプリプロセッサ #ifdef 〜 #endif 的にコードをストリップするトリックだよ
(ε・◇・)з o O ( 例えば、コードブロックの前後を↓のように謎のラベル行で囲っておき…
{label:true ... }label:true
(ε・◇・)з o O ( このコードを、preprocess関数に渡して余計なコードをストリップすると、スッキリするだよ!
// preprocess.js (function(global) { function preprocess(codeFragment, // @arg Function/String: code fragment labels) { // @arg String/StringArray: "label" or ["label", ...] var result = codeFragment + ""; var ary = Array.isArray(labels) ? labels : [labels]; ary.forEach(function(label) { var multiLine = RegExp("\\{" + label + "\\:true\\b" + "(?:[^\\n]*)\n(?:[\\S\\s]*?)?" + "\\}" + label + "\\:true\\b", "g"); // {label:true // ... // }label:true result = result.replace(multiLine, ""); }); // strip unnecessary labels return result.replace(/\{\w+\:true.*/g, ""). replace(/\}\w+\:true.*/g, ""); } if (typeof module !== "undefined") { module.exports = { preprocess: preprocess }; } global.preprocess = preprocess; })(this.self || global);
(ε・◇・)з o O ( こういった各種の事情が混じったコードを…
// src.js var browser = { ie: false, android: false }; function hoge() { {__PC__:true // PCブラウザで必要なコードブロック(モバイルブラウザでは不要なコードブロック) if (browser.ie) { } }__PC__:true {__MOBILE__:true // モバイルブラウザで必要なコードブロック(PCブラウザでは不要なコードブロック) if (browser.android) { } }__MOBILE__:true alert("Hello ifdef"); // 全てのブラウザで実行されるコード {__DEBUG__:true // デバッグビルドで有効なコードブロック(リリースビルド時には不要なコードブロック) alert("debug code"); // デバッグビルドで実行されるコード }__DEBUG__:true }
(ε・◇・)з o O ( モバイルブラウザ向けにリリースビルドするなら、こんな感じかな
// prebuild.js var fs = require("fs"); var preprocess = require("./preprocess.js").preprocess; var src = "./src.js"; var dest = "./dest.js"; var code = fs.readFileSync(src, "UTF-8"); var result = preprocess(code, "__PC__ __DEBUG__".split(" ")); fs.writeFileSync(dest, result);
$ node prebuild.js $ closure-compiler --js_output_file dest.min.js --js dest.js
(ε・◇・)з o O ( コンパイラに怒られないように、余計なラベル(__MOBILE__:true)は削除してあるよ
(ε・◇・)з o O ( dest.js と dest.min.js はこうなってるよ
(ε・◇・)з o O ( dest.js の空行が気になるね…
// dest.js var browser = { ie: false, android: false }; function hoge() { if (browser.android) { } alert("Hello ifdef"); // 全てのブラウザで実行されるコード }
// dest.min.js var browser={ie:!1,android:!1};function hoge(){alert("Hello ifdef")};
(ε・◇・)з o O ( JavaScript は { ... } でスコープが生成されないので、それを逆手に取ってる感じだね
「undefined よりも void 0 を使うべき」かと言われると、うーん、そろそろそーでもないかも
ボクも2008年ごろは「undefined → void 0 使うべき」とか言ってました(コメント欄参照)。
クロスブラウザ(+node.js)で動作するコードを書く場合は、いまだに void 0 か var undef; を使ったコードが必要なので、良く使います。
(function(global) { function hoge(arg) { if (arg === void 0) { ////// } } function piyo(arg) { var x = 0, y = 0, undef; ///// if (arg === undef) { ///// } } })(this.self || global);
とは言っても、ES5がデフォルトの時代になりつつあり、
global.undefined は上書きできない存在になったため、IE のお相手をする必要がない場合は、普通に undefind を使っても良いかなと。
(function(global) { function hoge(arg) { if (arg === undefined) { ///////// } } function piyo(arg) { var x = 0, y = 0; if (arg === undefined) { ////////// } } })(this.self || global);
関数呼び出し時の引数を意図的に1つ少なく指定することで、関数スコープ内の変数として undefined を用意する方法もありますが、
ES5の時代にはむしろ邪魔になるので、使わないほうが良いと思います。
(function(global, undefined) { ////////// })(this.self || global);
(ε・◇・)з o O ( 全てボクひとりが勝手に言ってるだけの、オレオレなので〜