试图屈服于Java脚本对OO的追求……并像其他许多方面一样,对constructor属性感到困惑。特别是constructor属性的重要性,因为我似乎无法使其发挥任何作用。例如:

function Foo(age) {
    this.age = age;
}

function Bar() {
    Foo.call(this, 42);
    this.name = "baz"; 
}

Bar.prototype = Object.create(Foo.prototype); 
var b = new Bar;    

alert(b.constructor); // "Foo". That's OK because we inherit `Foo`'s prototype.
alert(b.name);        // "baz". Shows that Bar() was called as constructor.
alert(b.age);         // "42", inherited from `Foo`.


在上面的示例中,对象b似乎具有称为(Bar)的正确构造函数–并且它继承了Foo的age属性。那么,为什么很多人建议将此作为必要步骤:

Bar.prototype.constructor = Bar;


显然,在构造Bar时调用了正确的b构造函数,那么这个原型属性有什么影响?我很想知道“正确设置”构造函数的属性会对实际产生什么影响,因为我看不到它对创建对象后实际调用哪个构造函数有任何影响。

评论

我已经在以下地址解释了原型与构造方法之间的关系:stackoverflow.com/a/23877420/895245

#1 楼

2020年9月更新
下面的答案是ECMAScript 3诞生之日起的第一句话不再适用,因为自ECMAScript 6起,constructor属性已在一些地方使用。但是,我认为总体要旨仍然适用。感谢T. J. Crowder在评论中指出了这一点,请阅读他的答案以更全面地了解当前情况。
原始答案
constructor属性对内部任何事物都没有任何实际区别。仅当您的代码明确使用它时,它才有用。例如,您可能决定需要每个对象都具有对创建它的实际构造函数的引用。如果是这样,则在设置继承时,需要通过将对象分配给构造函数的constructor属性来显式设置prototype属性,如您的示例。

评论


构造函数是能够填充Object.getPrototypeOf的重要部分。我个人使用Object.getPrototypeOf并假设每个人都不会破坏原型<->构造函数关系

–雷诺斯
2012年4月12日在21:14

@Raynos:这不是最安全的假设,但可能比以前更安全。可以追溯到几年前,几乎所有Web上的JavaScript对象继承教程都受Java影响,建议您手动设置构造函数属性。您和我的想法完全不同:我更喜欢使用包装而不是填充,并且在没有Object.getPrototypeOf的情况下生活了很长时间,以至于我现在对此没有强烈的渴望。

– Tim Down
2012年12月12日在22:06

要点是,我们应该警告人们,对于ES5合规性,不应在较旧的浏览器中打破这种关系

–雷诺斯
2012年4月12日在22:26

@Raynos:我想我应该考虑依赖构造函数属性,正如所讨论的那样,该属性具有被脚本混入的历史,否则就没有用了,这是任何垫片的不可接受的要求,因此一般不建议使用它。

– Tim Down
2012年12月12日22:39

@ T.J。Crowder:很好,很好。我没有关注最新的语言版本。无论如何,我认为我从未使用过构造函数。感谢您的更新,我将在答案中添加注释。

– Tim Down
9月2日15:38

#2 楼

第一步是了解constructorprototype的全部含义。这并不困难,但是必须放弃经典意义上的“继承”。除了您可以查看它以查看与运算符constructor一起使用了哪个函数来创建对象。如果键入new,它将是new Bar(),然后键入Bar。它将是new Foo。有要求的财产。如果您编写Foo,JavaScript将尝试在prototype的属性中找到x.attr。如果找不到,它将在attr中查找。如果也不存在,只要定义了x,它就会一直在x.__proto__中查找。简而言之,x.__proto__.__proto__用于“类型”,而__proto__用于“实例”。 (我说带引号是因为类型和实例之间实际上没有任何区别)。当您编写__proto__时,发生的事情(除其他外)是prototype设置为prototype。您自己的例子意味着,但要尝试回答您的实际问题; “为什么要写类似的东西”:

__proto__

我个人从未见过它,但我发现它有点傻,但是在上下文中,您给出的含义x = new MyType() -object(使用x.__proto___创建)将构成MyType.prototype而非Bar.prototype.constructor = Bar;创建的对象。我想这个想法有些类似于类似于C ++ / Java / C#的语言,其中类型查找(Bar.prototype属性)将始终产生最特定的类型,而不是原型中更通用的对象的类型,链。

我的建议:不要过多考虑JavaScript中的“继承”。接口和混合的概念更有意义。并且不要检查对象的类型。而是检查所需的属性(“如果它像鸭子一样走路,而像鸭子一样嘎嘎叫,那是鸭子”)。

试图将JavaScript强制为经典继承模型,而前提是它具有如上所述的原型机制是造成混乱的原因。建议手动设置new Foo(42) -property的许多人可能试图做到这一点。抽象是可以的,但是手动构造函数属性的分配并不是JavaScript的惯用用法。

评论


我同意,构造函数属性不是很有用,但是从另一个对象的原型继承可能会非常强大,并且我完全不会灰心。

– Tim Down
2010-10-25 9:38

谢谢-可能不是惯用的,但是网上的许多教程(以及SE上的问与答)都建议“需要”使用此特定方法-好像整个原型模型都损坏了(如果您不使用它)。

–aaa90210
2010-10-25 9:47

哦,很抱歉,那是怎么回事。从另一个对象原型继承不是问题。更改构造函数属性。

–雅各布
2010-10-25 9:49

@Jakob,您说:“构造函数属性不会对程序产生任何特殊影响,只是您可以查看它以查看哪个函数与new运算符结合使用来创建对象。如果键入new Bar()它将是Bar,而您键入的新Fooit将是Foo。”这是不正确的。构造函数属性引用创建给定对象原型的函数,如示例中甚至由OP所见。

–βξhrαng
2013年6月11日在16:11



“您可以查看它,看看哪个函数与new运算符一起使用来创建对象”。不,你不能。它提供了有关原型对象也首先关联的功能的提示(前提是尚未将构造函数属性设置为其他值)。它不能肯定地告诉您继承它的实例。

–RobG
2014年5月20日下午2:10

#3 楼

使用构造函数的一种情况:



这是继承的常见实现之一:

Function.prototype.extend = function(superClass,override) {
    var f = new Function();
    f.prototype = superClass.prototype;
    var p = this.prototype = new f();
    p.constructor = this;
    this.superclass = superClass.prototype;
    ...
};



new f()不会调用superClass的构造函数,因此在创建子类时,也许首先需要调用superClass,例如:

SubClass = function() {
    SubClass.superClass.constructor.call(this);
};



,所以constructor属性在这里很有意义。

评论


是的,但是您可以编写SubClass.superClass.call(this);。

–阿里·夏奇巴(Ali Shakiba)
15年4月8日在11:34

除非您打算在实例上引用构造函数属性,否则可以安全地注释p.constructor =此行,并且没有任何变化。答案可能演示了使用构造函数属性的情况,但没有解释为什么在此处使用它。简而言之,此答案不能回答关于构造函数属性的重要性的原始问题。

– golem
16年1月21日在20:13



1.您能否编写一个简单的用法示例? 2.替代的作用是什么?

–OfirD
16年11月9日在9:51



#4 楼

前面的答案(以各种方式)表明,JavaScript本身没有使用constructor属性的值。写下这些答案后确实如此,但是ES2015及更高版本已开始使用constructor做事。
函数的constructor属性的prototype属性旨在指向该函数,以便您可以询问对象什么建造它。它是在创建传统函数对象或类构造函数对象(详细信息)的过程中自动设置的。



 function TraditionalFunction() {
}

console.log(TraditionalFunction.prototype.constructor === TraditionalFunction); // true

class ExampleClass {
}

console.log(ExampleClass.prototype.constructor === ExampleClass); // true 




箭头函数没有prototype属性,因此它们没有prototype.constructor。链接回该函数)。但是从ES2015开始,情况发生了变化,并且规范中的各种操作现在实际上都使用了constructor属性,例如this,this,this和
。确保constructor属性引用了适当的功能。有关示例等,请参见此处的答案。

#5 楼

当您希望prototype.constructor属性可以保留prototype属性的一种使用情况是,您在prototype上定义了一种方法,该方法生成与给定实例具有相同类型的新实例。例如:



function Car() { }
Car.prototype.orderOneLikeThis = function() {  // Clone producing function
    return new this.constructor();
}
Car.prototype.advertise = function () {
    console.log("I am a generic car.");
}

function BMW() { }
BMW.prototype = Object.create(Car.prototype);
BMW.prototype.constructor = BMW;              // Resetting the constructor property
BMW.prototype.advertise = function () {
    console.log("I am BMW with lots of uber features.");
}

var x5 = new BMW();

var myNewToy = x5.orderOneLikeThis();

myNewToy.advertise(); // => "I am BMW ..." if `BMW.prototype.constructor = BMW;` is not 
                      // commented; "I am a generic car." otherwise.


评论


您为什么不喜欢Object.setPrototypeOf(BMW.prototype,Car.prototype);而不是BMW.prototype = Object.create(Car.prototype);?

–过度兑换
17-10-30在5:41

@overexchange是的,这样可以减少设置构造函数步骤。我们应该使用那个权利?

–苏拉杰(Suraj Jain)
19年4月15日在14:59

#6 楼

Constructor属性指向用于创建对象实例的构造函数。如果您键入“ new Bar()”,它将是“ Bar”,而您键入“ new Foo()”,它将是“ Foo”。

但是如果您设置原型而不设置构造函数,您将得到如下内容:

function Foo(age) {
    this.age = age;
}

function Bar() {
    this.name = "baz"; 
}

Bar.prototype = new Foo(42); 
var one = new Bar();
console.log(one.constructor);   // 'Foo'
var two = new Foo();
console.log(two.constructor);   // 'Foo'


要实际将构造函数设置为用于创建对象的构造函数,我们需要在设置时同时设置构造函数原型如下:

function Foo(age) {
    this.age = age;
}

function Bar() {
    this.name = "baz"; 
}

Bar.prototype = new Foo(42); 
Bar.prototype.constructor = Bar;
var one = new Bar();
console.log(one.constructor);   // 'Bar'
var two = new Foo();
console.log(two.constructor);   // 'Foo'