Hatena::Groupjavascript

JavaScriptで遊ぶよ

 | 

2009-12-15

Deferred.prototype._ ってのを作ってみた

10:36

JSDeferred の話。

例えば、何でもいいんだけど、Deferred チェーンを繋げるためのクラスがあるとする。

function Listener( element ) {
  this.element = element || window;
}
Listener.prototype.click = function() {
  var element = this.element;
  var d = new Deferred;
  function passToHandler(e) {
    element.removeEventListener('click', passToHandler, false);
    d.call(e);
  };
  element.addEventListener('click', passToHandler, false);
  return d;
}

この例だと、new Listener して click() メソッドからチェーンを作る。


で、↓こうやって使うと body 要素を1回目にクリックすると「こんにちは!」と、2回目にクリックすると「こんにちは こんにちは!」と、3回目にクリックすると「こんにちは こんにちは こんにちは!」とアラートが出るようにもできる。

window.onload = function() {

  var listener = new Listener(document.body);

  listener.click()
  .next(function(arg) {   
    alert('こんにちは!');
  })
  .next(function() {
    return listener.click();
  })
  .next(function(arg) {
    alert('こんにちは こんにちは!');
  })
  .next(function() {
    return listener.click();
  })
  .next(function(arg) {
    alert('こんにちは こんにちは こんにちは!');
  });  

};

でもイチイチ間に出てくる

  .next(function() {
    return listener.click();
  })

が邪魔くさい。


そこでこれ!

Deferred.prototype._ = function(obj) {
  var d = this._next = new Deferred;
  var delegate = {};
  for (var x in obj.constructor.prototype) if (typeof obj[x] == 'function') {
    delegate[x] = function() {
      var args = Array.prototype.slice.call(arguments);
      return d.next(function() {return obj[x].apply(obj, args);});
    }
  }
  return delegate;
}

何が便利って、↓こんなふうに書ける!

window.onload = function() {

  var listener = new Listener(document.body);

  listener.click()
  .next(function(arg) {   
    alert('こんにちは!');
  })
  ._(listener).click()
  .next(function(arg) {
    alert('こんにちは こんにちは!');
  })
  ._(listener).click()
  .next(function(arg) {
    alert('こんにちは こんにちは こんにちは!');
  });  

};

ただのシンタックスシュガーだけど、最初の listener.click() との対応もとれて、見た目スッキリ。


テストページ↓

addEventListener なので IE では動きません。


Deferred.prototype._ じゃアレなので Deferred.prototype._ = Deferred.prototype.delegate というエイリアスにしたほうがいいのかも。


失敗するケースがあったので改良

Deferred.prototype._ = function(obj) {
    var self = this;
    var delegate = {};
    for (var x in obj) if (typeof obj[x] === 'function') (function(x) {
        delegate[x] = function() {
            var args = Array.prototype.slice.call(arguments);
            return self.next(function() {
                return obj[x].apply(obj, args);
            });
        }
    })(x);
    return delegate;
};

変数 x が forin してる間に変わってくのでクロージャにしないとだめだった。。

トラックバック - http://javascript.g.hatena.ne.jp/edvakf/20091215
 |