家都知道的,JavaScript這門語言在ES6出來之前是沒有類(class)這個概念的。
所以JavaScript中的類都是通過原型鏈來實現的。
既然能實現類,那同樣也就可以在JavaScript中實現面向對象的繼承了。(喜歡看書的朋友,可以去看一下《JavaScript高級程序設計》第三版6.3小節)。
我們使用繼承主要是為了能實現代碼的抽象和代碼的復用,在應用層實現功能的封裝。
在JavaScript中,繼承的實現比較復雜,坑也很多,什么屬性繼承、原型繼承、call/aplly繼承、原型鏈繼承、對象繼承、構造函數繼承、組合繼承、類繼承... 十幾種,看著都頭暈,對于小白來說,看多了都不知道用哪個好了。
而且每一種都細講需要花很多時間,所以今天這里大致梳理常用的兩種給大家學習,一起來看看吧!
JavaScript 中的繼承并不是明確規定的,而是通過模仿實現的。
今天我們來講兩種,一個是原型鏈實現繼承、第二個借用構造函數
先看一下父類
父類
實現方式:子類的原型指向父類的實例
子類:
代碼測試
測試
這種方式會存在的問題:
1.引用類型的對象會被子類的所有實例共享(問題1解決)
2.無法在創建子類的實例時,給父類的構造函數傳遞參數(問題2解決)
實現方式:在子類的構造函數中利用call(或者apply)方法執行父類構造函數(問題2解決),將執行對象設為子類的this,相當于把父類構造函數中的成員拷貝了一份到子類(問題1解決)
子類
子類
測試代碼如下:
測試代碼
存在的問題:
3.父類原型中定義的屬性無法被繼承
以上兩種方法都是有存在問題的,所以我們現在就出現了第三種方法來實現,你知道第三種方式是怎么實現的嗎?歡迎在評論區留言哦!
我們下次繼續聊繼承的其他方法。
其原理如下:構造函數使用 this 關鍵字給所有屬性和方法賦值(即采用類聲明的構造函數方式)。因為構造函數只是一個函數,所以可使 Parent 構造函數 成為 Children 的方法,然后調用它。Children 就會收到 Parent 的構造函數中定義的屬性和方法。例如,用下面的方式定義 Parent 和 Children:
原理:就是把 Parent 構造函數放到 Children 構造函數里面執行一次。那為什么不直接執行,非要轉個彎把 Parent 賦值給 Children 的 method 屬性再執行呢? 這跟 this 的指向有關,在函數內 this 是指向 window 的。當將 Parent 賦值給 Children 的 method 時, this 就指向了 Children 類的實例。
眾所周知,JavaScript 是一門基于原型的語言,在 JavaScript 中 prototype 對象的任何屬性和方法都被傳遞給那個類的所有實例。原型鏈利用這種功能來實現繼承機制:
注意:調用 Parent 的構造函數,沒有給它傳遞參數。這在原型鏈中是標準做法。要確保構造函數沒有任何參數。
這個方法是與對象冒充方法最相似的方法,因為它也是通過改變了 this 的指向而實現繼承:
apply 方法本人就不舉列了,它和 call 方法的區別在于它的第二個參數必須是數組。
對象冒充的主要問題是必須使用構造函數方式,這不是最好的選擇。不過如果使用原型鏈,就無法使用帶參數的構造函數了。如何選擇呢?答案很簡單,兩者都用。 在 JavaScript 中創建類的最好方式是用構造函數定義屬性,用原型定義方法。這種方式同樣適用于繼承機制:
Object.create 方法會使用指定的原型對象及其屬性去創建一個新的對象:
@ 當執行 Children.prototype=Object.create(Parent.prototype) 這個語句后,Children 的 constructor 就被改變為 Parent ,因此需要將 Children.prototype.constructor 重 新指定為 Children 自身。
這個是 ES6 的語法糖,下面看下es6實現繼承的方法:
上面代碼中,子類的constructor方法沒有調用super之前,就使用this關鍵字,結果報錯,而放在super方法之后就是正確的。子類Children的構造函數之中的super(),代表調用父類Parent的構造函數。這是必須的,否則 JavaScript 引擎會報錯。
注意,super雖然代表了父類Parent的構造函數,但是返回的是子類Children的實例,即super內部的this指的是Children,因此super()在這里相當于Parent.prototype.constructor.call(this)。
創: 前端二牛
借用構造函數繼承解決了原型鏈數據共享和無法向超類型傳遞參數的問題,但自身的缺陷是不能使用超類型原型中定義的方法。組合繼承是將原型鏈繼承和借用構造函數繼承組合到一起,從而發揮二者之長的一種繼承模式,也被稱作偽經典繼承。背后的思路是使用原型鏈實現原型屬性和方法的繼承,使用構造函數來實現實例屬性的繼承。這樣既通過在原型上定義方法實現了函數的復用,又能保證每個實例都有它自己的屬性。
下面來看一個例子:
在這個例子中首先聲明了一個 Human函數,然后使用原型模式定義了 sayName方法。聲明了 Person函數,通過構造函數模式繼承了 Human,但是這只能繼承 Human中聲明的屬性,也就是實例屬性,沒辦法繼承原型屬性和方法,于是緊接著將 Person的原型賦值為 Human的一個實例,通過原型鏈實現原型屬性和方法的繼承,然后添加了一個子類型特有的方法 sayName,這就是組合繼承。測試使用,結果既可以通過構造函數向超類型傳遞參數,也沒有 colors被共享的問題,同時還可以使用超類型原型上定義的方法 sayName,可以說非常完美。
組合繼承避免了原型鏈和借用構造函數的缺陷,融合了它們的優點,成為JavaScript中最常用的繼承模式。而且,因為使用了原型鏈, instanceOf和 isPrototypeOf也能夠識別基于組合繼承創建的對象。
*請認真填寫需求信息,我們會在24小時內與您取得聯系。