【问题标题】:What is returned from a constructor?构造函数返回什么?
【发布时间】:2011-03-21 23:35:04
【问题描述】:

如果我在构造函数中返回一些值或对象,var 会得到什么?

function MyConstroctor()
{
    //what in case when return 5;
    //what in case when return someObject;
}

var n = new MyConstroctor();

n 在这两种情况下会得到什么?

其实是个问答题,答案是什么?
自定义对象构造函数返回什么?
a) 新实例化的对象
b)undefined - 构造函数不返回值
c)无论返回语句是什么
d) 无论返回声明是什么;如果没有返回语句,则新实例化的对象

【问题讨论】:

标签: javascript


【解决方案1】:

简答

构造函数返回this对象。

function Car() {
   this.num_wheels = 4;
}

// car = { num_wheels:4 };
var car = new Car();

长答案

根据 Javascript 规范,当使用 new 调用函数时,Javascript 会创建一个新对象,然后将该对象的“构造函数”属性设置为调用的函数,最后将该对象分配给名称 this .然后,您可以从函数体访问this 对象。

一旦函数体被执行,Javascript就会返回:

如果返回值的类型是object,则为任何对象:

function Car(){
  this.num_wheels = 4;
  return { num_wheels:37 };
}

var car = new Car();
alert(car.num_wheels); // 37

如果函数没有return 语句或者如果函数返回的值不是object,则为this 对象:

function Car() {
  this.num_wheels = 4;
  return 'VROOM';
}

var car = new Car();
alert(car.num_wheels); // 4
alert(Car()); // No 'new', so the alert will show 'VROOM'

【讨论】:

    【解决方案2】:

    基本上,如果您的构造函数返回一个原始值,例如字符串、数字、布尔值、null 或未定义,(或者您不返回任何等同于返回undefined),将返回一个继承自构造函数 prototype 的新创建对象。

    当使用 new 关键字调用时,您可以在构造函数中使用 this 关键字访问该对象。

    例如:

    function Test() {
      return 5; // returning a primitive
    }
    
    var obj = new Test();
    obj == 5; // false
    obj instanceof Test; // true, it inherits from Test.prototype
    Test.prototype.isPrototypeOf(obj); // true
    

    但是如果返回值是一个对象引用,那就是返回值,例如:

    function Test2() {
      this.foo = ""; // the object referred by `this` will be lost...
      return {foo: 'bar'};
    }
    
    var obj = new Test2();
    obj.foo; // "bar"
    

    如果你对new操作符的内部感兴趣,你可以查看[[Construct]]内部操作的算法,它负责创建从构造函数原型继承的新对象,并决定什么返回:

    13.2.2 [[Construct]]

    当使用可能为空的参数列表调用 Function 对象 F 的 [[Construct]] 内部方法时,将执行以下步骤:

    1. 让 obj 成为新创建的原生 ECMAScript 对象。
    2. 设置obj的所有内部方法在8.12中指定。
    3. 将obj的[[Class]]内部属性设置为"Object"。
    4. 将obj的[[Extensible]]内部属性设置为true。
    5. 令 proto 为使用参数 "prototype" 调用 F 的 [[Get]] 内部属性的值。
    6. 如果Type(proto)是Object, set the[[Prototype]]`obj到proto的内部属性。
    7. 如果Type(proto) 不是Object,则将obj 的[[Prototype]] 内部属性设置为标准内置Object 原型对象,如15.2.4 中所述。
    8. 令 result 为调用 F 的 [[Call]] 内部属性的结果,提供 obj 作为 this 值,并提供传递给 [[Construct]] 的参数列表作为 args。
    9. 如果 Type(result) 是 Object 则返回结果。
    10. 返回obj。

    【讨论】:

    • @CMS 所以基本上如果你通过this 在function object 上设置一些属性并返回一个原语 或来自function object 的任何其他内容,除了object本身,会发生两件事: 1. 当您通过 new 关键字创建一个新实例时,原始返回值是没有意义的,但属性是在新创建的对象上设置的,但是如果您返回 object 先前设置属性丢失。 2. 当您将其作为普通函数调用时,这些属性设置在window 上,您将获得返回的原始值。这准确吗?
    • @George,是的,它是准确的。请注意,您的观点没有。 2,如果您在严格模式下,在“正常函数调用”this 将是undefined。这将在尝试为其分配属性时产生TypeError。
    • @CMS 链接似乎已损坏。它要么超时,要么重定向到非编程页面。我相信其中之一:this 或 this 描述了您上面描述的内容。请您使用相关链接更新答案,以便人们可以转到相应的页面。谢谢!
    • @George,感谢您的关注!规范的最后一个版本是this section,但我的回答中描述的算法来自 ES5。
    • @George,不客气。是的,规格。语言相当技术性和枯燥。我想我已经习惯了,我从第 3 版开始就开始疯狂阅读。现在它比以前复杂得多。如果您有任何问题,请随时通过 twitter 与我联系或给我发邮件:c@cms.gt Cheers!
    【解决方案3】:

    我找到了这个很棒的链接:

    JavaScript: Constructor Return Value

    上面没有提到的第二个魔法是 构造函数返回一个特定的,可能预先存在的对象,而不是 而不是对新实例的引用。这将允许您管理 如果需要,您自己的实际实例数量;可能是因为 资源有限之类的。

    var g_deebee = new Deebee();
    function Deebee() { return g_deebee; }
    var db1 = new Deebee();
    var db2 = new Deebee();
    if (db1 != db2)
      throw Error("JS constructor returned wrong object!");
    else console.log('Equal');

    【讨论】:

    • 垃圾回收呢?上面链接中的文章提到“以程序方式管理它”,但该文章现在已有 10 年历史了。在 JavaScript 类的较新实现中是否有任何变化?我认为您不能将其放在构造函数的末尾:delete this; 或者可以吗?否则,您必须将this 存放在全局某个地方,以便调用构造函数的函数可以将其删除。对此有任何进一步的见解吗?我不想使用此功能来管理实例。在某些情况下,我使用它来返回不同类的实例。
    【解决方案4】:

    尝试通过提供离散示例来简化现有答案:

    只有返回 primitive 或 undefined 的构造函数才会隐式创建自己的新实例。否则,构造函数返回的确切对象将按原样使用。

    考虑下面的构造函数,它准确地返回我们传递给它的内容。检查用法:

    //A constructor returning the passed value, or not returning at all.
    function MyConstructor(obj){
        if(obj!==undefined){
            return obj;
        }
        //else do not call return
    }
    
    //no value passed (no value is returned from constructor)
    console.log((new MyConstructor()) instanceof MyConstructor)
    //true
    
    //Primitive passed:
    console.log((new MyConstructor(1)) instanceof MyConstructor)
    //true
    console.log((new MyConstructor(false)) instanceof MyConstructor)
    //true
    console.log((new MyConstructor("1")) instanceof MyConstructor)
    //true
    console.log((new MyConstructor(1.0)) instanceof MyConstructor)
    //true
    
    //Object passed
    console.log((new MyConstructor(new Number(1))) instanceof MyConstructor)
    //false
    console.log((new MyConstructor({num:1})) instanceof MyConstructor)
    //false
    console.log((new MyConstructor([1])) instanceof MyConstructor)
    //false
    console.log((new MyConstructor(MyConstructor)) instanceof MyConstructor)
    //false
    
    //Same results if we use: MyConstructor.prototype.isPrototypeOf(new MyConstructor()) e.t.c..

    上述相同的规则也适用于类构造函数。这意味着,如果构造函数没有返回 undefined 或 primitive,我们可以得到以下内容,这可能会让来自 java 的人感到奇怪:

    (new MyClass()) instanceof MyClass //false
    

    使用相同的构造函数,在实践中检查实例在返回未定义或原语时有何不同:

    //As above
    function MyConstructor(obj){
        if(obj!==undefined){
            return obj;
        }
        //else do not call return
    }
    
    //Same object passed (same instance to both variables)
    let obj = {};
    let a1 = new MyConstructor(obj)
    let a2 = new MyConstructor(obj)
    
    a1.x=1
    a2.x=2
    
    console.log(a1.x === a2.x, a1.x, a2.x)
    //true 2 2
    
    //undefined passed (different instance to each variable)
    let b1 = new MyConstructor()
    let b2 = new MyConstructor()
    
    b1.x=1
    b2.x=2
    console.log(b1.x === b2.x, b1.x, b2.x)
    //false 1 2
    
    //Primitive passed (different instance to each variable)
    let c1 = new MyConstructor(5)
    let c2 = new MyConstructor(5)
    
    c1.x=1
    c2.x=2
    console.log(c1.x === c2.x, c1.x, c2.x)
    //false 1 2

    补充说明:有时一个函数即使不作为构造函数调用,也可以充当构造函数:

    function F(){
        //If not called as a constructor, call as a constructor and return the result
        if(!new.target){
            return new F();
        }
    }
    
    console.log(F() instanceof F)
    //true
    console.log(new F() instanceof F)
    //true

    【讨论】:

      【解决方案5】:

      您不应该在 构造函数中返回任何内容。构造函数用于初始化对象。如果你想知道会发生什么,如果你返回5,那么n 将只是一个空对象,如果你返回例如{ a: 5 },那么n 将有一个属性a=5。

      【讨论】:

      • 如果你返回 5 你会得到一个空对象,如果你返回一个对象那么 n 可能会指向这个对象。
      • 不,刚刚在 Firefox 上测试过,如果我返回 5,它会给我默认对象。如果我返回一些对象,它会给我那个对象。
      • 实际上在 Javascript 中有许多常见的 OOP 模式,它们在构造函数中手动返回一个对象。即使是道格拉斯·克罗克福德也可以接受。
      【解决方案6】:

      回答您的具体问题:

      function MyConstructor()
      {
          return 5;
      }
      var n = new MyConstructor();
      

      n 是 MyConstructor 的一个对象实例。

      function SomeObject(name) {
          this.name = name;
          this.shout = function() {
              alert("Hello " + this.name)
          }
      } 
      
      function MyConstructor()
      {
          return new SomeObject("coure06");
      }
      
      var n = new MyConstructor();
      
       n.shout();
      

      n 是 SomeObject 的一个实例(调用 n.shout() 来证明)

      为了让这一切都一清二楚……

      1) 如果您返回原始类型,例如数字或字符串,它将被忽略。 2) 否则,您将传回对象

      函数和构造函数在 JavaScript 中是完全一样的,但是你如何称呼它们会改变它们的行为。下面是一个简单的例子......

      function AddTwoNumbers(first, second) {
          return first + second;
      }
      
      var functionCall = AddTwoNumbers(5, 3);
      alert(functionCall);// 8
      
      var constructorCall = new AddTwoNumbers(5, 3);
      alert(constructorCall);// object
      

      【讨论】:

        【解决方案7】:

        正如documentation (new operator) 中所说的那样简单:

        构造函数返回的对象成为整个new表达式的结果。如果构造函数没有显式返回对象,则使用在步骤 1 中创建的对象。 (通常构造函数不返回值,但如果他们想覆盖正常的对象创建过程,他们可以选择这样做。)

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2019-02-27
          • 1970-01-01
          • 1970-01-01
          • 2010-12-30
          • 2015-11-03
          • 1970-01-01
          相关资源
          最近更新 更多