1. 程式人生 > >JavaScript面向對象輕松入門之繼承(demo by ES5、ES6)

JavaScript面向對象輕松入門之繼承(demo by ES5、ES6)

element 增加 實現 原型 面向 pro es6 dog 關鍵字

  繼承是面向對象很重要的一個概念,分為接口繼承和實現繼承,接口繼承即為繼承某個對象的方法,實現繼承即為繼承某個對象的屬性。JavvaScript通過原型鏈來實現接口繼承、call()apply()來實現實現繼承。

  接口繼承的實現在ES5中是比較麻煩,在其它OOP語言中一個extends關鍵字就可以實現,但在ES5中要通過原型鏈去模擬,非常難理解,對初學者很不友好,並且有好幾種接口繼承的方式。本文為了對初學者更友好,並不打算讓讀者了解接口繼承的原理,而是直接把接口繼承實現方法封裝成一個函數,大家只要把這個函數拿過去用就可以了。

  相關概念:父類(超類)即為被繼承者,子類(派生類)即繼承者

接口繼承函數extend():

 1 function extend(subClass, superClass) {
 2     function o() {
 3         this.constructor = subClass;
 4     }
 5     o.prototype = superClass.prototype;
 6     subClass.prototype = new o();
 7     return subClass.prototype;
 8 }
 9 /*
10 subClass是子類,superClass是父類,extend函數讓subClass繼承superClass的原型方法,並返回subClass的原型;
11 這種繼承方式也是用的最多的,並且ES6的extends也是通過這種方式實現的,可以說是比較權威的用法; 12 */

ES5繼承DEMO:

 1 function Animal(shoutVoice,speed){
 2     this._shoutVoice = shoutVoice;//string
 3     this._speed = speed;//string
 4 }
 5 Animal.prototype.getSpeed = function(){
 6     return this._speed;
 7 };
 8 Animal.prototype.shout = function(){
9 console.log(this._shoutVoice); 10 }; 11 Animal.prototype.run = function(){ 12 console.log(嘿嘿,吃我灰吧!我的速度可是有+this._speed); 13 }; 14 15 function Dog(){ 16 Animal.call(this,汪汪汪!,10m/s); 17 //實現繼承:調用Animal的構造函數,繼承Animal類的屬性,第一個參數必須是this; 18 } 19 //接口繼承:extends函數讓Dog類繼承Animal類的的原型方法並返回Dog的新的原型prototype; 20 var DogP = extend(Dog,Animal); 21 /*可以繼續給的Dog類的prototype添加方法*/ 22 DogP.gnawBone = function() { 23 console.log(這是本狗最幸福的時候); 24 } 25 /*也可以把父類的方法覆蓋掉*/ 26 DogP.run = function(){ 27 console.log(這是Dog類上的run方法,不是Animal類的); 28 /*雖然覆蓋掉了,但實際上Animal類的run方法還在,也可以通過這種方式訪問父類的方法, 29 對原理有興趣的同學可以了解一下原型鏈*/ 30 Animal.prototype.run.call(this); 31 } 32 var dog = new Dog(); 33 console.log(dog.getSpeed());//log: ‘10m/s‘ 34 dog.shout();//log: ‘汪汪汪!‘ 35 dog.run(); 36 /*log: 37 ‘這是Dog類上的run方法,不是Animal類的‘ 38 ‘嘿嘿,吃我灰吧!我的速度可是有10m/s‘ 39 */ 40 dog.gnawBone();//log: ‘這是本狗最幸福的時候‘42 /*其它類繼承Animal類*/ 43 function Snake(){ 44 Animal.call(this,嘶!嘶!嘶!,5m/s); 45 } 46 var SnakeP = extend(Snake,Animal); 47 /*Dog類也可以繼續被繼承*/ 48 function PoodleDog(){ 49 Dog.call(this); 50 this._breed = poodle; 51 } 52 var PoodleDogP = extend(PoodleDog,Dog); 53 /*理論上講可以無限繼承下去,如瀏覽器DOM對象就繼承了很多個對象,組成了一個長長的原型鏈 54 如一個div標簽對象的類繼承順序: 55 HTMLDivElement<HTMLElement<Element<Node<EventTarget<Object 56 但我們的項目中最好別超過3次,否則就不太好控制了;*/

註意事項:

  *繼承的次數不應過多,否則子類一不小心就把父類的屬性方法給覆蓋了;
  *我們可以把繼承的對象作為成員屬性,即組合,盡量少用繼承,多用組合;
  *父類的屬性和方法最好別太多,過多也容易被子類覆蓋,可以抽象成一個對象來管理過多的屬性和方法。
  *繼承增加了耦合,所以父類封裝性一定要好,盡量降低與子類的耦合,
  *父類的設計要有前瞻性,具備一定的擴展能力,你也不希望今後修改父類的時候,再去修改所有的子類吧?

  *父類盡量只定義方法,不定義屬性,即構造函數最好是空函數;

ES6繼承DEMO:

  ES6實現繼承就方便很多了,由於TypeScript實現繼承和ES6差不多,所以這章就不貼出TypeScript的Demo了

 1 class Animal{
 2     constructor(shoutVoice,speed){
 3         this._shoutVoice = shoutVoice;//string
 4         this._speed = speed;//string
 5     }
 6     get speed(){
 7         return this._speed;
 8     }
 9     shout(){
10         console.log(this._shoutVoice);
11     }
12     run(){
13         console.log(‘嘿嘿,吃我灰吧!我的速度可是有‘+this._speed);
14     }
15 }
16 class Dog extends Animal{
17     constructor(){
18         super(‘汪汪汪!‘,‘10m/s‘);//相當於Animal.call(this,‘汪汪汪!‘,‘10m/s‘);
19     }
20     gnawBone() {
21         console.log(‘這是本狗最幸福的時候‘);
22     }
23     run(){
24         console.log(‘這是Dog類上的run方法,不是Animal類的‘);
25         super.run();//相當於Animal.prototype.run.call(this);
26     }
27 }
28 class PoodleDog extends Dog{
29     constructor(){
30         super();
31         this._breed = ‘poodle‘;
32     }
33     get breed(){
34         return this._breed;
35     }
36 }
37 let poodleDog = new PoodleDog();
38 console.log(poodleDog.breed);//log: ‘poodle‘
39 console.log(poodleDog.speed);//log: ‘10m/s‘
40 poodleDog.shout();//log: ‘汪汪汪!‘
41 poodleDog.run();
42 /*log:
43 ‘這是Dog類上的run方法,不是Animal類的‘
44 ‘嘿嘿,吃我灰吧!我的速度可是有10m/s‘
45 */
46 poodleDog.gnawBone();//log: ‘這是本狗最幸福的時候‘

後話

  js的繼承與其它OOP語言有一些不同的地方,所以最終還是要深刻的理解原型、原型鏈才能靈活運用,希望大家有時間一定要把這部分知識補上;

  如果你喜歡作者的文章,記得收藏,你的點贊是對作者最大的鼓勵;

  作者會盡量每周更新一章,下一章是講多態;

  大家有什麽疑問可以留言或私信作者,作者盡量第一時間回復大家;

  如果老司機們覺得那裏可以有不恰當的,或可以表達的更好的,歡迎指出來,我會盡快修正、完善。

JavaScript面向對象輕松入門之繼承(demo by ES5、ES6)