クロージャの何が便利なのか
ただ、実際にスクリプトを書く際、クロージャをどう使えば便利なのかはイマイチ分かっていません。これは今後の課題。
追記:はてな記法を間違えたせいで、見出しひとつひとつがエントリになってしまった。めんどくさいのでこのままにしておきます。
あとクロージャの便利な使用例見つけた!
var fade= function(node){ var level=1; var step = function(){ var hex = level.toString(16); node.style.backgroundColor ='#FFFF' + hex + hex; if(level < 15){ level += 1; setTimeout(step,100); } }; setTimeout(step,100); }; fade(document.body); // 背景の色が白から黄色に変わる
HTMLのノードのbackgroundColorを100msごとに変化させて、黄色から白にする関数です。
あえてクロージャを使わない版も考えてみた:
Element.prototype.step = function(){ var hex = level.toString(16); this.style.backgroundColor = '#FFFF' + hex + hex; if (level < 15) { level += 1; } } var level=1; setInterval("document.body.step()",100);
簡単な例
たとえば下のコード。
var myObject = function(){ var value=0; return{ increment: function(){ value +=1; }, getValue: function(){ return value; } }; }();
このコードでは変数myObjectに対して、incrementとgetValueというメソッドを持つオブジェクトが代入されています。
increamentとgetValueは、外側にある無名関数の変数valueにアクセスしていますよね。これがクロージャ。たぶん。
「でもこの無名関数が実行された後は変数valueは消えちゃうんじゃないの?」とか普通は思いますよね。
ところが驚くべきことに、いったんmyObjectが生成されたあとも、incrementメソッドとgetValueメソッドはこの変数valueにアクセスできてしまいます。
myObject.getValue(); >>0 myObject.increment(); myObject.increment(); myObject.increment(); myObject.getValue(); >>3
クロージャすげえ!
クロージャとは
「クロージャ」とは、簡単に言うと「自分自身が定義されたスコープを覚えていて、そのスコープ内の変数にアクセスできる関数(ないしメソッド)」ということらしいです。
JavaScriptのスコープには関数スコープしか存在しないから、これは必然的に関数がネストされた場合に限られますね。
そろそろクロージャをしっかり理解しておこう
4ヶ月近くブログを放置してしまった…。
JavaScript: The Good Parts ―「良いパーツ」によるベストプラクティス
- 作者: Douglas Crockford,水野貴明
- 出版社/メーカー: オライリージャパン
- 発売日: 2008/12/22
- メディア: 大型本
- 購入: 94人 クリック: 1,643回
- この商品を含むブログ (190件) を見る
Kindle Storeでこの本(の原著)を買って、通勤中にiPadで読みながらJSを再勉強してます。
prototype.jsみたいなフレームワークは超強力だけど、まずはJSの基礎をしっかり押さえとかないとね。基礎だ基礎!
そんな訳で、今回は今まで分かったようでいて放置していた「クロージャ」についてまとめておきます。
■
関数のバインディング
この章で少し分かり辛かった、関数のバインディングについてメモ。
バインディングとは、関数のなかでthisが参照するオブジェクトを実行時に結合すること。
function getName(){ return this.name } getName(); // -> '' window.name="Demo"; getName(); // -> 'Demo'
デフォルトでは、いずれのオブジェクトにも属さない形で定義された関数は、windowオブジェクトにバインドされる。
ただし、関数を呼び出すと気に特定のオブジェクトを使用しないと、そのバインディングを失ってしまうらしい。
var CoolObj = { name: 'Joe the cool object', getName: function(){ return this.name; } }; window.name = ''; //getNameはCoolObjにバインドされている CoolObj.getName(); // -> 'Joe the cool object' function callFx(fx){ return fx(); } //関数の引数としてメソッドを渡すと、バインドがデフォルトのwindowに戻ってしまう callFx(CoolObj.getName); // ->'' window.name = 'The window'; callFx(CoolObj.getName); // ->'the window'
この問題を避けるために、Prototypeではbind()という関数が用意されている。
var CoolObj = { name: 'Joe the cool object', getName: function() { return this.name; } }; CoolObj.getName() // -> 'Joe the cool object' function callFx(fx) { return fx(); } callFx(CoolObj.getName.bind(CoolObj)) // -> 'Joe the cool object'
引数の事前設定(カリー化)
メソッドをいろんなところに渡すとき、事前に引数を設定しておくと便利!なときに使う関数。
//split()に引数':'を渡してやりたいとき //ふつうなら… String.prototype.splitOnColons = function(){ return this.split(':'); '1:2:3:4'.splitOnColons(); // ->["1", "2", "3", "4"] //curry()を使うと… String.prototype.splitOnColons = String.prototype.split.curry(':');
Facebookアプリの開発環境を無料で手に入れる方法
ここ1ヶ月ほど、Twitterで知り合った方々とオンラインでFacebookアプリの勉強会をやっています。
テキストはこちら:
Facebook API Developers Guide (Firstpress)
- 作者: Wayne Graham
- 出版社/メーカー: Apress
- 発売日: 2008/02/28
- メディア: ペーパーバック
- 購入: 1人 クリック: 25回
- この商品を含むブログ (2件) を見る
Facebookアプリは、開発したWebアプリを外部のサーバーで走らせ、そこで生成されたHTMLをFacebookのサーバーが呼び出してFacebookのページ内に表示する、という仕組みになっています。
したがって開発者はサーバーを自前で用意せねばなりません。
そこでFileQという激安国内のレンタルサーバーを使ってサンプルのアプリを走らせたところ、(慣れっこだけど)悲しいことにエラーが出てしまいました。
どうもcURLというPHPのモジュールが無効になっているため、Facebook APIのクライアントライブラリが動いていないようなのです。
サーバー管理者に問い合わせた所、cURLはすぐにはインストールできないとの由。
そこで途方に暮れてしまったのですが、海外の企業でFacebookアプリ用のサーバーを無料で提供している所を見つけました。
ユーザー情報を登録すると、すぐにアカウントを発行してくれます。
セキュリティ上の理由でFTPではファイルがアップできないそうなので、WinSCPを使ってSFTPで公式ライブラリとサンプルをアップロード。恐る恐るURLにアクセスしてみると…
動いた!動いたよ!
無事に "Hello World!" と、友達のユーザーIDを列挙するサンプルが動きました。
これからさらにAPIをいじくれば、画像を表示させたりリンクも貼れるようになるみたいです。
Prototype & script.aculo.us 第2章 $()関数のクイックヘルプ
Prototype.jsで使える超便利なユーティリティ関数たち:
$()関数
DOM要素をid属性で取り出す。document.getElementByIdとほぼ同等。
さらにProtorype.jsではDOMが拡張されているので、ふつうなら
document.getElementById('navBar').style.display='none';
と書いていたところを、
$('navBar').hide();
と書ける。かんたん!
$$()関数
CSSクラス名やセレクタに基づいてDOM要素を取得できる強力な関数。
$$('div') // -> all DIVs in the document. Similar to // document.getElementsByTagName('div'), although returning a non-live // Array of extended (as in $'d) elements, instead of a live NodeList // of (possibly unextended) elements.
$$('div') $$('#contents') // -> same as $('contents'), only it returns an array anyway. $$('li.faux') // -> all LI elements with class 'faux' $$('#contents a[rel]') // -> all links inside the element of ID "contents" with a rel attribute $$('a[href="#"]') // -> all links with a href attribute of value "#" (eyeew!)
$A()関数
コレクションの性質を持つものは、なんでも配列に変換する。
// 複数の要素に変更を加える通常の方法 var paras = document.getElementsByTagName('p'); for (var index = 0; index < paras.length; ++index) Element.hide(paras.item(index)); Element.update(paras.item(paras.length - 1), 'Jeez that is verbose'); // Prototypeを使ったやり方。ただし要素は拡張されていないものが返される。 var paras = $A(document.getElementsByTagName('p')); paras.each(Element.hide); Element.update(paras.last(), 'This looks better'); //拡張された要素を取り出すなら、次のようにも書ける。 var paras = $$('p'); paras.invoke('hide');
$F()関数
フォームのフィールド、またはそのIDを受け取って、そのフィールドの値を返す。
$H()関数
ハッシュを生成する関数。
$R()関数
範囲を表すオブジェクトを生成する関数。
$A($R(1,7))
// -> [1,2,3,4,5,6,7]