2014-12-04 240 views
1
定義構造函數

我讀這段代碼爲什麼我們需要在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.prototype.constructor = Rectangle; 

爲什麼我們不使用Rectangle.prototype =形狀。原型,Object.create()有什麼特別之處? 以及如果Rectangle.prototype.constructor = Rectangle;不叫?

+0

'爲什麼我們不使用Rectangle.prototype = Shape.prototype'矩形是一個形狀,但不是每個形狀都是矩形。改變Rectangle.prototype會改變Shape.prototype。如果有人想要使用它(和Chrome中的控制檯日誌記錄),構造函數會進行一致性修復。更多關於構造函數,原型,繼承,混合ins等在這裏:http://stackoverflow.com/a/16063711/1641941 – HMR 2014-12-05 02:40:27

回答

2

如果你做Rectangle.prototype = Shape.prototype那麼你將在Rectangle.prototoype上執行的任何修改將反映在Shape.prototype上,因爲它們是相同的對象。

爲了避免這種情況,我們使用Object.create(Shape.prototype)創建一個新對象,該對象的原型鏈接指向Shape.prototype

至於Rectangle.prototype.constructor = Rectangle;,這是爲了確保new Shape().constructor指向Rectangle而不是Shape

函數的原生prototype具有指向該函數的constructor屬性。

例如

function Rectangle() {} 

Rectangle.prototype.constructor === Rectangle; //true 

現在,當我們做Rectangle.prototype = ...,我們打破參考,並需要事後修復。

+0

嗨,謝謝你的回覆。是的..我總是困惑在什麼情況下變量是一個參考(如在這種情況下Rectangle.prototype),當它是一個副本。你對此有何看法?或者你能指點我一些好的文章嗎? – Blake 2014-12-04 22:39:19

+0

@Blake對象被引用,但原始值被複制(沒有選擇,因爲它們是不可變的)。 – plalx 2014-12-04 22:49:51

0

如果您只是將Rectangle.prototype設置爲Shape.prototype,那麼RectangleShape將與原型共享同一個對象。這意味着,您添加到Rectangle.prototype的所有內容也可在Shape.prototype上獲得 - Rectangle將不再繼承自Shape

Object.create(parent)創建一個從傳入的父對象繼承的子對象。這樣,父對象的所有屬性也可用於子對象,而子對象的屬性不會影響父對象。

要回答你的問題(「爲什麼我們需要在JavaScript中定義構造函數?」) - 我們不需要。您也可以使用普通對象:

// Shape - superclass 
function Shape() { 
    return { 
    x: 0, 
    y: 0, 
    // superclass method 
    move: function(x, y) { 
     this.x += x; 
     this.y += y; 
     console.info('Shape moved.'); 
    } 
    }; 
} 

// Rectangle - subclass 
function Rectangle() { 
    var rectangle = Shape(); 
    // rectangle-specific things could be added here 
    return rectangle; 
} 

var rect = Rectangle(); 

rect instanceof Rectangle; // false, instanceof does not work here 
rect instanceof Shape; // false 

rect.move(1, 1); // Outputs, 'Shape moved.' 
+0

你的非構造函數樣本不太純粹,而且很具誤導性。以下是你應該做的事情'var shape = {...}; var rectangle = Object.create(shape); //添加矩形東西var r1 = Object.create(rectangle);' – plalx 2014-12-04 22:17:24

+0

請原諒我編寫不太純的示例。我只是想證明我們不受JavaScript中「類的」繼承的束縛。純淨不是我最關心的問題。不過,我不同意這個例子有誤導性。 Shape()在每次調用時都會返回一個新形狀。通過'Object.create()'繼承它沒有任何好處。直接擴展該對象是完全可能的和安全的。 – 2014-12-04 22:22:18

+0

好吧,這是誤導性的,因爲有一個廣泛的命名約定,它說上層函數應該是構造函數,但即使你修正了這個問題,你的代碼在內存方面仍然效率低下,因爲你沒有通過原型分享任何東西。在執行方面也是低效的,例如,您將不得不每次創建新創建的形狀對象以創建矩形實例。所有這些都通過像我建議的那樣使用Object.create來解決,這是避免構造函數的標準方法。 – plalx 2014-12-04 22:48:34