Javascript构造函数属性的意义是什么?

试图弯曲JavaScript的承担OO …和其他许多人一样,混淆了constructor属性。 特别是constructor属性的意义,因为我似乎无法使其产生任何效果。 例如:

 function Foo(age) { this.age = age; } function Bar() { this.name = "baz"; } Bar.prototype = new Foo(42); 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 ) – 它inheritance了Foo的age属性。 那么为什么很多人认为这是一个必要的步骤:

 Bar.prototype.constructor = Bar; 

显然,在构造b时调用了正确的Bar构造函数,那么这个prototype属性有什么影响呢? 我很好奇,知道实际的差异,实际上使build设者属性设置“正确”,因为我不能看到它有什么影响创build对象后实际调用哪个构造函数。

constructor属性在内部完全没有实际的区别。 如果你的代码明确地使用它,这只是任何用途。 例如,你可能会决定你需要每个对象来引用创build它的实际构造函数; 如果是这样的话,当你设置inheritance的时候,你需要显式地设置构造函数的属性,方法是将一个对象赋值给构造函数的prototype属性,如你的例子。

第一步是了解constructorprototype是什么。 这并不难,但必须放弃古典意义上的“inheritance”。

构造函数

constructor属性在你的程序中不会引起任何特殊的效果,只不过你可以看看哪个函数和new运算符一起用来创build对象。 如果你inputnew Bar()它会是Bar ,你inputnew Foo它将是Foo

原型

如果所讨论的对象没有要求的属性,那么prototype属性用于查找。 如果你写x.attr ,JavaScript会尝试从x的属性中findattr 。 如果找不到,它会在x.__proto__ 。 如果不存在,则只要__proto__被定义,它就会在x.__proto__.__proto__等等中查找。

那么__proto__prototype有什么关系呢? 简而言之, prototype是“types”,而__proto__是“实例”。 (我用引号说,因为types和实例之间没有任何区别)。 当你写x = new MyType() ,会发生什么(除此之外)是x.__proto___被设置为MyType.prototype

这个问题

现在,上面应该是所有你需要得出你自己的例子的意思,但试图回答你的实际问题; “为什么写这样的东西”:

Bar.prototype.constructor = Bar;

我个人从来没有见过它,我觉得它有点傻,但在上下文中,你会得到这将意味着Bar.prototype (通过使用new Foo(42)创build)将构成由Bar创build而不是Foo 。 我想这个想法有些类似于C ++ / Java / C#类似的语言,其中types查找( constructor属性)将始终产生最具体的types,而不是原型中更一般对象的types -链。

我的build议是:不要太在意JavaScript的“inheritance”。 接口和mixin的概念更有意义。 不要检查对象的types。 检查所需的属性,而不是(如果它走路像一只鸭子,鸭子像鸭子,它是一只鸭子)。

试图强制JavaScript成为一个经典的inheritance模型,当它所拥有的只是上述的原型机制时,就是造成混淆的原因。 许多人build议手动设置constructor – 属性可能试图做到这一点。 抽象是好的,但是这个手工赋值的构造函数属性不是JavaScript的惯用用法。

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

  1. 这是inheritance的常见实现之一:

     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; ... }; 
  2. 这个new f()不会调用superClass的构造函数,所以当你创build一个子类的时候,也许你需要先调用superClass,像这样:

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

所以构造函数属性在这里是有意义的。

您希望prototype.constructor属性保留prototype属性重新分配的用例之一是在prototype定义一个方法,该方法会生成与给定实例相同types的新实例。 例:

 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. 

构造函数属性指向用于创build对象实例的构造函数。 如果你input'new Bar()'就会是'Bar',你input'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' 

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

 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'