繼承是面向對象很重要的一個概念,分爲接口繼承和實現繼承,接口繼承即爲繼承某個對象的方法,實現繼承即爲繼承某個對象的屬性。JavvaScript經過原型鏈來實現接口繼承、call()或apply()來實現實現繼承。瀏覽器
接口繼承的實如今ES5中是比較麻煩,在其它OOP語言中一個extends關鍵字就能夠實現,但在ES5中要經過原型鏈去模擬,很是難理解,對初學者很不友好,而且有好幾種接口繼承的方式。本文爲了對初學者更友好,並不打算讓讀者瞭解接口繼承的原理,而是直接把接口繼承實現方法封裝成一個函數,你們只要把這個函數拿過去用就能夠了。app
相關概念:父類(超類)即爲被繼承者,子類(派生類)即繼承者函數
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 */
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次,不然就不太好控制了;*/
*繼承的次數不該過多,不然子類一不當心就把父類的屬性方法給覆蓋了;
*咱們能夠把繼承的對象做爲成員屬性,即組合,儘可能少用繼承,多用組合;
*父類的屬性和方法最好別太多,過多也容易被子類覆蓋,能夠抽象成一個對象來管理過多的屬性和方法。
*繼承增長了耦合,因此父類封裝性必定要好,儘可能下降與子類的耦合,
*父類的設計要有前瞻性,具有必定的擴展能力,你也不但願從此修改父類的時候,再去修改全部的子類吧?this
*父類儘可能只定義方法,不定義屬性,即構造函數最好是空函數;spa
ES6實現繼承就方便不少了,因爲TypeScript實現繼承和ES6差很少,因此這章就不貼出TypeScript的Demo了prototype
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語言有一些不一樣的地方,因此最終仍是要深入的理解原型、原型鏈才能靈活運用,但願你們有時間必定要把這部分知識補上;設計
若是你喜歡做者的文章,記得收藏,你的點贊是對做者最大的鼓勵;code
做者會盡可能每週更新一章,下一章是講多態;對象
你們有什麼疑問能夠留言或私信做者,做者儘可能第一時間回覆你們;blog
若是老司機們以爲那裏能夠有不恰當的,或能夠表達的更好的,歡迎指出來,我會盡快修正、完善。