2012-04-26 29 views
10

我知道这不是推荐的方式,但是如果我声明以下函数,然后将它们作为构造函数调用,那么结果对象之间会有什么不同(如果有的话)?在JavaScript中,构造函数和函数返回的对象之间有什么不同?

function Something() { 
    this.foo = "bar"; 
} 

function something2() { 
    var that = {}; 
    that.foo = "bar"; 
    return that; 
} 

var x = new Something(); 
var y = new something2(); 
var z = something2(); 

I.e. x,yz这里有什么不同?

不会something2是一个更好的写构造函数的方法,因为不管你使用new还是不会影响函数的结果?

顺便说一句应该something2在这里大写? (我认为不是因为Crockford在大写字母上如此坚决,因为函数会打破全局名字空间......)

+2

有点警惕先生克罗克福德。虽然他有很多好话要说,但他确实有不同意见。 – staticsan 2012-04-26 06:50:37

回答

14

简而言之:

new something2() instanceof something2 === false 

与此相关的,如果你扩展你的榜样使用原型属性

Something.prototype.method = function() { }; 
something2.prototype.method = function() { }; 

你会发现,原型是不是在后一种情况下继承:

typeof (new Something()).method === "function" 
type (new something2()).method === "undefined" 

该rea l答案是你正在攻击完全不同的基础机器。调用new调用[[Construct]]机制,该机制涉及根据构造函数的.prototype属性设置[[Prototype]]属性。

但是一个有趣的事情发生在[[Construct]]算法的步骤8--10:设置一个新的空对象,然后附加其[[Prototype]]后,它会执行[[Call] ]到实际的构造函数,使用这个新的空加原型对象作为this。然后,在第9步中,如果事实证明该构造函数返回了一些东西 - 它抛弃了原型绑定,传递为this的对象,它一直花费在设置上!

注意:您可以访问对象的[[原型](这是由不同构造的.prototype)与Object.getPrototypeOf

Object.getPrototypeOf(new Something()) === Something.prototype // steps 5 & 6 
Object.getPrototypeOf(new something2()) === Object.prototype // default 

回答一些元问题:

  • 不,请不要大写something2,因为它是工厂函数而不是构造函数。如果某些东西是大写的,则预计会有构造函数语义,例如new A() instanceof A
  • 如果您担心全局命名空间遭到破坏,您应该开始使用strict mode,将"use strict";放在文件的顶部。严格模式的很多很好的清理之一是this默认为undefined,而不是全局对象,例如,在构造函数尝试将属性附加到undefined时调用构造函数而不使用new将导致错误。
  • 工厂函数(又称“闭包模式”)通常是构造函数和类的合理替代,只要您(a)不使用继承; (b)不构造太多的对象实例。后者是因为在闭包模式中,每个方法的新实例附加到每个新创建的对象,这对内存使用来说并不好。国际海事组织(IMO)最大的回报就是使用"private" variables(这是一个good thing,并且别让别人告诉你:P)。
+0

第二种方法中的'new'也是可选的(或者确实不需要),但是在第一种情况下忽略掉这些东西(这个===窗口) – Matt 2012-04-26 06:52:38

+0

@ Matt-你可以从内部防御一点点通过看看这个'this'是否是全局对象并相应地开始构造函数。 – RobG 2012-04-26 06:58:12

+1

@Matt,@RobG ---或者,只需在文件顶部加上'“use strict”;'!那么'this'将会是'undefined'。 – Domenic 2012-04-26 07:01:31

2

在第二种情况下,返回的对象不会从构造函数继承任何东西,所以没有什么意义像这样使用它。

> var x = new Something(); 
> var y = new something2(); 
> var z = something2(); 

即这里的x,y和z会有什么不同?从Something,何在既不y或从something2z继承

x继承。

岂不something2是编写构造, 因为你是否使用新的更好的方式还是不不会影响 函数的结果?

有呼吁something2的构造,因为它返回的对象不是分配给其this新构造的对象,从something2.prototype继承,这是另一些可能是调用new something2()时候能得到没有意义的。

顺便说一句,这里应该用大写吗? (我认为不是,因为 克罗克福德是在资本如此坚决,对于功能将 痛殴全局命名空间......)

没有,因为调用它的构造是有点意义的,所以表征它作为一个会误导人。

1

调用一个函数作为构造(即与新的keyword)运行以下步骤:

  1. prototype属性创建新对象
  2. 该对象的原型设置为对象的功能
  3. 在该对象的上下文中执行构造函数(即this是新对象)
  4. 返回该对象(如果构造函数或没有return声明)

因此,您的第二个解决方案将返回一个普通的对象与属性“foo”。但是yz都不是instanceof Something2,并且不从该原型继承。有这样的功能,是的,但他们不应该被称为构造函数(没有大写的命名,没有调用new)。它们属于工厂模式。

如果你想可以在不新执行的构造,使用该代码:

function Something(params) { 
    if (! this instanceof Something) 
     return new Something(params); 
    // else use "this" as usual 
    this.foo = "bar"; 
    ... 
} 
1

我想说的最重要的事情是返回的对象的原型。

function Something() { 
     this.foo = "bar"; 
    } 

    Something.prototype = { 
    // Something prototype code 
    hello: function(){ 
    //... 
    } 
    } 

    function something2() { 
    var that = {}; 
    that.foo = "bar"; 
    return that; 
    } 

    something2.prototype = { 
     // something2 prototype code 
     greetings : function() { 
     //... 
     } 
    } 

    var x = new Something(); 
    var y = new something2(); 
    var z = something2(); 

    typeof x.hello === function // should be true 
    typeof y.greetings === undefined // should be true 
    typeof z.greetings === undefined // should be true 

换句话说,我会说你没有用something2实例化对象,你正在创建纯粹的从Object继承的新对象。当您使用关键字

  1. 东西()会给你“东西”类型的新对象。
  2. something2()会给你“对象”类型的新对象,它将立即返回一个新的空对象。
  3. 新something2是低效的,因为你正在创建一个空白的范围,从中你创建一个新的对象

    var that = {}; 
    

    这相当于

    var that = new Object(); 
    
相关问题