2009-02-12 105 views
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; 
} 

但構造函數一直OBJ 2 .. 。 爲什麼? OBJ1已成爲OBJ 2原型...

有人能解釋我,詳細,原型鏈和constructor屬性

感謝

回答

25

constructor是原型對象的一個​​普通的屬性(與DontEnum標誌已設置,因此它不會出現在for..in循環中)。如果您替換原型對象,constructor屬性也將被替換 - 有關更多詳細信息,請參閱this explanation

您可以通過手動設置Obj2.prototype.constructor = Obj2來解決問題,但這種方式將不會設置DontEnum標誌。

由於這些問題,依靠constructor進行類型檢查並不是一個好主意:請使用instanceofisPrototypeOf()代替。


安德烈·費奧多羅夫問爲什麼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() {}; 

這使得多重繼承小事爲好。

9

檢出Tom Trenka's OOP woth ECMAscript,即「繼承」頁面。 從原型的所有東西都是繼承的,包括constructor屬性。因此,我們必須別讓它自己:

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

好,constructor屬性是像任何其他財產,在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去從[Obj1.prototype.constructor

一直到[[Prototype]]鏈上,我不知道爲什麼構造函數屬性不是隻設置在一個對象上當你使用'新'時。可能有一個原因,或者它可能只是一個疏忽。無論如何,我傾向於避免它。

+1

`constructor`之間的所有實例對象來自同一個構造函數創建了共享的屬性,因此,把它放在原型中是正確的;只是JS沒有內置的(深層)繼承層次支持 - 我會給我的答案添加一個解釋...... – Christoph 2009-02-12 14:10:29

3

簡短版本:'構造函數'不符合你的想法,也不是跨瀏覽器兼容的。切勿使用它。

龍版本:Convention for prototype inheritance in JavaScript

一般:你感到困惑,由於(一)間的阻抗不匹配基於類和基於原型的OO,和(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);