jQueryプラグインの先頭にセミコロンがある理由

jQueryプラグインの先頭にセミコロンがある理由

Posted at April 9,2013 12:33 AM
Tag:[JavaScript, jQuery]

jQueryプラグインの先頭にセミコロンがある理由を調べてみました。

jQueryプラグインの先頭にセミコロンがある理由

1.「jQueryプラグインの先頭にセミコロン」とは?

例えば、レスポンシブ対応で画像を拡大表示できる「SuperBoxプラグイン」のコードは次のようになっています。

;(function($) {
 
    $.fn.SuperBox = function(options) {
 
        var superbox      = $('<div class="superbox-show"></div>');
        var superboximg   = $('<img src="" class="superbox-current-img">');
        …中略…
        });
    };
})(jQuery);

1行目の行頭にセミコロンがついています。

;(function($){

直感的に「エラー防止用かな?」と思って調べてみたらそのとおりだったのですが、以下調べたことについて解説します。

2.セミコロンの省略と自動補完について

このエントリーの本題ではないので詳細は割愛しますが、JavaScriptはセミコロンの省略が可能で、省略した場合、処理系でセミコロンの自動補完を行ってくれるようです。

たとえば次のようなコードは正常に動作します。

var foo = "foo"
console.log(foo)
var bar = "bar"
console.log(bar)

すべての改行で補完されるわけではないようですが、ここでお伝えしたいのは「セミコロンが省略されたコードも存在する」ということです。

なお、Google JavaScript Style Guide 和訳ではセミコロンを省略してはいけないように記載されています。

常にセミコロンを使います. コードのセミコロンを省き, セミコロンの挿入を処理系に任せた場合, 非常にデバッグが困難な問題が起こります. 決してセミコロンを省くべきではありません.

3.jQueryプラグインの先頭にセミコロンがある理由

jQueryプラグインはひとつの独立したファイルとして扱うこと以外に、パフォーマンス向上などのため、他のJavaScriptコードと混在させて1つのファイルにまとめるケースもあると思います。

で、混在させて、さらに圧縮・難読化などでコードを1行にしたときに直前のコードにあるべきセミコロンがなかった場合、エラーになる可能性が高いです。

例えば、

console.log(bar)

の後方にjQueryプラグイン

(function($) {
   …後略…

を貼り付けて1行にすると、

console.log(bar)(function($) { …後略…

となり、処理系で補完されないため、実行時にエラーとなります。

そのような不測の事態を発生させないようにするため、jQueryプラグインの先頭にセミコロンをつけているようです。

console.log(bar);(function($) { …後略…

4.予期しない動作になるサンプル

3項では1行化を行ったケースについて触れましたが、1行化を行わない(=改行が含まれる)場合でもセミコロンがないことにより予期しない動作になることがあります。

jQueryプラグインの先頭にあるセミコロンは、そのケースも考慮しているように思われます。

その具体的なケースについて、「Google JavaScript Style Guide 和訳」のサンプルを使って解説します。

jQuery で ;(function () ...と、セミコロンで始めることがあるのはなぜか」で別のサンプルによる解説を参考にさせていただいてますが、認識誤りがありましたらどこかでつぶやいてください。

「Google JavaScript Style Guide 和訳」には次のようなサンプルが掲載されています。

MyClass.prototype.myMethod = function() {
    return 42;
}  // ここにセミコロンがない
 
(function() {
    // この一時的なブロックスコープで初期化処理などを行う
})();

このコードについて次のように解説されています。

JavaScript Error: はじめの 42 を返している無名関数が, 2つ目の関数を引数にとって実行されてしまい, 42 を関数として呼び出そうとしてエラーになる.

まず、前半の「はじめの 42 を返している無名関数が, 2つ目の関数を引数にとって実行されてしまい」までを解説します。

理解を容易にするため、コメントの一部を省略して次のようにします。

MyClass.prototype.myMethod = function() {
    return 42;
}
(function() {
    // ...
})();

2項でセミコロン補完の話をしましたが、このコードの3行目でセミコロンは補完されず、次のコードとして解釈されます。

MyClass.prototype.myMethod = function() {
    return 42;
}(function() { // ... })();

これで無名関数は即時関数「 (function(){...})();」として解釈されてしまうようです。

分かりにくいのですが、即時関数は定義部分のカッコを省略できるらしく、このコードで定義部分というのは「function() { return 42; }」が該当します。

つまり、上記のコードで省略されたカッコをつけると、

MyClass.prototype.myMethod = (function() {
    return 42;
})(function() { // ... })();

となります。

そして、この即時関数は「function() { // ... }」を引数として実行してしまいます、というのが前半の「はじめの 42 を返している無名関数が, 2つ目の関数を引数にとって実行されてしまい」の部分になります。

後半の「42 を関数として呼び出そうとしてエラーになる.」は、即時関数が実行されて「return 42;」で42を返却すると、イメージとしては、

MyClass.prototype.myMethod = (function() {
    return 42;
})(function() { // ... })();

(function() {
    return 42;
})(function() { // ... })

の部分が実行され、その返却値と元のコードにある後のカッコをあわせて

42();

のような形で実行されるのではないかと思います。

最後のカッコがなければ、実行結果の42はMyClass.prototype.myMethodに代入されるようです。

参考サイト「Dangers of anonymous function closures」にある次のコードでより動作が明確になると思いますので試してみてください。

var foo = function(bar) {
    console.log("foo");
    return bar;
}
(function(){
    console.log("bar")
})();

このコードを実行すると、「foo」「bar」の順で文字列がコンソールに出力されます。

5.参考サイト

参考サイトは以下です。ありがとうございました。

関連記事
zenback
人気エントリー
トラックバックURL


コメントする
greeting

*必須

*必須(非表示)


ご質問のコメントの回答については、内容あるいは多忙の場合、1週間以上かかる場合があります。また、すべてのご質問にはお答えできない可能性があります。予めご了承ください。

太字イタリックアンダーラインハイパーリンク引用
[サインインしない場合はここにCAPTCHAを表示します]

コメント投稿後にScript Errorや500エラーが表示された場合は、すぐに再送信せず、ブラウザの「戻る」ボタンで一旦エントリーのページに戻り(プレビュー画面で投稿した場合は、投稿内容をマウスコピーしてからエントリーのページに戻り)、ブラウザをリロードして投稿コメントが反映されていることを確認してください。

コメント欄に(X)HTMLタグやMTタグを記述される場合、「<」は「&lt;」、「>」は「&gt;」と入力してください。例えば「<$MTBlogURL$>」は「&lt;$MTBlogURL$&gt;」となります(全て半角文字)