クロージャの何が便利なのか

ただ、実際にスクリプトを書く際、クロージャをどう使えば便利なのかはイマイチ分かっていません。これは今後の課題。


追記:はてな記法を間違えたせいで、見出しひとつひとつがエントリになってしまった。めんどくさいのでこのままにしておきます。


あとクロージャの便利な使用例見つけた!

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 ―「良いパーツ」によるベストプラクティス

JavaScript: The Good Parts ―「良いパーツ」によるベストプラクティス

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)

Facebook API Developers Guide (Firstpress)


Facebookアプリは、開発したWebアプリを外部のサーバーで走らせ、そこで生成されたHTMLをFacebookのサーバーが呼び出してFacebookのページ内に表示する、という仕組みになっています。


したがって開発者はサーバーを自前で用意せねばなりません。
そこでFileQという激安国内のレンタルサーバーを使ってサンプルのアプリを走らせたところ、(慣れっこだけど)悲しいことにエラーが出てしまいました。


どうもcURLというPHPのモジュールが無効になっているため、Facebook APIのクライアントライブラリが動いていないようなのです。


サーバー管理者に問い合わせた所、cURLはすぐにはインストールできないとの由。


そこで途方に暮れてしまったのですが、海外の企業でFacebookアプリ用のサーバーを無料で提供している所を見つけました。


Triton | Joyent


ユーザー情報を登録すると、すぐにアカウントを発行してくれます。
セキュリティ上の理由で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.

タグ名の他にもたくさのCSSセレクタをサポートしている。

$$('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]