これでクロージャも怖くない


「4.10クロージャ」からのメモ。


javascriptにはブロックスコープがありません。しかし関数スコープが通常の言語の関数スコープとは異なっていて、クロージャという機能をもっています。
クロージャ - Wikipedia

クロージャ(クロージャー、closure、閉包)はプログラミング言語における関数の一種。引数以外の変数を実行時の環境ではなく、自身が定義された環境(静的スコープ)において解決することを特徴とする。

( ゚д゚)ポカーン
あー、これだけだと意味がわかりませんよね(少なくとも僕はわからなかったです)


僕はこの本を読んで以下のように理解しています。

  • ある関数Aの中で定義された関数Bがあるとします
  • このとき関数Bからは関数A内で定義された変数Xにアクセスすることができます
  • 関数Aの実行が終わってしまった後でも何らかの方法で関数Bを実行できた場合、関数Bだけが変数Xを操作することができます
  • 関数Bだけが変数Xを操作できるのでクロージャ(閉包)と呼ばれる


コードで書くとこんな感じです。

function makeCountupClosure(){
  var count = 0;
  var countup = function(){ //countup()だけがcountを操作できる
    count++;
    return count;
  };

  return countup; 
}

var myCountup = makeCountupClosure();

alert(myCountup()); //1
alert(myCountup()); //2
alert(myCountup()); //3

クリックされた回数をカウントする

あるDOM要素が何回クリックされたかカウントするために、「クリックされた回数をカウントする」関数を作ってみます。

クロージャを使わないで実装

まずクロージャを知らなかった頃の僕ならこう作っていると思います。

var dom = document.getElementById("hoge");
dom.count = 0;
dom.onclick = function(){
  this.count++;
}

//5秒後にクリックされた回数を表示
setTimeout(function(){ alert(dom.count); } , 5000);

これでもちゃんと動きます。
ただし、ダメダメなところがあります。*1それはdom.countが完全にパブリックになっており、誰でも操作できるという点です。

//勝手に値を変更
dom.count = 1000;
クロージャを使って実装

この問題を解決するために、クロージャを使って実装してみます。

function setCountup(elm){
  var count = 0; //elm.onclickとelm.getCountからしかアクセスできない
  
  elm.onclick = function(){
    count++;
  } 
  
  elm.getCount = function(){
    return count;
  } 
} 

var dom = document.getElementById("hoge");
setCountup(dom);

//5秒後にクリックされた回数を表示
setTimeout(function(){ alert(dom.getCount()); } , 5000);

ちょっと行数は多くなってしまったけど、変数countはプライベートになりました。これで安心。
しかもちょっとうれしいことに、setCountup()を使えば好きな要素にカウントアップの機能を追加することができます。




というわけで、クロージャすごい!
まだまだ勉強しはじめなので、何かあればツッコミお願いします><

*1:本当はonclickじゃなくてaddEventListener()を使ったほうがよい