【问题标题】:prototype and constructor object properties原型和构造函数对象属性
【发布时间】:2010-10-07 04:09:08
【问题描述】:

我已经:

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 的原型了……

谁能详细解释一下原型链和构造函数属性

谢谢

【问题讨论】:

    标签: javascript prototype constructor


    【解决方案1】:

    constructor 是原型对象的常规属性(设置了DontEnum 标志,因此它不会出现在for..in 循环中)。如果您替换原型对象,constructor 属性也将被替换 - 有关详细信息,请参阅 this explanation

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

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


    Andrey Fedorov 提出了为什么new 不将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() {};
    

    这也使得多重继承变得微不足道。

    【讨论】:

      【解决方案2】:

      查看Tom Trenka's OOP woth ECMAscript,“继承”页面。 所有东西都继承自原型,包括constructor 属性。因此,我们必须自己解开它:

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

      【讨论】:

        【解决方案3】:

        短版:‘constructor’并没有按照你的想法做,并且不兼容跨浏览器。永远不要使用它。

        长版:Convention for prototype inheritance in JavaScript

        通常:您会因为 (a) 基于类和基于原型的 OO 之间的阻抗不匹配,以及 (b) JavaScript 对基于原型的 OO 的特别糟糕的解释感到陌生而感到困惑。

        如果您找到一个您喜欢的类原型实现并坚持下去,您可能会更开心。许多图书馆都有。这是我使用的任意一个:

        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);
        

        【讨论】:

          【解决方案4】:

          好吧,构造函数属性与任何其他属性一样,位于 Obj1 的原型(属性)上。如果您understand how prototypes work,这可能会有所帮助:

          >>> 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' 时,构造函数属性不只是设置在一个对象上。可能是有原因的,也可能只是疏忽。不管怎样,我倾向于避免它。

          【讨论】:

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