2014-01-17 47 views
3

我正在學習在JavaScript基於原型的OOP最近。閱讀大量的網站解釋JavaScript的原型機制後,我從Mozilla的落戶與Mozilla如果我在JavaScript中創建子類時沒有正確設置構造函數,會出現什麼問題?

下面這種方法是一些片段:

// Shape - superclass 
function Shape() { 
    this.x = 0; 
    this.y = 0; 
} 

// superclass method 
Shape.prototype.move = function(x, y) { 
    this.x += x; 
    this.y += y; 
    console.info("Shape moved."); 
}; 

// Rectangle - subclass 
function Rectangle() { 
    Shape.call(this); // call super constructor. 
} 

// subclass extends superclass 
Rectangle.prototype = Object.create(Shape.prototype); 
Rectangle.prototype.constructor = Rectangle; 

var rect = new Rectangle(); 

rect instanceof Rectangle // true. 
rect instanceof Shape // true. 

rect.move(1, 1); // Outputs, "Shape moved." 

我明白Rectangle.prototype = Object.create(Shape.prototype);將設置原型鏈正常使Rectangle的實例將繼承Shape的方法。

Rectangle.prototype.constructor = Rectangle;線是有點混亂。

this website我知道,當我們創建一個像function MyClass(){}一個新的對象,JavaScript就其原型設置爲空對象和MyClass.prototype.constructor將指向回MyClass。所以Rectangle.prototype.constructor = Rectangle;將修復斷開的鏈接,因爲Object.create(Shape.prototype).constructor仍指向Shape

但如果我刪除此行,我仍然會得到沒有問題運行下面的代碼。如果有構造函數屬性的用例,我只是好奇嗎?如果我把它指向超類,會出現什麼問題?

回答

3

設置屬性這種方式將使其枚舉(一個用於聲明將列出constructor屬性)。

var rec = new Rectangle(); 
'constructor' in rec; //true 

您應該使用Object.defineProperty或通過在第二個參數的Object.create。

Rectangle.prototype = Object.create(Shape.prototype, {constructor: {enumerable: false }}); 

var rec = new Rectangle(); 
'constructor' in rec; // 

請參閱本主題更多關於constructor屬性:

https://stackoverflow.com/a/4013295

+0

謝謝你的enumerable部分。這是我從來不知道的事情。我曾嘗試在有或沒有設置構造函數的情況下運行'''rect instanceof Rectangle'''。他們都返回''''''''。你能指出一點點差別嗎? – adieu

+0

對不起@adieu,我錯了'instanceof'操作符。構造函數屬性實際上與'instanceof'無關' –

+0

請注意,當您正確地對其進行多邊形填充時,將第二個參數傳遞給Object.create應該會導致舊版瀏覽器發生錯誤。換一種說法;沒有辦法將第二個參數polyfil Object.create。 – HMR

-1

}這種將如果對象是在繼承鏈指定對象類型的返回true。 它與構造函數屬性無關。

我們可以使用constructor屬性,如果我們想初始化財產或在創建對象的時候執行默認代碼。

例如,在你的例子,如果我們要始終紅色創建矩形,我們可以按如下方式使用長方形的構造函數:

function Rectangle() { 
    Shape.call(this); // call super constructor. 
    this.color = "red"; 
} 

希望這會清除您的查詢。

2

在你的代碼,它沒有任何區別,因爲你仍然創建使用Rectangle構造函數(顯式地使用它的名字:var rect = new Rectangle();)你的對象。

的差異只在情況下,當你失去了參照對象的構造函數,或者你根本不知道它是什麼:

// Shape - superclass 
function Shape() { 
    this.x = 1; 
    this.y = 1; 
} 

// superclass method 
Shape.prototype.move = function(x, y) { 
    this.x += x; 
    this.y += y; 
    console.info(this.x + " " + this.y); 
}; 

// Rectangle - subclass 
function Rectangle() { 
    Shape.call(this); // call super constructor. 
} 

// subclass extends superclass 
Rectangle.prototype = Object.create(Shape.prototype); 
//Rectangle.prototype.constructor = Rectangle; 

console.info("RECT"); 
var rect = new Rectangle; 
console.info(rect); 
console.info(rect instanceof Rectangle); 
console.info(rect instanceof Shape); 
//console.info(rect instanceof Square); TypeError - Square must be a function, but is still not initialized 
rect.move(1, 1); 

console.info("RECT2"); 
var rect2 = new rect.constructor(); 
console.info(rect2); 
console.info(rect2 instanceof Rectangle); 
console.info(rect2 instanceof Shape); 
// console.info(rect2 instanceof Square); TypeError - Square must be a function, but is still not initialized 
rect2.move(1, 1); 

console.info("RECT3"); 
var Square = rect.constructor; 
Square.prototype.move = function(x, y) { // just to make it different 
    this.x *= x; 
    this.y *= y; 
    console.info(this.x + " " + this.y); 
} 

var rect3 = new Square(); 
console.info(rect3); 
console.info(rect3 instanceof Rectangle); 
console.info(rect3 instanceof Shape); 
console.info(rect3 instanceof Square); 
rect3.move(4, 4); 

我註釋掉符合設置構造函數。取消註釋以查看輸出的差異。我還添加了另一種使用構造函數屬性的方法 - 另一個繼承示例(Square基於Shape,但與move方法不同)。

所以如果你不修復這個鏈接,在我展示的情況下,你將使用Shape構造函數,並且創建相同類型的對象將變得根本不可能。 rect屬於這兩種類型,但rect2已不在。

因此,假設即使您不需要它,將此鏈接修復爲正確的構造函數也是一種非常好的做法,因爲將來有人在使用您的代碼時可能需要它。

編輯:

constructor屬性不會影響在object本身什麼。它的設置一旦創建object以將reference保存到創建objectfunction。如果你將來需要它,那麼有一個正確的方法是有意義的。我沒有看到任何其他問題。

+0

謝謝。你已經指出了構造函數屬性的一個可能的用例。我們可以編寫一個接受任何對象作爲輸入的函數,並創建一個相同類型的新對象。在這種情況下,我們將依賴構造函數屬性,因爲我們不知道輸入對象的類型。並修復鏈接是有道理的。只是想知道是否有更多的用例。 – adieu

+0

@adieu我添加了一些註釋,並修改了代碼,提供了'constructor'屬性的實用用法的更多示例。想不到別的... – Karol

相關問題