1

假设我们有这个构造函数:

var Foo = function(){
   this.x = "y";
}

FooFoo.prototype.constructor评估相同的功能,但Foo.prototype.constructor = function(){this.x="z"}似乎并没有改变new Foo(). 然而,它确实改变了结果

var i = new Foo();
i.constructor; // evals to function (){this.x = "z"}

这里发生了什么?我不打算它用于任何事情,我只是对这种语言感到好奇。

4

2 回答 2

3

constructor函数属性的属性prototype是指指向函数,以便您可以询问对象是什么构造了它。它是作为创建函数对象的一部分自动设置的(请参阅规范的第 13.2 节[或此处获取更新的版本]。)如您所见,您可以根据需要覆盖constructor属性Foo.prototype,改变它,但默认情况下这就是它的用途。多年来,JavaScript 规范只说该constructor属性将存在并具有给定的默认值(function Foo() { }这意味着默认情况下Foo.prototype.constructorFoo.)。但是从 ES2015 开始,情况发生了变化,规范中的各种操作现在实际上都使用了该constructor属性,例如这里这里这里这里

(请注意,以下内容是在class添加 ES2015 的功能之前编写的。以下是在 ES5 及更早版本中如何执行此操作。在 ES2015+ 中,如果您正在执行构造函数和继承层次结构,则没有充分的理由执行以下操作;只需使用class。[如果你不使用构造函数来构建继承层次结构——你不必这样做,在 JavaScript 中还有其他方法可以做到——你不会执行以下操作使用class.])

有一个很好的理由可以覆盖它,这与继承有关。假设您想要一个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
Object.defineProperty(Derived.prototype, "constructor", { // Fix up
    value: Derived,
    writable: true,
    configurable: true
});
...

(旁注:所有这些管道——以及围绕超级调用的复杂性——是 ES2015+ 具有class语法的原因。)


请注意,上述内容并不是设置继承层次结构的理想方式。这是您通常看到的,但正如我上面所说,并不理想。为了完整起见,在仅限于 ES5 语法的环境中,这样会更好:

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
Object.defineProperty(Derived.prototype, "constructor", {
    value: Derived,
    writable: true,
    configurable: true
});
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 之前的环境中,您将 shim/polyfill 用于Object.create. 但同样,我不直接这样做(也不推荐这样做),我使用帮助脚本,所以它是声明性的和可重复的。

于 2012-05-31T15:01:37.287 回答
2

.prototype.constructor只是一个辅助属性,以便创建的对象可以轻松地引用构造函数this.constructor,或者在您的情况下i.constructor

将其任意设置为其他内容不会产生任何影响,除非代码期望通过以下方式获取对象的构造函数obj.constructor

if( obj.constructor === Foo ) {
 //It's a Foo
}

因此,保留它是一个很好的约定。

es5shim依赖于shim.constructor.prototypeObject.getPrototypeOf

于 2012-05-31T15:01:50.060 回答