読者です 読者をやめる 読者になる 読者になる

tohokuaikiのチラシの裏

技術的ネタとか。

Confluenceのプラグイン開発を承ります。ご連絡はこちらのホームページからお願いいたします。

(function(var){..............})(var) と function(var){............} の違い。つまり、クロージャ。

<script>
(function(v){alert(v)}("aa"));
hoge=function(v){alert(v)};
</script>

ってやった場合、ローディングが終わったら alert("aa") が実行される。

ただし、

<script>
(function(v){alert(v)}("aa"));
hoge=function(v){alert(v)};
hoge("bb");
</script>

ってやった場合、ローディングが終わったら alert("aa"); alert("bb") が実行される。

function全体を()でくくった場合は、それ自体を無名関数化して1回実行させる。

じゃあ、(function(){})は何を返すか?

<script>
hoge=(function(v){alert(v)})("aa");
alert(hoge);
</script>

ってやると、undefinedになる。

すると、この(function(){})は使い捨てか・・・っていうと、そうではなく。

で、なんでこれを調べてたかってYoung risk taker.: [Javascript] クロージャを利用したイベントリスナの登録を読んでて、

  <ul id="selection">
    <li>&nbsp;</li> 
    <li>&nbsp;</li> 
    <li>&nbsp;</li> 
    <li>&nbsp;</li> 
  </ul> 
<script>
document.observe('dom:loaded', function() {
    $('selection').observe('click', (function(){
        var SELECTED_CLASS_NAME = 'selected';
        var selected;
        return function(event) {
            if(selected) selected.removeClassName(SELECTED_CLASS_NAME);
            event.target.addClassName(SELECTED_CLASS_NAME);
            selected = event.target;
        };
    })());
});
</script>

が理解できなかったから。

上記の、Scriptには3つfunctionが出てくる。

んで、onclickに結び付けられているのは、1つだけ。二つ目のfunction。
これが()でくくられているので、DOMがローディングされたときに一度走る。

クロージャって分かってるようで分かってなかったんだけど、こうやって()でくくられると完全に独立しちゃってその内部を覗くことって難しそうだ。隠蔽しているんだろうけど。

じゃあ、これを()でくくらなくてただのfunctionにしたらどうなるか。

この辺て、そもそもdocument.observeの第二引数が何ナノかっていうのを知らないとダメな感じ。だけどあえてそのまま実験。つまりこんな感じ。

document.observe('dom:loaded', function() {
    $('selection').observe('click', function(){
        var SELECTED_CLASS_NAME = 'selected';
        var selected;
        return function(event) {
            if(selected) selected.removeClassName(SELECTED_CLASS_NAME);
            event.target.addClassName(SELECTED_CLASS_NAME);
            selected = event.target;
        };
    });
});

これやると、<ul id="selection">をクリックするたびに2つ目のfunctionが実行される。さっきの場合は実行はローディングに1度だけ。
さらに、期待通りに動かない。


なんでか?

return function(){.........} になっている理由は?

そもそも、3つ目のfunctionがなんでreturn functionになってるのかが良く分かっていない。

これまで、return functionの記述ってEventオブザーバで出てくることしか見たことが無い・・・。じゃあ、Eventオブザーバって何なんだって話になる。


一番簡単な例は

<div class="title" id="Etest">
Event Observe test.
</div>
<script>
$('Etest').observe('click', function(event){alert(event.target.id)});
</script>

じゃないかな。$('Etest')はクリックされるたびに、function(){.....}が実行されてalertが返る。

じゃあ、このfunciton(){.....}を()でくくってみる

<div class="title" id="Etest">
Event Observe test.
</div>
<script>
$('Etest').observe('click', (function(event){alert(event)})("aaaaaa"));
</script>

これだと、ローディングがすんだ瞬間にalert("aaaaaa")が実行されて終わり。

その後$('Etest')をクリックしても、エラー。FireBugには

handler has no properties
wrapper(click clientX=0, clientY=0)prototype.1.6.0.j... (line 3842)
[Break on this error] handler.call(element, event)

ってでてる。

そこで、return functionする

<div class="title" id="Etest">
Event Observe test.
</div>
<script>
$('Etest').observe('click', (function(argv){
    var step = argv;
    var num = 0;
    return function(event){
        num = num + step;
        alert(event.target.id+num);
    }
})(100));
</script>

これだと、$('Etest')をクリックするたびにalertが返る。しかも、特筆すべきはローカルの変数が使える。使えるというのは「残る」って言う意味で。なんか「包まれてるっぽい」感じがする!!


ということで、まとめてみる。これは「なんで?」ってレベルじゃなくて「そうなってるから」で覚えておくことにする

onclickやonmouseoverにくくりつけられているのが、普通のfunctionの場合

→ イベントが起こるたびに、イベントハンドラはそのfunctionを実行する。

no_closuer
no_closuer posted by (C)ITOH Takashi

この場合、このfunction内のローカル変数を保存しておく方法が無い(たぶん?)。簡単な例で言うと、

<div class="title" id="Etest">
Event Observe test.
</div>
<script>
$('Etest').observe('click', function(event){var a=0;a++; alert("hoge"+a);});
</script>

当然だけど、ローカル変数aは、いつも0に初期化されるので意味が無い。

なので、かっこ悪いけどグローバル変数に振っておくしかない。これって結構気持ち悪い。

これを解決するのがクロージャ

onclickやonmouseoverにくくりつけられているのが、クロージャの場合

→ イベントが起こるたびに、イベントハンドラクロージャがreturnするfunctionを実行する。その時、クロージャ自体まで保存してくれて実行してくれる。

closuer
closuer posted by (C)ITOH Takashi

「なんで〜〜〜〜〜!?!?!?」って思っちゃうけどそういうもんだと、そういう仕様なんだと理解するしかないね。これは。

ただ、かなり便利。

そして、この最初に書いておいた「ロード時に一回実行」っていうのは、クロージャを作り出すのに必要なプロセスなんだと思った。で、クロージャのセットアップをする時に必要なのが(function(){...........})(ここで与えられる引数)なんだな。あー、そういうこと。


追記:
(function(){})() と function(){}() - IT戦記
文と式って何が違うんだろう?

てか、↑で知ったのだけど
function() {} ()

function() {}
は、大きく違う気がするけど、
(function() {})

function() {}
はほとんど変わる気がしない。
ケツの括弧の方が重要なのかなぁ?


追記2:
http://d.hatena.ne.jp/teramako/20080208/p1
で分かったんだけど。
そうか、ケツに()を付けると無名関数を作ってすぐ実行ってことになるのね。あー。そりゃそうじゃんね。言われてみればそうだけど気がつかなかったよ・・・・・。orz。

関数オブジェクトに()ですぐ実行。そりゃそうよね。
でも、returnでfunctionを返すのがクロージャのやり方ってかそういうもんだってのは間違いじゃないのか・・・な?