原型Prototype是一個設計模式,但是也可用來設計繼承,它有自己的簡單性,可以模擬出面向對象功能,但是在設計
復雜的繼承層次的時候比較蹩腳。
在JS中要實現復雜的面向對象模型,需要自己做大量的工作,先研究和檢驗JS的原型鏈和構造函數,原則上不需要構造函數和類也是可以OO的
1,定義一個類
/**
* 定義一個構造函數Plane,這個時候Plane的prototype指向瞭一個Object構造出來的對象,同時有一個constructor屬性指回Plane
* 這個構造函數在和new一起被執行的時候,會傳入一個由Object構造的幹凈對象作為this,當然還有其他參數,在真正執行代碼之前
* 就已經介紹構造函數的prototype給新對象讓他們關聯起來,然後執行構造函數中的代碼,一般做數據初始化而不放置方法
* @param x
* @param y
*/
var Plane = function(x, y) {
//檢驗是否設置原型true
alert(this.__proto__ == Plane.prototype);
this.x = x;
this.y = y;
};
//靜態字段
Plane.STATIC_FIELD = 2;
//靜態方法
Plane.staticFunction = function() {
alert(Plane.STATIC_FIELD);
}
//檢驗構造函數上的prototype的constructor是否等於構造函數 true
alert(Plane.prototype.constructor == Plane);
/**
* 在原型上設定一個方法,這樣每個實例都共享這些方法
*/
Plane.prototype.XY = function() {
alert(this.x * this.y);
};
//實例化對象
var planeObject = new Plane(2, 3);
//Object的protype上的原型鏈為空true
alert(Object.prototype.__proto__ == null);
//檢驗構造函數的prototype上的對象的原型是否是Object的prototype上的對象 true
alert(Plane.prototype.__proto__ == Object.prototype);
//檢驗實例對象的原型是否是構造函數的prototype true
alert(planeObject.__proto__ == Plane.prototype);
planeObject.XY();
Plane.staticFunction();
2,繼承這個類
/**
* 如果要實現繼承結構,超類的不變量需要維護,在JS中,如果隻是從Object繼承,不需要維護,因為Object中根本沒有屬性,當然除瞭那個constructor
* Java的Object也是隻有方法,沒有屬性的
* 現在如果我們要繼承Plane,首先構造對象的時候需要調用超類的構造函數構造超類那一部分
* 上面我們可以認為Plane是Object的子類,特點就是Plane的prototype是Object構造的,那麼要讓一個類成為Plane的子類,新類的prototype也需要是Plane構造的
* 問題來瞭,從Plane構造出來那個對象_proto_肯定指向Plane.prototype,但是可能會在對象上設置Plane初始的一些數據,而我們隻希望繼承行為,所以存在兩種方法:
* 1,手動delete掉
* 2,用一個幹凈函數做嫁接,我們的最終目的是:要一個新對象,它的_proto_指向Plane的prototype,它的constructor指向新構造函數,我們當然希望
直接將Plane的原型設置在_proto_上,但是js是不允許的,所以要通過函數構造的方式,第一種會留有垃圾數據,采用第二重把Plane的prototype取出來放到一個幹凈的函數上,用那個函數
構造一個對象,然後再對構造出來的對象設置constructor就完美瞭,這也是Ext的做法
*/
var Space = function(x, y, z) {
//用this調用超類構造函數
Plane.call(this, x, y);
//this.super(x, y);
this.z = z;
};
/**
* 要讓Space繼承Plane,需要讓Space的原型是Plane的實例,現在我們是在手動設置原型,這原本JS引擎做的事
*
*/
Space.prototype = new Plane();
//在子類的原型上放一個不變的屬性指向超類,這種方法,如果多於兩次繼承,當this.super走到超類構造函數,因為this.super始終指向這個超類,所以會無限遞歸
Space.prototype.super = Plane;
/**
* 我們隻希望繼承方法和不變量,所以要刪除超類自身的數據字段,雖然那些字段的值是undefined
*/
alert(Space.prototype.x); //undefined
delete Space.prototype.x;
delete Space.prototype.y;
/**
* 原型除瞭是個對象,還有個constructor指向它所在的函數,所以要設置這個值
*/
Space.prototype.constructor = Space;
/**
* 子類覆蓋父類進行裝飾
*/
Space.prototype.XY = function() {
alert("Before Invoke Super Method");
Plane.prototype.XY.call(this);
}
Space.prototype.XYZ = function() {
alert(this.x * this.y * this.z);
}
var spaceObject = new Space(2, 3, 4);
//調用覆蓋的方法
spaceObject.XY();
//調用自身的方法
spaceObject.XYZ();
3,使用幹凈函數做嫁接實現原型繼承
//一個幹凈函數做嫁接,第二種方法
var F = function() {
}
//嫁接過去
F.prototype = Plane.prototype;
Space.prototype = new F();
摘自 Adley