JavaScript在构造函数和作为构造函数调用的函数返回对象之间有什么区别?

时间:2012-04-26 06:44:40

标签: javascript function constructor

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

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();

即。 xyz之间会有什么不同?

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

BTW应something2在这里大写? (我假设不是因为Crockford对资本化如此坚定,因为函数会破坏全局命名空间......)

4 个答案:

答案 0 :(得分:15)

简而言之:

new something2() instanceof something2 === false

相关地,如果扩展您的示例以使用prototype属性

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

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

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

真正的答案是你正在利用完全不同的底层机器。使用new调用将调用[[Construct]]机制,该机制涉及根据构造函数的.prototype属性设置[[Prototype]]属性。

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

注意:您可以使用.prototype访问对象的[[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,而不是全局对象,所以例如在构造函数尝试将属性附加到new时,调用不带undefined的构造函数会导致错误。
  • 工厂函数(又称“闭包模式”)通常是构造函数和类的合理替代,只要您(a)不使用继承; (b)不构造该对象的太多实例。后者是因为,在闭包模式中,您将每个方法的新实例附加到每个新创建的对象,这对于内存使用来说并不是很好。关闭模式的最大收益IMO是能够使用"private" variablesgood thing,并且不要让任何人告诉你:P)。

答案 1 :(得分:2)

在第二种情况下,返回的对象不会从构造函数继承任何内容,因此使用它没有什么意义。

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

即。 x,y和z之间的区别是什么?

x继承自Somethingyz都不会继承something2

  

不会有更好的方法来编写构造函数,   既然你是否使用新的不会影响结果   功能

something2作为构造函数调用是没有意义的,因为它返回的对象不是分配给this的新构造对象,而是继承自something2.prototype,这是其他的调用new something2()时可能会得到。

  顺便说一下,BTW应该在这里大写吗? (我假设没有   对于职能而言,克罗克福德对资本化非常坚定   clobber全局命名空间...)

不,因为将其称为构造函数是有点无意义的,因此将其描述为一个会产生误导。

答案 2 :(得分:1)

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

  1. 创建新对象
  2. 将该对象的原型设置为函数
  3. prototype属性中的对象
  4. 在该对象的上下文中执行构造函数(即this是新对象)
  5. 返回该对象(如果构造函数没有return语句)
  6. 所以,你的第二个解决方案只会返回一个带有属性" foo"的普通对象。但yz都不是instanceof Something2,也不会继承该原型。有这样的函数,是的,但它们不应该被称为构造函数(没有大写命名,没有new的调用)。它们属于工厂模式。

    如果你想要一个可以在没有new的情况下执行的构造函数,请使用该代码:

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

答案 3 :(得分: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

换句话说,我说你没有用某些东西实例化对象,你正在创建从Object继承的纯粹新对象。

  1. 当您使用 new 关键字时,Something()会为您提供“Something”类型的新对象。
  2. something2()将为您提供“对象”类型的新对象,它将立即返回一个新的空对象。
  3. new something2效率低下,因为您正在创建一个空白范围,您可以从中创建一个新对象

    var that = {};
    

    相当于

    var that = new Object();