2007.7.9

Javascriptのブックマークレットに見る無名関数の使われ方

村式ブログ

こんにちは。中川です。

最近Javascriptが流行っているようで、関連する記事やコードを見かける機会が増えました。
最近になって興味を持ち始めた方、急に仕事でやるハメになった方、結構多いのではないでしょうか。
Javascriptは書式こそC言語やJavaに似ていて馴染みやすいように見えますが、中身はかなり違ってます。
はじめてJavascript特有の書き方に触れたときに戸惑ったことがある方も多いと思います。

私が他人のコードを見ていて一番最初に「なんだこれは?」と思ったのは、あるブックマークレットのコードを見たときでした。
どんなコードだったかは忘れましたが、書式はこうでした。

javascript:(function(){/* statements */})();    ...[1]

最後の()はなんだろうか?
関数は

function f(x){/* statements */};    ...[2]

と定義するのではなかったか?
定義した後に

f(2);    ...[3]

のように書いてコールするのではないのか?
関数の中身は分かっても、なぜこれで動くのかが理解できませんでした。
そしてなぜこんな分かりにくい書き方をするのか。

この奇怪?な書き方には理由があります。
謎を紐解いていきましょう。

はじめに、最初の「javascript:」という部分はブラウザにJavascriptを実行させるための命令です。
こう書くと、ブラウザが残りのコードを実行してくれます。
このことについてここでは詳しく触れません。残りのコードを見ていきましょう。

Javascriptでは、関数型のオブジェクトを作ることができます。
以下のような書式になります。

// 関数型のオブジェクト
function(x){/* statements */};    ...[4]

このオブジェクトを無名関数と呼びます。
無名関数はオブジェクトですので、他のオブジェクトと同じように名前をつけることができます。

// 無名関数を作ってそれに"f"という名前をつける
var f = function(x){/* statements */};    ...[5]

実は最初に出てきた[2]とこの[5]はまったく等価です。
[2]は[5]のシンタックスシュガーに過ぎません。
C言語やJavaなどから来た移民を取り入れるための罠です。
従って、[5]の後に

// 関数をコールする
f(2);    ...[6]

とすればこれをコールすることができます。
[5]の右辺はfと等価なので、f(2)とする代わりにこんなこともできます。

// 名前をつけずにいきなりコール
(function(x){/* statements */})(2);    ...[7]

[5],[6]と[7]をよく見比べてください。
前半の括弧の中身が無名関数で、後半が引数です。
ここまでで[1]のブックマークレットがなぜ動くのか、理解できると思います。
ブックマークレットでなくとも、[7]のような書き方をしているケースは最近特によく良く見かけます。
以上のことを知っていれば、読めるようになるコードが増えることでしょう。

では、なぜわざわざそんな読みにくい書き方をするのでしょうか。
Javascript使いは変な人が多いから?
それは半分当たっていますが理由は違います。

[5],[6]と書くのと[7]だけ書く場合で決定的に違うのは「f」という名前が登場するかしないかです。
ブックマークレットで使う場合、「f」という名前はそれを実行するwindowのスコープに入ります。
簡単に言うと、開いているwebページのグローバルなスコープに「f」が割り込むということになります。
そのページでもし「f」という名前が使われていたら互いに干渉してしまう恐れがあります。
昔のブックマークレットの中には、これを避ける為に誰も使わなさそうな長い変数名を使っている物もありましたが、
新しい名前を登場させずに処理を実行するために[7]のような書式にしているのです。

これで疑問は解決したでしょうか?
一度知ってしまえば簡単なことですね。

最後におまけとしてブックマークレットを置いておきます。
そのページのクッキーを表示するブックマークレットです。
文字数を削るために少し変な書き方をしていますが、これならもう読めますね。

javascript:(function(d,c){c=d.cookie;d.writeln(c?c.replace(/;/g,';<br />'):'no cookie.');})(document);

この記事を書いた人

中川尚(メガネ)