无需"新"

gsa*_*kis 2 javascript constructor factory new-operator

javascript中令人讨厌的问题是忘记调用new一个意图实例化的函数,导致this被绑定到一个不同的对象(通常是全局的)而不是一个新对象.我读到的一个解决方法是使用以下习语在函数构造函数中显式检查它:

function SomeConstructor(x, y, ...) {
    // check if `this` is created with new
    if ( !(this instanceof arguments.callee) )
        return new SomeConstructor(x, y, ...);
    // normal initialization code follows
Run Code Online (Sandbox Code Playgroud)

现在new SomeConstructor(...)SomeConstructor(...)等同.

我想通过创建一个包装函数来简化这一过程,该函数factory(fn)执行前两行,然后委托给包装函数fn.这将用作:

SomeConstructor = factory(function (x, y, ...) {
    // normal initialization code follows
})
Run Code Online (Sandbox Code Playgroud)

我的第一次尝试是:

function factory(fn) {
  return function() {
    if ( !(this instanceof arguments.callee) ) {
      return new arguments.callee.apply(this, arguments);
    }
    fn.apply(this, arguments);
  }
}
Run Code Online (Sandbox Code Playgroud)

但它失败了"在不兼容的[对象对象]上调用了Function.prototype.apply".第二次尝试是:

function factory(fn) {
  return function() {
    if ( !(this instanceof arguments.callee) ) {
      var tmp = new arguments.callee();
      arguments.callee.apply(tmp, arguments);
      return tmp;
    }
    fn.apply(this, arguments);
  }
}
Run Code Online (Sandbox Code Playgroud)

这类工作但它可以调用包装函数两次:一次没有参数(创建一个新实例),一次使用传递的参数进行实际初始化.显然这是脆弱和低效的,但我无法通过一次通话找到一种方法.这可能吗 ?

编辑:基于bobince的方法,这里有一个类似的方法:

function factory(init) {
    var run_init = true;
    function constr() {
        if ( !(this instanceof constr) ) {
            run_init = false;
            var tmp = new constr();
            run_init = true;
            init.apply(tmp, arguments);
            return tmp;
        }
    if (run_init)
        init.apply(this, arguments);
  }
  return constr;
}
Run Code Online (Sandbox Code Playgroud)

至于这是否应该被鼓励,这是值得商榷的.我来自Python背景,我认为new只是噪音(Java)或疣(Javascript),但我可能会遗漏一些东西.

ric*_*ent 9

这简单地鼓励了一种坏习惯的快捷方式,它在很大程度上依赖于类的实现来"修复"调用代码.

如果这是一个问题,请不要让它滑动,抛出错误信息.