即使忘记了 `new`,我什么时候应该自动创建一个对象?

IT技术 javascript object
2021-02-27 06:03:19

假设我有以下对象构造函数:

function Foo(bar) {
    this.bar = bar;
}

如果我在没有new关键字的全局范围内运行该函数,那么bar将在Foo()调用的任何范围内设置

var foo = Foo(42);
console.log(bar); // 42
console.log(foo.bar); // ERROR

所以我的想法是做这样的事情:

function Foo(bar) {
    if(!(this instanceof Foo)) {
        // return a Foo object
        return new Foo(bar);
    }
    this.bar = bar;
}

这样,如果我这样做new Foo(42) or Foo(42),它总是会返回一个Foo对象。

这是有史以来一个好主意?如果有,是什么时候?什么时候(以及为什么)避免这种技术是明智的?

4个回答

当您想在内部构造对象包装器时,这可能很有用

一个鲜为人知的库在内部使用这种方法,jQuery。

不过,他们不再使用这种instanceof方法。每次调用 jQuery 时,它都会自动执行以下操作:

// Define a local copy of jQuery
jQuery = function( selector, context ) {
 // Need init if jQuery is called (just allow error to be thrown if not included)
 return new jQuery.fn.init( selector, context );
}

它在内部所做的一切都引用了这个本地副本。在它工作的最后,它然后将它附加到全局范围

window.jQuery = window.$ = jQuery;

所以每次你$()在内部调用它时都使用new. 它还假设您使用new外部,但它真的不在乎您是否使用。


编辑

jsFiddle Demo

//Foo entrance
function Foo(bar){
 //construct a new Foo object to return
 return new Foo.prototype.build(bar);
}

//constructor for returning new prototype
Foo.prototype.build = function(bar){
 //initialize base settings
 this.bar = bar;
 //chain object
 return this;
};

//this is the complex part
//tie the prototype of Foo.prototype.build.prototype to Foo.prototype
//so that is "seems" like Foo is the parent of the prototype assignments
//while allowing for the use of returning a new object in the Foo entrance
Foo.prototype.build.prototype = Foo.prototype;

//simple expansions at this point, nothing looks too different
//makes a function for a constructed Foo that logs hello
Foo.prototype.hello = function(){
 console.log("Hello "+this.bar+" :)");
 //returns this for chaining
 return this;
};

//more extensions, this one changes this.bar's value
Foo.prototype.setBar = function(arg){
 //accesses the current Foo instance's .bar property
 this.bar = arg;
 //returns this for chianing
 return this;
};

//should be seeing a pattern
Foo.prototype.goodbye = function(){
 console.log("Bye "+this.bar+" :(");
 return this;
};

var foo = Foo(42);
//console.log(bar); // ERROR
console.log(foo.bar); // 42
foo.hello(); //Hello 42 :)
foo.hello().setBar(9001).goodbye(); //Hello 42 :) Bye 9001 :(
Foo(12).hello(); //Hello 12 :)
那么有什么作用new jQuery(...)呢?
2021-04-23 06:03:19
所以new (new Foo(...))没有做任何不同的事情new Foo(...)吗?
2021-04-24 06:03:19
@qwertynl - 我认为返回值必须是复杂类型。如果它是值类型,那么您仍然会得到构造的 Function 对象。所以return barorreturn "hi"仍然会返回一个新的 Foo,但是return [], or return {}, or return new Something(), 或任何其他复杂类型将被返回。例如:function Foo(bar){ this.bar = bar; return []; } var foo = new Foo(42); console.log(foo); console.log(foo.bar);foo 将是 [] 并且 foo.bar 将是未定义的。而如果Foo返回bar,则 foo 将是一个构造函数Foo并且foo.bar是 42。
2021-05-05 06:03:19
@qwertynl - 它与jQuery(...). 没有区别。
2021-05-07 06:03:19
@qwertynl - 嗯,这真的不一样。在函数上使用时,new会导致返回一个 Function 对象。这个对象不是一个函数,所以你不能真正使用new它。如果你尝试,你会得到一个错误:“TypeError: object is not a function”。jQuery 似乎逃脱了它的原因是它在内部使用这个新版本,所以无论它看起来如何或在外部使用如何,在内部它总是可以访问它的原型。也可以返回 Function 对象。这可以通过返回任何其他东西来完成。
2021-05-18 06:03:19

虽然我不反对这种风格,但我个人不会仅仅为了保持一致而使用它。当然,我可以像这样制作我的所有构造函数,但看起来要编写更多的代码。

如果我担心在没有 的情况下意外调用构造函数new,我宁愿使用 JSHint 来警告我。请参阅http://www.jshint.com/docs/options/#newcap

我有一个很好的理由来避免它:如果您没有忘记使用new,那么这只是不必要的开销。除非您正在创建给定对象的数千个实例,否则您可能永远不会注意到这一点,但它仍然存在。

我建议限制代码中完成new基于对象创建的位置的数量- 如果您不是在每个需要新对象的地方都依赖它,那么您不必担心在其中一个地方忘记它。有许多模式可以在这里帮助您,但最简单的一种是只在某处创建一个负责创建对象的函数,并且每次都遵循这一点 - 是创建 1 个实例还是 1,000,000 个实例取决于您。

也就是说,如果这没有意义,那么这种后卫是一个很好的后备。请注意,它与 JavaScript 的内置类型构造函数在不使用时如何兼作类型转换器的方式有很多相似之处new- 所以我认为用户定义的类型是使用这种技术的特别合适的地方。

另请参阅:JavaScript 的“new”关键字是否有害?

“可读性”的倡导者可能会将其描述为一种反模式,但您比其他任何人都更了解自己的代码——如果它对您有用,那么就去做吧。就我个人而言,我更愿意用一种方法来做件事——它使它清晰简洁。如果另一个开发人员加入竞争并尝试使用我的对象而不实例化它,我宁愿它抛出一个错误来告诉他/她他们是愚蠢的。