読者です 読者をやめる 読者になる 読者になる

素人がプログラミングを勉強していたブログ

プログラミング、セキュリティ、英語、Webなどのブログ since 2008

連絡先: すかいぷ:javascripter_  か javascripter あっと tsukkun.net skypeのほうがいいです

newを省略できる、可変引数を取るコンストラクタの作り方

javascript

追記:コメントを元に、__proto__を使ったthisのエミュレートをやってみた。結構シンプルでいい。
参考:JavaScript のコンストラクタ関数 - 冬通りに消え行く制服ガールは✖夢物語にリアルを求めない。 - subtech

function defer(){
  if(!(this instanceof defer))
    return defer.apply({__proto__:defer.prototype},arguments);
  this.task=Array.slice(arguments);
  this.next();
  return this;
}
defer.prototype.next=function(){
  var self=this;
  this.arg=this.task.shift().call(this,
    function() self.next.call(self),
    this.arg);
}

追記ここまで。

JSDeferredのように、newを付けても付けていなくてもうまく動くコンストラクタは、

function constructorFn(a,b,c){
  if(!(this instanceof arguments.callee)) return new arguments.callee(a,b,c);
}

のように、割と簡単に書くことができるが、コンストラクタの取る引数が可変の場合、

return new arguments.callee.apply(ここに入れるものがない,arguments); // newされる前にarguments.callee.applyが呼ばれてしまう

のように問題が多く、実現するのが難しい。

こういった場合はクロージャを使って、下のように書くことで解決できる。
ただし、コードが分かりづらくなるので、普通に毎回newを付けるようにしたほうがいいかもしれない。
下のコードはを参考にした。

var defer;
(function(){
  var arg,flag;
  defer=function(){
    if(this instanceof defer){
      var a;
      if(flag)a=arg;
      else a=arguments;
      flag=false;
      this.task=Array.slice(a);
      this.next();
    }else{
      flag=true;
      arg=arguments;
      return new defer();
    }
  }
  defer.prototype.next=function(){
    var self=this;
    this.arg=this.task.shift().call(this,
      function() self.next.call(self),
      this.arg);
  }
})();

defer(function(next){
  console.log("start!");
  setTimeout(next,1000);
},
function(){
  console.log("finish!");
}); // new defer(function(next){…でも動く