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
属性,如你的例子。
第一步是了解constructor
和prototype
是什么。 这并不难,但必须放弃古典意义上的“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的惯用用法。
一个使用构造函数的情况:
-
这是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; ... };
-
这个
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'