试图围绕Javascript对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
构造函数,那么这个原型属性有什么影响呢?我很想知道它实际上使构造函数属性设置'正确'有什么实际区别 - 因为我无法看到它对创建对象后实际调用的构造函数有任何影响。
constructor
属性对内部任何事物都没有实际的区别。只有在您的代码明确使用它时,它才有用。例如,您可能决定需要每个对象都具有对创建它的实际构造函数的引用;如果是这样,您需要在设置继承时通过将对象分配给构造函数的constructor
属性来显式设置prototype
属性,如示例所示。
第一步是了解constructor
和prototype
的全部内容。这并不困难,但人们不得不放弃传统意义上的“继承”。
构造函数
constructor
属性不会在程序中产生任何特殊效果,除了您可以查看它以查看与运算符new
一起使用哪个函数来创建对象。如果你输入new Bar()
它将是Bar
你输入new Foo
it将是Foo
。
原型
prototype
属性用于查找,以防有问题的对象没有要求的属性。如果你写x.attr
,JavaScript将尝试在attr
的属性中找到x
。如果它找不到它,它将在x.__proto__
看。如果它不存在,只要x.__proto__.__proto__
被定义,它将在__proto__
等等。
那么什么是__proto__
and与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。
一个使用构造函数的情况:
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);
};
所以构造函数属性在这里有意义。
当您希望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.
构造函数属性指向用于创建对象实例的构造函数。如果您输入'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'