假设我们有这个构造函数:
var Foo = function(){
this.x = "y";
}
Foo
和Foo.prototype.constructor
评估相同的功能,但Foo.prototype.constructor = function(){this.x="z"}
似乎没有更改new Foo()
的结果。但它会改变
var i = new Foo();
i.constructor; // evals to function (){this.x = "z"}
这里发生了什么?我不打算使用来做任何事情,我只是对这种语言感到好奇。
答案 0 :(得分:3)
函数的constructor
属性的prototype
属性旨在指向函数,以便您可以向对象询问构造它的内容。它是作为更新功能对象的一部分自动设置的(参见规范的Section 13.2)。如您所见,您可以覆盖constructor
上的Foo.prototype
属性,如果您愿意,可以更改它,但默认情况下就是它的用途。
有一个很好的理由可以覆盖它,这与继承有关。假设您希望拥有一个用于创建基础对象的Base
构造函数,以及一个Derived
构造函数,该构造函数用于创建具有Base
特征以及Derived
的添加/修改的派生对象。通常(虽然不是我的想法)你看到完成(没有帮助脚本)的方式是:
function Base() {
}
Base.prototype.foo = function() {
console.log("I'm Base#foo");
};
function Derived() {
}
Derived.prototype = new Base(); // So we get all the `Base` stuff
Derived.prototype.bar = function() {
console.log("I'm Derived#bar");
};
var d = new Derived();
d.foo(); // "I'm Base#foo"
d.bar(); // "I'm Derived#bar"
现在的问题是,d.constructor === Base
而不是Derived
。所以能够解决这个问题很重要:
...
Derived.prototype = new Base(); // So we get all the `Base` stuff
Derived.prototype.constructor = Derived; // Fix up
...
(旁注:所有这些管道 - 以及围绕超级调用的复杂性 - 是为什么这么多人为此创建了帮助脚本的原因,包括(咳嗽) mine。)
请注意,上述内容并不是设置继承层次结构的理想方法。这是你经常看到的,但正如我上面所说,并不理想。为了完整起见,这更好:
function Base() {
}
Base.prototype.foo = function() {
console.log("I'm Base#foo");
};
function Derived() {
Base.call(this); // So Base sets up its stuff
}
Derived.prototype = Object.create(Base.prototype); // So we get all the `Base` stuff
Derived.prototype.bar = function() {
console.log("I'm Derived#bar");
};
var d = new Derived();
d.foo(); // "I'm Base#foo"
d.bar(); // "I'm Derived#bar"
...在ES5之前的环境中,您使用垫片/填充物Object.create
。但同样,我不直接这样做(并且不推荐它),我使用帮助脚本,因此它是声明性的和可重复的。
答案 1 :(得分:2)
.prototype.constructor
只是一个辅助属性,因此创建的对象可以轻松地使用this.constructor
引用构造函数,或者像i.constructor
一样。
将其任意设置为其他内容并没有任何效果,除了期望获取具有obj.constructor
的对象的构造函数的代码:
if( obj.constructor === Foo ) {
//It's a Foo
}
因此,留下它是一个很好的惯例。
es5shim依靠.constructor.prototype
来填充Object.getPrototypeOf