前段時間溫故了下JS OO之寫類方式,從這篇開始我們看看JS OO之繼承方式。
物件導向的語言多數都支援繼承,繼承最重要的優點就是代碼複用,從而構建大型軟體系統。如果一個類能夠重用另一個類的屬性和或方法,就稱之為繼承。從這個角度來看看JS的繼承方式。JS中繼承方式與寫類方式息息相關。不同的寫類方式造成不同的繼承方式。各種流行JS庫繼承方式也各不相同。從最簡單的複用開始。
1、建構函式方式寫類,通過方法調用複製父類屬性/欄位到子類 實現繼承
這裡父類,子類都採用建構函式方式寫,不用原型。子類調用父類函數來複製父類的屬性。
view sourceprint?01 /**
02 * 父類Polygon:多邊形
03 * @param {Object} sides
04 */
05 function Polygon(sides) {
06 this.sides = sides;
07 this.setSides = function(s) {this.sides=s;}
08 }
09
10 /**
11 * 子類Triangle:三角形
12 */
13 function Triangle() {
14 this.tempfun = Polygon;//父類引用賦值給子類的一個屬性tempfun
15 this.tempfun(3);//調用
16 delete this.tempfun;//刪除該屬性
17 this.getArea = function(){};
18 }
19
20 //new個對象
21 var tri = new Triangle();
22 console.log(tri.sides);//繼承的屬性
23 console.log(tri.setSides);//繼承的方法
24 console.log(tri.getArea);//自有的方法
25
26 //缺點是對於Triangle的執行個體對象用instanceof為父類Polygon時是false
27 console.log(tri instanceof Triangle);//true
28 console.log(tri instanceof Polygon);//false
因為 JavaScript中具名函數的多種調用方式 ,子類還可以有以下的多種實現方式。只是在子類中調用父類方法不同而已。
view sourceprint?01 function Triangle() {
02 Polygon.call(this,3); //call方式調用父類
03 this.getArea = function(){};
04 }
05 function Triangle() {
06 Polygon.apply(this,[3]); //apply方式調用父類
07 this.getArea = function(){};
08 }
09 function Triangle() {
10 var temp = new Polygon(3); //new方式調用父類
11 for(atr in temp) { //全部複製給子類
12 this[atr] = temp[atr];
13 }
14 this.getArea = function(){};
15 }
這種方式的缺點是子類的執行個體對象用instanceof檢查父類時總是false。這與java中繼承"is a "的關係是違背的。
2、原型方式寫類,原型方式繼承
core JS自身的對象系統就是採用原型方式(prototype based)繼承的。或者說core JS沒有採用常見的類繼承(class based)系統,而是使用原型繼承來實現自己的對象系統。工作中我們也可以用原型方式來實現繼承,代碼複用以構建自己的功能模組。
view sourceprint?01 /**
02 * 父類Polygon:多邊形
03 *
04 */
05 function Polygon() {}
06 Polygon.prototype.sides = 0;
07 Polygon.prototype.setSides = function(s) {this.sides=s;}
08
09 /**
10 * 子類Triangle:三角形
11 */
12 function Triangle() {}
13 Triangle.prototype = new Polygon(); //這是原型繼承關鍵的一句
14 Triangle.prototype.getArea = function(){}
15
16 //new個對象
17 var tri = new Triangle();
18 console.log(tri.sides);//繼承的屬性
19 console.log(tri.setSides);//繼承的方法
20 console.log(tri.getArea);//自有方法
21
22 //instanceof測試
23 console.log(tri instanceof Triangle);//true,表明該對象是三角形
24 console.log(tri instanceof Polygon);//true,表明三角形也是多邊形
雖然從輸出可以看出子類繼承了父類Polygon的屬性sides和方法setSides,但sides是0,怎麼會是三角形呢。還得調用下tri.setSides(3)使之成為三角形。這樣似乎很不方便。不能傳參數,即是原型方式的缺點。優點是正確的維護了"is a"的關係。
3、組合建構函式/原型方式寫類,採用前面種方式繼承
這種方式父類,子類的屬性都掛在建構函式裡,方法都掛在原型上。
view sourceprint?01 /**
02 * 父類Polygon:多邊形
03 */
04 function Polygon(sides) {
05 this.sides = sides;
06 }
07 Polygon.prototype.setSides = function(s) {this.sides=s;}
08
09 /**
10 * Triangle 三角形
11 * @param {Object} base 底
12 * @param {Object} height 高
13 */
14 function Triangle(base,height) {
15 Polygon.call(this,3);//複製父類屬性給自己
16 this.base = base;
17 this.height = height;
18 }
19 Triangle.prototype = new Polygon();//複製父類方法給自己
20
21 Triangle.prototype.getArea = function(){ //最後定義自己的方法
22 return this.base*this.height/2;
23 }
24
25 //new個對象
26 var tri = new Triangle(12,4);
27 console.log(tri.sides);//繼承的屬性
28 console.log(tri.setSides);//繼承的方法
29 console.log(tri.base);//自有屬性
30 console.log(tri.height);//自有屬性
31 console.log(tri.getArea);//自有方法
32
33 //instanceof測試,表明正確的維護了"is a"的關係
34 console.log(tri instanceof Triangle);//true,表明該對象是三角形
35 console.log(tri instanceof Polygon);//true,表明三角形也是多邊形