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
构造函数,那么这个原型属性有什么影响?我很想知道“正确设置”构造函数的属性会对实际产生什么影响,因为我看不到它对创建对象后实际调用哪个构造函数有任何影响。#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 楼
第一步是了解constructor
和prototype
的全部含义。这并不困难,但是必须放弃经典意义上的“继承”。除了您可以查看它以查看与运算符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'
评论
我已经在以下地址解释了原型与构造方法之间的关系:stackoverflow.com/a/23877420/895245