构造函数返回什么?

42

如果我在构造函数中返回某个值或对象,那么变量会得到什么?

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

var n = new MyConstroctor();

实际上这是一个问题,无论哪种情况下都会得到什么?

实际上这是一个小测验的问题,以下哪个是从自定义对象构造函数返回的内容?
a)新创建的对象
b)undefined - 构造函数不返回值
c)任何返回语句
d)如果没有返回语句,则为任何返回语句;否则是新创建的对象


4
我的构造函数很棒。永远不要编辑它。你已经走了很长一段路, coure2011。 - ADJenks
7个回答

97

简短回答

构造函数返回 this 对象。

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

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

长篇回答

根据 Javascript 规范,当一个函数被使用 new 调用时,Javascript 会创建一个新对象,然后将该对象的 "constructor" 属性设置为被调用的函数,并最终将该对象赋值给名称为 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'

57

基本上,如果你的构造函数返回一个原始值,例如字符串、数字、布尔值、null或undefined(或者你没有返回任何东西,这相当于返回undefined),则会返回一个从构造函数的原型继承的新创建对象。

这就是你在使用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. 按8.12中指定的方式设置所有obj的内部方法。
  3. obj[[Class]]内部属性设置为"Object"
  4. obj[[Extensible]]内部属性设置为true
  5. 让proto成为使用参数"prototype"调用F[[Get]]内部属性的值。
  6. 如果Type(proto)为Object,则将obj[[Prototype]]内部属性设置为proto。
  7. 如果Type(proto)不是Object,则将obj[[Prototype]]内部属性设置为标准内置对象原型对象,如15.2.4中所述。
  8. 让result成为使用参数args调用F的[[Call]]内部属性的结果,并将obj作为this值传递。
  9. 如果Type(result)为Object,则返回result。
  10. 返回obj

@CMS,如果您通过this设置函数对象的某些属性,并且从函数对象返回一个原始值或除对象本身以外的任何其他内容,则会发生以下两件事情:
  1. 如果您使用new关键字创建新实例,则该原始返回值无意义,但是在新创建的对象上设置了属性,但如果您返回一个object ,则之前设置的属性将被丢失
  2. 当您将其作为普通函数调用时,这些属性将设置在window上,并且您将得到所返回的原始值。这准确吗?
- Marius Mucenicu
1
@CMS 链接似乎已经失效。它要么超时了,要么重定向到非编程页面。我认为这两个链接之一:这个 或者 这个 描述了您上面所描述的内容。您能否更新答案并提供相关链接,以便其他人可以访问相应的页面。谢谢! - Marius Mucenicu
1
@George,谢谢你的关注!在规范的最新版本中,应该是这个章节,但我回答中描述的算法来自ES5。 - Christian C. Salvadó
我们得到了相同的结果,但我本来期望返回一个“对象”,而不是一个“函数”。我以为除了“对象”之外的任何东西都会被丢弃并返回一个“对象”。如果你返回一个无意义的“基元”,你会得到一个“对象”,但如果你返回一个“函数”,你会得到那个“函数”。有趣.. - Marius Mucenicu
1
@George,不用谢。是的,规范语言相当技术化和枯燥。我想我已经习惯了,自第三版以来就开始沉迷于阅读它。现在它比以前复杂得多了。如果你有任何问题,随时可以通过Twitter联系我或给我发送电子邮件:c@cms.gt。干杯! - Christian C. Salvadó
显示剩余6条评论

26

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

JavaScript: 构造函数返回值

上面提到的第二个神奇之处是构造函数能够返回一个特定的、可能预先存在的对象,而不是一个对新实例的引用。如果需要,这将使您能够自己管理实例的数量;可能是由于有限的资源或其他原因。

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


1
这是存档链接:https://web.archive.org/web/20100216124827/http://www.gibdon.com/2008/08/javascript-constructor-return-value.html - Kamafeather
垃圾回收怎么样?上面链接中的文章提到“以过程方式管理”,但该文章现在已经有10年了。在新的JavaScript类实现中是否有任何变化? 我认为你不能把这个放在你的构造函数的末尾: delete this; 或者你可以吗? 否则,你必须将this存储在全局某个地方,以便调用构造函数的函数可以删除它。对此还有什么进一步的见解吗?我不是试图使用此功能来管理实例。在某些情况下,我使用它来返回不同类的实例。 - Sideways S

2

如文档中所述,这很容易实现:(new运算符)

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


2

本文试图通过提供离散的例子来简化现有答案,并证明以下事实:

只有返回primitiveundefined的构造函数会隐式创建新的实例对象,否则将直接使用由构造函数返回的完整对象。

考虑下面这个构造函数,它会返回我们传递给它的内容。请查看用法示例:

//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或原始类型,我们可以使用以下内容,这可能会让从java转过来的人感到奇怪:
(new MyClass()) instanceof MyClass //false

使用相同的构造函数,在实践中检查当返回undefined或原始值时实例的区别:

//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


1
回答您的具体问题:
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

1

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


1
请查看我的评论:http://www.gibdon.com/2008/08/javascript-constructor-return-value.html - Haim Evgi
1
如果你返回5,你将得到一个空对象,如果你返回一个对象,那么n可能会指向这个对象。 - Darin Dimitrov
不,我刚在Firefox上测试了一下,如果我返回5,它会给我默认对象。如果我返回某个对象,它会给我那个对象。 - coure2011
1
实际上,在Javascript中有许多常见的面向对象编程模式,它们在构造函数中手动返回一个对象。即使是Douglas Crockford也认可这种做法。 - Kenan Banks

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接