jQuery.Callbacks()

参考: jQuery.Callbacks() - jQuery API

jQuery.Callbacks( flags ) 返り値:Callbacks

概要

コールバックを管理するオブジェクトを作成する

用法

  • jQuery.Callbacks( flags )

    • 1.7で追加
    flags

    コールバックの振る舞いを指定するフラグのリスト。 スペースで区切ってリストにする。

※1.8 でオブジェクトの形で指定できるようになっていますが、 推奨される方法なのかは不明です。

// 1.8 では可能
var callbacks = jQuery.Callbacks({
  once: true,
  memory: true
});

詳細

jQuery.Callbacks()は、コールバック関数を 登録して管理するオブジェクトを作成して返します。 jQuery の内部でjQuery.ajax()や Deferred 、 アニメーションなどで使用されるキュー を実装するのに使用されています。

使い方は、jQuery.Callbacks()を実行して Callbacks オブジェクトを作成し、.add()を 使って関数を登録していき、.fire()などで 登録した関数を実行させます。

// func1, func2 は関数とする
var callbacks = jQuery.Callbacks();
callbacks.add( func2 );
callbacks.add( [func1, func2] );
callbacks.fire( "someValue" );
// → func2( "someValue" )
//    func1( "someValue" )
//    func2( "someValue" )  と実行される

.remove()で登録した関数を Callbacks オブジェクト から削除できます。同じ関数を複数登録した場合は それら全てが 削除されます。

// 上のコードの続き
callbacks.remove( func2 );
callbacks.fire( "someValue", "anValue" );
// → func1( "someValue", "anValue" ) と実行される

Callbacks オブジェクトは以下のようなメソッドが使用できます。

add() コールバックをオブジェクトに追加します。複数追加可能です。 コールバックをまとめた配列を渡すことも可能です
remove() 指定したコールバックをオブジェクトから削除します。 複数指定可能です
has( fn ) 指定したコールバックがオブジェクトに登録してあれば trueを、なければfalseを 返します
empty() オブジェクトから登録してあるコールバックを削除し空にします
disable() オブジェクトを使用不可にします。登録も実行(fire)も できなくなります。戻すことはできません(例外あり(後述))
disabled() オブジェクトが使用可能ならfalseを、 不可ならtrueを返します
lock() 現在の状態でオブジェクトを固定します。登録も実行(fire)も できなくなります。戻すことはできません。 フラグ memory を指定した場合のみ.add()で 指定した関数が実行されます
fire() 指定した引数で登録されているコールバックを実行します
fireWith(context, args) 指定したコンテキスト(this)と引数で 登録されているコールバックを実行します
fired() 少なくとも一回は.fire() (.fireWith())を実行していたら trueを、一回もしていなかったら falseを返します

.disable()したオブジェクトに .empty()を実行すると、内部のコールバックの リストがundefinedから空の配列になります。 そのため、.disabled()falseを 返すようになり、.add()でコールバックを 登録できるようになってしまい、1.8 だと.fired()falseの場合は一回だけ .fire()(.fireWith())で コールバックが実行できてしまうようになります。 おそらくバグだと思われます。

フラグについて

$.Callbacks()にフラグを文字列で渡すことで Callbacks オブジェクトの振舞い方を変更することができます。 フラグは複数指定することが可能で、その場合はスペースで 区切って指定します。 (例:$.Callbacks("unique stopOnFalse");

指定できるフラグは以下のようになります。(デフォルトでは .fire()するたびに登録された関数が実行されます)

once
Callbacks オブジェクトは一回しか起動できなくなる。
memory
.add()で関数を登録した時、その直前の .fire()(.fireWith()) に渡した引数で その登録した関数が実行される
unique
同じ関数を複数登録できなくなる
stopOnFalse
登録した関数がfalseを返した時、 後続の登録関数が実行されなくなる

once を指定するとその Callbacks オブジェクトでの 登録関数の実行は一回だけに制限されます。 つまり.fire()(.fireWith())を 複数回実行しても、2回目以降は何も起こりません。

var fn1 = function(v){ console.log("fn1: "+ v); };
var fn2 = function(v){ console.log("fn2: "+ v); };
var fn3 = function(v){ console.log("fn3: "+ v); };

var callbacks = $.Callbacks( "once" );
callbacks.add( fn1 );
callbacks.add( fn2 );
callbacks.fire( "someValue" ); // → fn1: someValue
                               //    fn2: someValue
callbacks.add( fn3 );
callbacks.fire( "anValue" ); // 登録関数は実行されない

memory を指定すると、最後に実行した.fire().fireWith())の引数を記憶するようになります。 そして.add()で関数を登録した時、  記憶しておいた引数をその関数に渡して実行するようになります。 .lock()された後はその前に記憶した引数の値で 実行されます。

var fn1 = function(v){ console.log("fn1: "+ v); };
var fn2 = function(v){ console.log("fn2: "+ v); };
var fn3 = function(v){ console.log("fn3: "+ v); };

var cls = $.Callbacks("memory");
cls.add( fn1 );
cls.fire("someValue"); // → fn1: someValue
cls.add( fn2 );        // → fn2: someValue
cls.add( fn2 );        // → fn2: someValue
cls.fire("another");   // → fn1: another
                       //    fn2: another
                       //    fn2: another
cls.add( fn1, fn2, fn3 ); // → fn1: another
                          //    fn2: another
                          //    fn3: another
cls.lock();
cls.add( fn3 );        // → fn3: another
cls.fire("other");
cls.add( fn3 );        // → fn3: another

unique を指定すると同じ関数を重複して登録できなくなります。

var fn1 = function(v){ console.log("fn1: "+ v); };
var fn2 = function(v){ console.log("fn2: "+ v); };
var fn3 = function(v){ console.log("fn3: "+ v); };

var cls = $.Callbacks("unique");
cls.add( fn1 );
cls.fire("someValue"); // → fn1: someValue
cls.add( fn1 );        // 同じ関数は登録されない
cls.add( fn2 );
cls.fire("another");   // → fn1: another
                       //    fn2: another
cls.add( fn1, fn2, fn3 ); // fn3 だけ登録される
cls.fire("other"); // → fn1: other
                   //    fn2: other
                   //    fn3: other

stopOnFalse を指定すると、登録した関数がfalseを 返した場合、残りの登録関数が実行されなくなります。

var fn1 = function(v){ console.log("fn1: "+ v); return null; };
var fn2 = function(v){ console.log("fn2: "+ v); return false; };
var fn3 = function(v){ console.log("fn3: "+ v); };

var cls = $.Callbacks("stopOnFalse");
cls.add( [fn1, fn2, fn3] );
cls.fire("someValue"); // → fn1: someValue
                       //    fn2: someValue
                       // ※fn3 は実行されない

フラグは空白をはさんで連結することが出来ます。 その効果も重ね合わせることができます。 例えば、"once memory"を指定して作成した Callbacks オブジェクトは、一回だけ.fire() (.fireWith())することができ、 その後、関数を.add()した時はその引数の値で 登録した関数が実行されます。(これは jQuery の内部で Deferred の.done().fail()の 実装に使用されているそうです)

var fn1 = function(v){ console.log("fn1: "+ v); };
var fn2 = function(v){ console.log("fn2: "+ v); };
var fn3 = function(v){ console.log("fn3: "+ v); };

var cls = $.Callbacks("once memory");
cls.add( fn1 );
cls.fire("someValue");  // → fn1: someValue
cls.add( fn2 );         // → fn2: someValue
cls.fire("otherValue"); // ※何も実行されない
cls.add( fn3 );         // → fn3: someValue

メソッドの分離

Callbacks オブジェクトのメソッドは、その Calllbacks オブジェクト上で実行する必要は無く、メソッドだけを 他の変数に格納して使用することができます。

var fn1 = function(v){ console.log("fn1: "+ v); };
var fn2 = function(v){ console.log("fn2: "+ v); };
var fn3 = function(v){ console.log("fn3: "+ v); };

var cls = $.Callbacks("once memory");
var add = cls.add,
    fire= cls.fire;
add( fn1 );
fire("someValue");  // → fn1: someValue
add( fn2 );         // → fn2: someValue
fire("otherValue"); // ※何も実行されない
add( fn3 );         // → fn3: someValue

ただしfire()(fireWith())で 実行されるコールバックのコンテキスト(this)は変化します。

コールバックのコンテキスト(this)

コールバックのコンテキスト(this)は、.fireWith() で実行した場合は 引数に指定したオブジェクトになりますが、 .fire()で実行した場合は そのメソッド(fire)が所属する場所によって変わります。 オブジェクトのプロパティとして所属している場合は そのオブジェクトが this になり、変数として所属している場合は グローバルオブジェクトが this になります。

// コールバックのコンテキスト調査
var callbacks = $.Callbacks();
callbacks._id = "myCallbacks";
callbacks.add( function(){
  console.log( this );
});

var anObject = { "_id": "anObject" };

callbacks.fire("someValue");
// → Object { _id="myCallbacks", add=function(), remove=function(), ...}
callbacks.fireWith(anObject, ["someValue"]);
// → Object { _id="anObject" }

// メソッドを他の引数に入れて実行した場合
var fire = callbacks.fire,
    fireWith = callbacks.fireWith;
fire("anotherValue");
// → Window オブジェクト
fireWith(anObject, ["anotherValue"]);
// → Object { _id="anObject" }

// メソッドを他のオブジェクトのプロパティに入れた場合
var someObject = { "_id": "someObject" };
someObject.fire = callbacks.fire;
someObject.fireWith = callbacks.fireWith;
someObject.fire("fromSomeObject");
// → Object { _id="someObject", fire=function(), fireWith=function()}
someObject.fireWith(anObject, ["fromSomeObject"]);
// → Object { _id="anObject" }

Callbacks オブジェクトにフラグ memory を指定した場合、 .add()した際に実行されるコールバックの コンテキストは、直近のfire() (fireWith())でのコンテキストになります。

// memory の時のコールバックのコンテキスト調査
var fn1 = function(){
  console.log( "fn1 実行開始" );
  console.log( this );
};
var fn2 = function(){
  console.log( "fn2 実行開始" );
  console.log( this );
};

var someObject = { "_id": "someObject" };
var anObject = { "_id": "anObject" };

var cls = $.Callbacks("memory"),
    fire = cls.fire,
    fireWith = cls.fireWith;
cls._id = "myCallbacks";

cls.add( fn1 );
cls.fire("foo"); // fn1 実行開始 this:cls arg:"foo"
cls.add( fn2 );  // fn2 実行開始 this:cls arg:"foo"
cls.fire("bar"); // fn1 実行開始 this:cls arg:"bar"
                 // fn2 実行開始 this:cls arg:"bar"
cls.empty();

cls.add( fn1 );  // fn1 実行開始 this:cls arg:"bar"
fire("foo");     // fn1 実行開始 this:Window arg:"foo"
cls.add( fn2 );  // fn2 実行開始 this:Window arg:"foo"
fireWith(someObject, ["bar"]); 
  // fn1 実行開始 this:someObject arg:"bar"
  // fn2 実行開始 this:someObject arg:"bar"
cls.add( fn2 );  // fn2 実行開始 this:someObject arg:"bar"

anObject.fire = fire;
anObject.fire("foobar"); // fn1 実行開始 this:anObject arg:"foobar"
                         // fn2 実行開始 this:anObject arg:"foobar"
                         // fn2 実行開始 this:anObject arg:"foobar"

cls.remove( fn2 );
cls.add( fn2 );  // fn2 実行開始 this:anObject arg:"foobar"

anObject.fireWith = fireWith;
anObject.fireWith(someObject, ["bar"]);
  // fn1 実行開始 this:someObject arg:"bar"
  // fn2 実行開始 this:someObject arg:"bar"

cls.remove( fn2 );
cls.add( fn2 ); // fn2 実行開始 this:someObject arg:"bar"

スポンサードリンク