latest log

酩酊状態で書いたエンジニアポエムです。酩酊状態で読んでください。

(〜・◇・)〜 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 ( 全てボクひとりが勝手に言ってるだけの、オレオレなので〜