原型和构造函数对象属性

10

我已经:

function Obj1(param)
{
    this.test1 = param || 1;

}

function Obj2(param, par)
{
    this.test2 = param;

}

现在当我执行:

Obj2.prototype = new Obj1(44);
var obj = new Obj2(55);

alert(obj.constructor) 

我有:

function Obj1(param) {
    this.test1 = param || 1;
}

但构造函数已经是 Obj2 了...为什么?因为Obj1已经成为了Obj2的原型...

有人能详细解释一下原型链和构造函数属性吗?

谢谢

4个回答

25
constructor是原型对象的常规属性(带有DontEnum标志,因此不会在for..in循环中出现)。如果您替换原型对象,则constructor属性也将被替换 - 请参见此解释获取更多详细信息。

您可以通过手动设置Obj2.prototype.constructor = Obj2来解决此问题,但这种方式不会设置DontEnum标志。

由于这些问题,依赖constructor进行类型检查并不是一个好主意:而应该使用instanceofisPrototypeOf()


Andrey Fedorov提出了为什么new不会将constructor属性分配给实例对象的问题。我想这样做的原因是:

从同一构造函数创建的所有对象共享constructor属性,共享属性驻留在原型中。

真正的问题在于JavaScript没有内置支持继承层次结构。有几种解决方法(您的解决方法是其中之一),另一种更符合JavaScript精神的方法如下:

function addOwnProperties(obj /*, ...*/) {
    for(var i = 1; i < arguments.length; ++i) {
        var current = arguments[i];

        for(var prop in current) {
            if(current.hasOwnProperty(prop))
                obj[prop] = current[prop];
        }
    }
}

function Obj1(arg1) {
    this.prop1 = arg1 || 1;
}

Obj1.prototype.method1 = function() {};

function Obj2(arg1, arg2) {
    Obj1.call(this, arg1);
    this.test2 = arg2 || 2;
}

addOwnProperties(Obj2.prototype, Obj1.prototype);

Obj2.prototype.method2 = function() {};

这也使得多重继承变得轻而易举。


9

查看Tom Trenka的ECMAscript OOP,"继承"页面。所有从原型中继承的内容,包括constructor属性,都需要自己修复。

Obj2.prototype = new Obj1(42);
Obj2.prototype.constructor = Obj2;

3
短版:'constructor'不是您想要的东西,并且不跨浏览器兼容。永远不要使用它。
长版:JavaScript原型继承的约定 总体而言,您可能会感到困惑,因为(a)基于类和基于原型的面向对象之间存在阻抗匹配,以及(b)JavaScript对基于原型的面向对象的理解相当差。
如果您找到一个类在原型中的实现并坚持使用它,您可能会更加满意。许多库都有一个。这是我使用的任意一个:
Function.prototype.subclass= function() {
    var c= new Function(
        'if (!(this instanceof arguments.callee)) throw(\'Constructor called without "new"\'); '+
        'if (arguments[0]!==Function.prototype.subclass.FLAG && this._init) this._init.apply(this, arguments); '
    );
    if (this!==Object)
        c.prototype= new this(Function.prototype.subclass.FLAG);
    return c;
}
Function.prototype.subclass.FLAG= new Object();

以下是如何使用它的示例:

// make a new class
var Employee= Object.subclass();

// add members to it
Employee.prototype._LEGS= 2;
Employee.prototype.getLegs= function() {
    return this._LEGS;
};

// optional initialiser, takes arguments from constructor
Employee.prototype._init= function(name) {
    this.name= name;
};

// make a subclass
Manager= Employee.subclass();

// extend subclass method
Manager.prototype._init= function(name, importance) {
    // call base class's method
    Employee.prototype._init.call(this, name);
    this.importance= importance;
}

// all managers are well-known to have three legs
Manager.prototype._LEGS= 3;

// create one
var jake= new Manager('Jake the Peg', 100);

1

好的,构造函数属性是Obj1原型(属性)上像其他属性一样的属性。如果你了解原型的工作原理,这可能会有所帮助:

>>> obj.hasOwnProperty("constructor")
false

// obj's [[Prototype]] is Obj2.prototype
>>> Obj2.prototype.hasOwnProperty("constructor")
false

// Obj2.prototype's [[Prototype]] is Obj1.prototype
>>> Obj1.prototype.hasOwnProperty("constructor")
true

// Oh?
>>> Obj1.prototype.constructor
Obj1()

啊哈!所以 obj 没有构造函数,JS 会沿着 [[Prototype]] 链去获取它,一直到 Obj1.prototype.constructor。

我不确定为什么当你使用 `new' 时,构造函数属性不会直接设置在对象上。可能有原因,也可能只是一个疏忽。无论如何,我倾向于避免使用它。


1
“constructor”是一个属性,它在从同一个构造函数创建的所有实例对象之间共享,因此,将其放在原型中是正确的做法;只是JS没有内置对(深度)继承层次结构的支持 - 我会在我的答案中添加一个解释... - Christoph

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接