ES6時代的來臨,使得類繼承變得如此的圓滑。可是,你有思考過ES6的類繼承模式嗎?如何去實現它呢?git
類繼承對於JavaScript來講,實現方式與Java等類語言大不相同。熟悉JavaScript的開發者都清楚,JavaScript是基於原型模式的。那麼,在es6沒有出來以前,js是如何繼承的呢?這是一個很是有意思的話題。但願帶着疑問看文章,或許對你的提高會更加巨大。若是你喜歡個人文章,歡迎評論,歡迎Star~。歡迎關注個人github博客es6
其實,js模擬一個類的方式很是的簡單——構造函數。或許,這是全部人都在廣泛使用的方式。咱們先來看一個例子:github
function Person(name){ this.name = name; } Person.prototype.sayName = function(){ console.log(this.name); } const person = new Person('zimo'); person.sayName(); //zimo
這裏經過構造函數模擬出來的類,其實和其餘語言的類行爲上是基本一致的,惟一的區別就是它不具有私有方法。並且,他們一樣是經過new操做符來進行實例化的,可是js的new操做與其它語言的new操做又會有所不一樣。比方說:app
const person = Person('zimo'); console.log(person) //undefined
構造函數前面沒有加new的狀況下,會致使這個對象沒有返回值來進行賦值。可是,在類語言中,在類名前不使用new是會報錯的。函數
下面,咱們應該進一步來看一下js的new操做符的原理,以及實現。工具
new方法原理:this
js代碼實現部分:spa
const person = new Person(args); //至關於 const person = Person.new(args); Function.prototype.new = function(){ let obj = new Object(); obj.__proto__ = this.prototype; const ret = this.apply(obj, arguments); return (typeof ret == 'object' && ret) || obj; }
到此爲止,js如何去模擬類,咱們已經講述完了。接下來,咱們應該看一下如何去實現相似與其餘語言的類繼承模式。prototype
儘管ES6已經對extends關鍵詞進行了實現,可是原理性的知識,咱們應該須要明白。code
先來看一個場景,不管是狗或者是貓,它們都有一個共同的類animal,如圖:
在真實開發中,咱們必須去實現類與類之間的繼承關係,否則的話,咱們就必須重複地去命名構造函數(這樣的方式是醜陋的)。
因此,像上述的場景,開發過程當中多的數不勝數,可是本質都是不變的。接下來,那咱們以一個例子來作說明,而且明白大體是如何去實現的。
例子:咱們須要去構造一個交通工具類,該類具有屬性:輪子、速度和顏色(默認爲黑),它還具有方法run(time)返回距離。以後,咱們還須要去經過該類繼承一個‘汽車’類和一個‘單車’類。
如圖:
實現:
function Vehicle(wheel, speed){ //首先構造一個交通工具類 this.wheel = wheel; this.speed = speed; this.color = 'black'; } Vehicle.prototype.run = function(time){ //在它的原型上定義方法 return this.speed * time; } function Car(wheel, speed, brand){ //經過在汽車類中去調用父類 Vehicle.call(this, wheel, speed); this.brand = brand; } Car.prototype = new Vehicle(); //將汽車類的原型指向交通工具的實例 function Bicycle(wheel, speed, owner){ //一樣,構造一個自行車類,在其中調用父類 Vehicle.call(this, wheel, speed); this.owner = owner; } Bicycle.prototype = new Vehicle(); //將其原型指向交通工具實例 const car = new Car(4, 10, 'baoma'); const bicycle = new Bicycle(2, 5, 'zimo'); console.log(car.run(10)); //100 console.log(bicycle.run(10)); //50
這樣子,就實現了類的繼承。
大體的思路是:在繼承類中調用父類,以及將繼承類的原型賦值爲父類的實例。
可是,每次實現若是都是這樣子的話,又會顯得很是的累贅,咱們並無將能夠重複使用的部分。所以,咱們須要將不變的部分進行封裝,封裝成一個方法,而後將可變的部分當中參數傳遞進來。
接下來,咱們來分析一下類繼承的封裝方法extend。
首先,咱們來看一下,咱們須要實現怎樣的繼承:
function Animal(name){ //構造一個動物類 this.name = name; } Animal.prototype.sayName = function(){ console.log('My name is ' + this.name); } /** extends方法其中包含子類的constructor、自身的屬性、和來自父元素繼承的屬性 */ var Dog = Animal.extends({ //使用extends方法來實現類的封裝 constructor: function(name, lan){ this._super(name); this.lan = lan }, sayname: function(){ this._super.sayName(); }, sayLan: function(){ console.log(this.lan); } }); var animal = new Animal('animal'); var dog = new Dog('dog', '汪汪汪'); animal.sayName(); //My name is animal dog.sayName(); // My name is dog dog.sayLan(); // '汪汪汪'
其中的extend方法是咱們須要去實現的,在實現以前,咱們能夠來對比一下ES6的語法
class Animal { constructor(name){ this.name = name; } sayName(){ console.log('My name is ' + this.name); } } /** 對比上面的extend封裝和es6的語法,咱們會發現,其實差別並無太大 */ class Dog extends Animal{ constructor(name, lan){ super(name); this.lan = lan; } sayName(){ super.sayName(); } sayLan(){ console.log(this.lan); } }
其實,不少地方是類似的,比方說super和this._super。這個對象實際上是看起來是父構造函數,由於他能夠直接調用this._super(name),但它同時還具有父構造函數原型上的函數,所以咱們能夠把它稱爲父包裝器。可是,必須保證的是_super中的函數對象上下文必須都是指向子構造函數的。
使用一張簡陋的圖來表示整個關係的話,如圖:
下面咱們來實現一下這個extend方法。
Function.prototype.extend = function(props){ var Super = this; var Temp = function(){}; Temp.prototype = Super.prototype; var superProto = new Temp(); //去建立一個指向Super.prototype的實例 var _super = function(){ //建立一個父類包裝器 return Super.apply(this, arguments); } var Child = function(){ if(props.constructor){ props.constructor.apply(this, arguments); } for(var i in Super.prototype){ _super[i] = Super.prototype[i].bind(this); //確保Super的原型方法拷貝過來時,this指向Child構造函數 } } Child.prototype = superProto; //將子類的原型指向父類的純實例 Child.prototype._super = _super; //構建一個引用指向父類的包裝器 for(var i in props){ if( i !== 'constructor'){ Child.prototype[i] = props[i]; //將props中方法放到Child的原型上面 } } return Child; }
繼承的一些內容就分析到這裏。其實,自從ES6標準出來以後,類的繼承已經很是廣泛了,由於真心好用。可是,也是愈來愈有人不懂得如何去理解這個繼承的原理了。其實ES6中的繼承的實現,也是挺簡單的。
若是你對我寫的有疑問,能夠評論,如我寫的有錯誤,歡迎指正。你喜歡個人博客,請給我關注Star~呦。你們一塊兒總結一塊兒進步。歡迎關注個人github博客