オブジェクトのメソッドをメソッドチェーンで遅延実行させる

ppBlog official
setTimeoutのメソッドチェーンとループ時の利便性向上について - 三等兵

元ネタとなった、先の2つのリンク先でもいろいろ書かれているのですが、興味が湧いたのでやってみた。

コードを拝借。

var foo = function(){}; // 関数オブジェクトを作成

foo.prototype = { // プロトタイプを設定
 say : function(s){
  alert(s);
  return this;
 },
 await : function(ms){ // ここで遅延処理をゴニョゴニョしたい
  // var _this = this;
  // setTimeout(function(){
  // return _this; # まぁ、これはダメだけど、こんな感じでやれたらいい
  // }, ms);
   return this;
  }
}

var bar = new foo();
bar.await(2000).say("Hello"); // すぐに「Hello」が出力される
ppBlog official

やってみた

fooのsayはsayであり、say以外の処理が書かれているのはセイセイセイだと思ったので、遅延部分を別オブジェクトにしてみたフォー!

function Delayer(obj) {
  if (!(this instanceof arguments.callee)) { return new arguments.callee(obj); }
  this._time  = 0;
  
  // すべてのメソッドを遅延対応にして己のものとする
  for (var key in obj) {
    var f = obj[key];
    if (typeof f != 'function') { continue; }
    
    this[key] = (function (f) {
      return function () {
        var arg = arguments;
        setTimeout(function () {
          f.apply(obj, arg);
        }, this._time);
        return this;
      }
    })(f);
  }
}

Delayer.prototype.await = function (t) {
  this._time += t;
  return this;
};

var foo = function(){}; // 関数オブジェクトを作成

foo.prototype = { // プロトタイプを設定
 say : function(s){
  alert(s);
  return this;
 }
};

var bar = new foo();
Delayer(bar).await(1500).say('Hello, ').await(2000).say('world!');

簡単に説明すると、Delayerのコンストラクタに渡されたオブジェクト内のメソッドを遅延実行されるようにラップして、Delayerオブジェクト自身のメソッドにしちゃってますYo。

さらにやってみた

先の方法だと、同じオブジェクトでなんども遅延処理を行う場合に毎回すべてのメソッドをラップする必要があって速度的に気になるので、ラップした関数をprototypeに入れたオブジェクトを作成するようにしてみたYo。

function DelayerNeo(obj) {
  var retFunc = function () {
    this._time  = 0;
  };
  
  // すべてのメソッドを遅延対応にしてretFuncへと託す
  for (var key in obj) {
    var f = obj[key];
    if (typeof f != 'function') { continue; }
    (function (f) {
      retFunc.prototype[key] = function () {
        var arg = arguments;
        setTimeout(function () {
          f.apply(obj, arg);
        }, this._time);
        return this;
      };
      retFunc[key] = function (t) {
        return (new this())[key](t);
      };
    })(f);
  }
  
  retFunc.prototype.await = function (t) {
    this._time += t;
    return this;
  };
  
  retFunc.await = function (t) {
    return (new this()).await(t);
  };
  
  return retFunc;
}


// fooは先と同じ
var bar = new foo();
var delayedBar = DelayerNeo(bar);

delayedBar.await(1500).say('やぁ、').await(2000).say('世界!');
delayedBar.await(6500).say('よう、').await(2000).say('言葉!');

細かい説明は面倒なので割愛。