JavaScriptおれおれAdvent Calendar 2014 – 01日目

最初なんで最初に書くやつの話。

(function() {
    // …
})();

これですね。これ何か呼び方あるの?

なにこれ

グローバル変数にならないようにする仕組み。

スコープ問題

JavaScriptではスコープ(変数の有効範囲)は関数単位(function(){})になるのが基本。

そして一番外側、どの関数の中でもないものは「グローバルスコープ」に所属する「グローバル変数」となり、どこからでも利用できる変数になる。そうなるとどこかで変数の名前がかぶった際、勝手に値が変わったりして大変恐ろしい事になってしまう。

そこで、ファイル全体を関数で括る事でスコープを生成して、グローバルではない変数にしておこう、というもの。ただしvar宣言なしに変数を使い始めると、結局グローバル変数になってしまうので注意。

次世代JavaScriptだとletとかあるけど触れません、関係ないし。

関数の前後にくっついてる括弧は

関数は作成するだけでは何もならないので、即時実行してやります。

var f = (function() {});
f();

を省略して

(function() {})();

です。

後ろで解説します。

最後の括弧は関数実行の括弧です。(alert()()と同じ。)

Node.jsのスコープ問題

クライアント側じゃないJavaScriptだと、普通にファイル単位のスコープ(という表現で良いのかは知らないけど)になるので、全体を括ってやる必要はないです。

問題なし。

バリエーション

無名関数を即時実行するパターン、いくつか書き方があります。

全体を括弧で括る

(function(){
    // …
}());

えーと、おわかり頂けただろうか。三行目に注目してください。最初の括弧始めに対応する終わりの位置が、関数実行の括弧の前から後ろになりました。

ちなみにこっちの方が冒頭に掲示したやつより0.5%早い、という記事が三年前にあったんだけど、ブックマークを見返したら404になってた……。たしか当時のrhinoで計測してたんだったような。

とりあえずはてブの方だけでも。

!

実行するぞという強い意志が感じられる。(???)

!function() {
    // …
}();

+

!と同じようなもの。

+function() {
    // …
}();

なぜこんな書き方になるのか

無名関数の実行はさておいて、なんで単純にfunction(){}()を付けるだけではいけないのか。それはJavaScriptにおける”function”という文字列が二つの意味を持つ事に起因します。

function文

よくあるこういう書き方、これは文です。if文やfor文と同じ。

function foo() {
    // …
}

文なので、末尾にセミコロンはいらないですね。そうですね。

function式

こっちの書き方だと式になります。

foo = function() {
    // …
};

式なので、評価して戻り値があります。

例えば+を使った式で

a = 3 + 4;

+を「評価した結果」が戻って

a = 7;

になるじゃないすか。それと同じです。さっきのやつも、function式を評価して

foo = (関数の実態);

てな感じになります。

あ、この「評価した結果の値に置き換わる」感覚大事だと思いますよ。

()も式

実はこれも式です。関数呼び出し(実行)の式ですね。

例えば-n-は「右側の数値に-1を乗じた結果を返す」という単項式です。

それと同じで、()は「左側の関数を、括弧内の引数を伴って実行し、結果を返す」という単項式なんですよ。

これだよね。

全体を括るやつは式にする

文の場合は何の値にもならないので、そのまま実行できません。if文を実行するようなもんです。

if (flag) {

}()  // ←!?

functionから書き始めると、JavaScriptの実行環境が「お、これはfunction文だな」と理解してしまいます。困ってしまいます。

なので式なんだって事にしたいわけですが、そのために「あー何か書いてるここは式の途中なんだなー」てのをわからせるために、括弧で括るのです。

括弧で括らなくても、文じゃなくて式だなーてのが読み取れれば何でも大丈夫です。先の!とか+とかがそれですね。

他にも、例えば代入式でも大丈夫。

f = function() {
    return 123;
}();

これは関数を実行した結果123fに代入されます。順に見てみましょうか。「評価順」が決まっているので、まずfunction式で関数を生成して、

f = (関数の実態)();

続いて()でその関数を実行し、

f = 123;

結果を=で代入します。

はいおしまい。

今回の件で言うと、代入されようが!で真偽逆転しようが構わなくて、ともかく「関数が実行される」というところが大切です。

まとめ

  • グローバル変数作りたくないので、全体を関数にしてスコープ生成する
  • 即時実行したいので、無名の関数をfunction文でつくる

そんな感じ

あーあーあーこんな言語仕様について触れるつもりじゃなかったのに。明日からはもっと簡単な感じで書いていきたいと思います。

(あと不正確な表現とかあると思うんだけどなんかもう面倒なのでアレです、ここら辺で放出します。すみません。マサカリはビニール製の柔らかいやつで、何卒お願いします。)