即使忘记了“新”,我应该何时自动创建一个对象?

时间:2013-12-31 16:06:20

标签: javascript object

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

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) Foo(42),那么 总是 会返回{{1}对象。

永远是个好主意吗?如果是的话,何时?何时(和为什么)避免这种技术是明智的?

4 个答案:

答案 0 :(得分:8)

虽然我没有任何反对这种风格的东西,但我不会亲自使用它只是为了保持一致。当然,我可以让我所有的构造函数都像这样,但看起来要编写的代码要多得多。

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

答案 1 :(得分:8)

当您希望内部构造对象包装器时,这可能很有用。

一个鲜为人知的库在内部使用这种方法,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 :)

答案 2 :(得分:3)

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

我建议限制代码中基于new的对象创建完成的位置数 - 如果你不是每个地方都需要一个新对象,那么你就不用担心了关于在其中一个中忘记它。有许多patterns可以帮助你,但最简单的是在一个负责创建对象的地方创建一个函数,并且每次都遵循它 - 无论它是否创建了1个实例或1,000,000个实例由您决定。

那就是说,如果这没有意义,那么这种防守就是一个很好的后备。请注意,在没有new的情况下使用时,它与JavaScript的内置类型构造函数如何与转换器类型相比具有很多相似性 - 所以我会将用户定义的类型视为一个特别适合的位置使用这种技术。

另见:Is JavaScript's "new" keyword considered harmful?

答案 3 :(得分:2)

“可读性”的倡导者可能会将其描述为anti-pattern,但您比其他任何人都更了解自己的代码 - 如果它适合您,那就去做吧。就个人而言,我宁愿 一个 一个 的事情 - 它使它清晰简洁。如果另一个开发者进入战斗并尝试使用我的对象而没有实例化它,我宁愿抛出错误告诉他/她他们是愚蠢的。