【问题标题】:What difference is there in JavaScript between a constructor function, and function returning object which is invoked as a constructor?JavaScript中构造函数和作为构造函数调用的函数返回对象之间有什么区别?
【发布时间】:2012-04-26 06:44:39
【问题描述】:

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

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 都不会影响函数的结果?

顺便说一句,something2 应该在这里大写吗? (我认为不会,因为 Crockford 如此坚持大写,因为函数会破坏全局命名空间......)

【问题讨论】:

  • 对克罗克福德先生有点警惕。虽然他有很多好话要说,但他确实有不同意的意见。

标签: javascript function constructor


【解决方案1】:

简而言之:

new something2() instanceof something2 === false

如果您扩展示例以使用原型属性

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]]到实际的构造函数,使用这个新的空加原型对象作为this。然后,在第 9 步中,如果结果证明该构造函数返回了一些东西——它会丢弃它花费所有时间设置的原型绑定、传递为this 的对象!

注意:您可以使用 Object.getPrototypeOf 访问对象的 [[Prototype]](与构造函数的 .prototype 不同):

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)。

【讨论】:

  • 第二种方法中的“新”也是可选的(或者实际上不需要),但是在第一种方法中省略时会搞砸(这个 === 窗口)
  • @Matt——您可以通过查看this 是否是全局对象并相应地处理来从构造函数内部稍微防御一下。
  • @Matt, @RobG---或者,只需将"use strict"; 放在文件顶部!那么this 将是undefined
  • @Dominic 在函数的顶部就足够了——但我们将不得不等待 JScript 跟上。一般来说,ES5 严格模式还不是一个安全的特性,即使在非 JScript 中也是如此。
  • 为什么要等待 JScript?它将帮助您在开发中发现错误,您可能在其中测试的不仅仅是 IE
【解决方案2】:

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

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

即这里的 x、y 和 z 有什么区别?

x 继承自 Something,而 yz 均未继承自 something2

something2 不是编写构造函数的更好方法吗? 因为无论你是否使用 new 都不会影响结果 功能?

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

顺便说一句,something2 应该在这里大写吗? (我假设不是因为 克罗克福德对大写如此坚持,因为函数将 破坏全局命名空间...)

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

【讨论】:

    【解决方案3】:

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

      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() 将为您提供“Object”类型的新对象,这将立即返回一个新的空对象。
    3. new something2 效率低下,因为您正在创建一个空白范围,从中创建一个新对象

      var that = {};
      

      相当于

      var that = new Object();
      

    【讨论】:

      【解决方案4】:

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

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

      因此,您的第二个解决方案将只返回一个带有属性“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";
          ...
       }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-03-19
        • 1970-01-01
        • 2011-10-23
        • 1970-01-01
        • 1970-01-01
        • 2015-02-24
        相关资源
        最近更新 更多