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

在JavaScript中,构造函数和作为构造函数调用的返回对象的函数之间有什么区别?,javascript,function,constructor,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

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

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();
也就是说,
x
y
z
之间的区别是什么

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


顺便说一句,
something2
应该在这里大写吗?(我假设不是因为Crockford对大小写如此坚定,因为函数会破坏全局名称空间…

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

> var x = new Something();
> var y = new something2();
> var z = something2();
也就是说,这里的x、y和z有什么不同

x
继承自
Something
,其中既不是
y
也不是
z
继承自
something2

写构造函数不是更好的方法吗, 因为无论你是否使用新的都不会影响测试结果 功能

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

顺便说一句,这里应该大写一些东西吗?(我想从那以后就不会了 Crockford非常坚持大写,因为函数将 删除全局命名空间…)

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

简而言之:

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
调用调用该机制,该机制涉及根据构造函数的
.Prototype
属性设置[[Prototype]]属性

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

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

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

要回答一些元问题:

  • 不,不要大写
    something2
    ,因为它是工厂函数而不是构造函数。如果某个内容是大写的,则它应该具有构造函数语义,例如
    新的A()实例A
  • 如果您担心破坏全局名称空间的危险,那么应该开始使用,方法是将
    “use strict”位于文件的顶部。strict模式的许多好的清理方法之一是
    默认为
    未定义的
    ,而不是全局对象,因此,例如,在构造函数尝试将属性附加到
    未定义的
    时,调用未添加
    的构造函数将导致错误
  • 工厂函数(又名“闭包模式”)通常是构造函数和类的合理替代品,只要您(a)不使用继承;(b) 不要构造太多该对象的实例。后者是因为,在闭包模式中,您将每个方法的一个新实例附加到每个新创建的对象,这对内存使用不是很好。IMO,闭包模式的最大回报是使用的能力(这是一个,不要让任何人告诉你:P)

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

  • 创建一个新对象
  • 将该对象的原型设置为函数的
    prototype
    属性中的对象
  • 在该对象的上下文中执行构造函数(即,
    是新对象)
  • 返回该对象(如果构造函数没有
    return
    语句)
  • 因此,您的第二个解决方案将只返回一个带有属性“foo”的普通对象。但是无论是
    y
    还是
    z
    都不是某个事物的
    实例2
    ,也不是从该原型继承的。有这样的函数,是的,但它们不应该被称为构造函数(不使用大写字母命名,不使用
    new
    调用)。它们属于工厂模式

    如果您想要一个无需新代码即可执行的构造函数,请使用该代码:

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

    我认为最重要的是返回对象的原型

      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
    
    换句话说,我要说的是,您不是在用某种东西实例化对象,而是在创建从对象继承的纯新对象

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

    var that = {};
    
    这相当于

    var that = new Object();
    

  • 对克罗克福德先生要有点警惕。虽然他有很多好话要说,但他确实有一些不同意的意见。第二种方法中的“新”也是可选的(或实际上不需要),但如果在第一种方法中被忽略,就会把事情搞砸(这个===窗口)@Matt