试图绕过Javascript对OO的看法......和许多其他人一样,对constructor
属性感到困惑。特别是constructor
属性的重要性,因为我似乎无法使其产生任何影响。 E.g:
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
构造函数,那么这个原型属性有什么影响呢?我很想知道它实际上使构造函数属性设置'正确'有什么实际区别 - 因为我看不出它对创建对象后实际调用哪个构造函数有任何影响。
答案 0 :(得分:98)
第一步是了解constructor
和prototype
的全部内容。这并不困难,但必须放弃传统意义上的“继承”。
构造函数
constructor
属性不会导致程序中的任何特定效果,除了您可以查看它以查看与运算符new
一起使用的功能创建你的对象。如果您输入new Bar()
,则会Bar
,而您输入new Foo
则会Foo
。
原型
如果有问题的对象没有要求的属性,prototype
属性用于查找。如果您撰写x.attr
,JavaScript会尝试在attr
的属性中找到x
。如果它找不到它,它将在x.__proto__
中查找。如果它不存在,只要x.__proto__.__proto__
被定义,它就会在__proto__
中查找等等。
那么__proto__
是什么以及它与prototype
有什么关系?简而言之,prototype
用于“类型”,而__proto__
用于“实例”。 (我用引号说,因为类型和实例之间没有任何区别)。当您撰写x = new MyType()
时,x.__proto___
设置为MyType.prototype
会发生什么(包括其他内容)。
问题
现在,上面应该是你需要得出你自己的例子意味着什么,但试图回答你的实际问题; “为什么写点”:
Bar.prototype.constructor = Bar;
我个人从来没有见过它,我发现它有点傻,但在你给出它的上下文中意味着Bar.prototype
- 对象(使用new Foo(42)
创建)将构成由Bar
而不是Foo
创建。我想这个想法是有些类似于C ++ / Java / C#类似的语言,其中类型查找(constructor
属性)将始终产生最具体的类型而不是更通用的对象的类型在原型链中。
我的建议:不要过多考虑JavaScript中的“继承”。接口和mixin的概念更有意义。并且不要检查对象的类型。检查所需的属性(“如果它像鸭子一样走路,像鸭子那样嘎嘎叫,它就是鸭子”)。
尝试强制JavaScript进入经典继承模型,当它具有上述原型机制时,就会导致混淆。建议手动设置constructor
- 属性的许多人可能试图这样做。抽象很好,但是构造函数属性的这种手动赋值并不是非常惯用的JavaScript。
答案 1 :(得分:70)
constructor
属性对内部的任何内容都没有任何实际区别。只有在您的代码明确使用它时,它才有用。例如,您可能决定需要每个对象都具有对创建它的实际构造函数的引用;如果是这样,您需要在设置继承时通过将对象分配给构造函数的constructor
属性来显式设置prototype
属性,如示例所示。
答案 2 :(得分:10)
使用构造函数的一种情况:
这是继承的常见实现之一:
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的构造函数,所以当你创建一个subClass时,你可能需要先调用superClass,如下所示:
SubClass = function() {
SubClass.superClass.constructor.call(this);
};
所以构造函数属性在这里有意义。
答案 3 :(得分:4)
在此之前的回答(以各种方式)表明constructor
属性的值没有被JavaScript本身使用。写下这些答案后确实如此,但ES2015及更高版本已开始使用constructor
做事。
函数的constructor
属性的prototype
属性旨在指向该函数,以便您可以询问对象构造它的原因。它是在创建传统函数对象或类构造函数对象(details)的过程中自动设置的。
function TraditionalFunction() {
}
console.log(TraditionalFunction.prototype.constructor === TraditionalFunction); // true
class ExampleClass {
}
console.log(ExampleClass.prototype.constructor === ExampleClass); // true
箭头功能没有prototype
属性,因此它们没有prototype.constructor
。
多年来,JavaScript规范仅声明constructor
属性在那里,并且默认情况下具有该值(指向该函数的链接)。但是从ES2015开始,情况发生了变化,并且规范中的各种操作现在实际上都使用constructor
属性,例如this,this,this和{{3} }。
因此,在设置构建继承链的构造函数时,最好确保constructor
属性引用了适当的函数。有关示例,请参见this。
答案 4 :(得分:2)
当您希望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.
答案 5 :(得分:0)
构造函数属性指向用于创建对象实例的构造函数。如果你输入了新的Bar()'它将是' Bar'然后你输入了新的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'