JS中的繼承(上)
學過java或許c#之類言語的同硯,應當會對js的繼承感到很疑心–不要問我怎樣曉得的,js的繼承主假如基於原型(prototype)的,對js的原型感興緻的同硯,
能夠相識一下我之前寫的
JS中的原型對象
置信許多同硯也跟我一樣,剛開始打仗js的面向對象編程的時刻,都抱着一種排擠的心態–為何js這麼貧苦?
實在相識完原型鏈后,再來看js的繼承,你會發明js的繼承實在比其他OOP言語更簡樸,更天真,我們來看一個基於原型鏈的繼承
// 父類
function Person() {}
// 子類
function Student(){}
// 繼承
Student.prototype = new Person()
我們只需把子類的prototype設置為父類的實例
,就完成了繼承,怎樣? 是否是超等簡樸? 有無比Java,C#的清楚?
事實上,以上就是js內里的原型鏈繼承
固然,經由過程以上代碼,我們的Student只是繼承了一個空殼的Person,如許視乎是毫無意義的,我們運用繼承的目標,
就是要經由過程繼承獵取父類的內容,那我們先給父類加上一點點簡樸的內容(新增的處所標記 ‘// 新增的代碼’):
// 父類
function Person(name,age) {
this.name = name || 'unknow' // 新增的代碼
this.age = age || 0 // 新增的代碼
}
// 子類
function Student(name){
this.name = name // 新增的代碼
this.score = 80 // 新增的代碼
}
// 繼承
Student.prototype = new Person()
運用
var stu = new Student('lucy')
console.log(stu.name) // lucy --子類掩蓋父類的屬性
console.log(stu.age) // 0 --父類的屬性
console.log(stu.score) // 80 --子類本身的屬性
這裏為了下降龐雜度,我們只演示了一般屬性的繼承,沒有演示要領的繼承,事實上,要領的繼承也很簡樸,
我們再來輕微修正一下代碼,基於上面的代碼,給父類和子類分別加一個要領(新增的處所標記 ‘// 新增的代碼’)
// 父類
function Person(name,age) {
this.name = name || 'unknow'
this.age = age || 0
}
// 為父類新曾一個要領
Person.prototype.say = function() { // 新增的代碼
console.log('I am a person')
}
// 子類
function Student(name){
this.name = name
this.score = 80
}
// 繼承 注重,繼承必需要寫在子類要領定義的前面
Student.prototype = new Person()
// 為子類新增一個要領(在繼承以後,不然會被掩蓋)
Student.prototype.study = function () { // 新增的代碼
console.log('I am studing')
}
運用
var stu = new Student('lucy')
console.log(stu.name) // lucy --子類掩蓋父類的屬性
console.log(stu.age) // 0 --父類的屬性
console.log(stu.score) // 80 --子類本身的屬性
stu.say() // I am a person --繼承自父類的要領
stu.study() // I am studing --子類本身的要領
如許,看起來我們彷佛已完成了一個完全的繼承了,這個就是原型鏈繼承,怎樣,是否是很好明白?
然則,原型鏈繼承有一個瑕玷,就是屬性假如是援用範例的話,會同享援用範例,請看以下代碼
// 父類
function Person() {
this.hobbies = ['music','reading']
}
// 子類
function Student(){}
// 繼承
Student.prototype = new Person()
運用
var stu1 = new Student()
var stu2 = new Student()
stu1.hobbies.push('basketball')
console.log(stu1.hobbies) // music,reading,basketball
console.log(stu2.hobbies) // music,reading,basketball
我們能夠看到,當我們轉變stu1的援用範例的屬性時,stu2對應的屬性,也會隨着變動,這就是原型鏈繼承瑕玷 –援用屬性會被一切實例同享,
那我們怎樣處理這個題目呢? 就是下面我們要提到的借用組織函數繼承,我們來看一下運用組織函數繼承的最簡樸例子:
// 父類
function Person() {
this.hobbies = ['music','reading']
}
// 子類
function Student(){
Person.call(this) // 新增的代碼
}
運用
var stu1 = new Student()
var stu2 = new Student()
stu1.hobbies.push('basketball')
console.log(stu1.hobbies) // music,reading,basketball
console.log(stu2.hobbies) // music,reading
如許,我們就處理了援用範例被一切實例同享的題目了
注重,這裏跟 原型鏈繼承 有個比較顯著的區別是並沒有運用prototype繼承,而是在子類內里實行父類的組織函數,
相當於把父類的代碼複製到子類內里實行一遍,如許做的另一個優點就是能夠給父類傳參
// 父類
function Person(name) {
this.name = name // 新增的代碼
}
// 子類
function Student(name){
Person.call(this,name) // 修改的代碼
}
運用
var stu1 = new Student('lucy')
var stu2 = new Student('lili')
console.log(stu1.name) // lucy
console.log(stu2.name) // lili
看起來已很有Java,C#的滋味了有無?
然則,組織函數處理了援用範例被一切實例同享的題目,但恰是由於處理了這個題目,致使一個很抵牾的題目湧現了,–函數也是援用範例,
也沒方法同享了.也就是說,每一個實例內里的函數,雖然功用一樣,然則卻不是同一個函數,就相當於我們每實例化一個子類,就複製了一遍的函數代碼
// 父類
function Person(name) {
this.say = function() {} // 修改的代碼
}
// 子類
function Student(name){
Person.call(this,name)
}
運用
var stu1 = new Student('lucy')
var stu2 = new Student('lili')
console.log(stu1.say === stu2.say) // false
以上代碼能夠證實,父類的函數,在子類的實例下是不同享的
總結
繼承體式格局 | 繼承中心代碼 | 優瑕玷 |
---|---|---|
原型鏈繼承 | Student.prototype = new Person() | 實例的援用範例同享 |
組織函數繼承 | 在子類(Student)里實行 Person.call(this) | 實例的援用範例不同享 |
從上表我們能夠看出 原型鏈繼承
和 組織函數繼承
這兩種繼承體式格局的優瑕玷剛好是互相抵牾的,那末我們有無方法魚和熊掌兼得呢?
沒有的話,我就不會說出來了,^_^,接下來請許可我盛大引見 組合繼承
組合繼承,就是各取上面2種繼承的優點,一般屬性 運用 組織函數繼承
,函數 運用 原型鏈繼承
,
這個代碼輕微龐雜一點,不過置信有了上面的基本后,看起來也是很輕鬆
// 父類
function Person() {
this.hobbies = ['music','reading']
}
// 父類函數
Person.prototype.say = function() {console.log('I am a person')}
// 子類
function Student(){
Person.call(this) // 組織函數繼承(繼承屬性)
}
// 繼承
Student.prototype = new Person() // 原型鏈繼承(繼承要領)
運用
// 實例化
var stu1 = new Student()
var stu2 = new Student()
stu1.hobbies.push('basketball')
console.log(stu1.hobbies) // music,reading,basketball
console.log(stu2.hobbies) // music,reading
console.log(stu1.say == stu2.say) // true
如許,我們就既能完成屬性的自力,又能做到函數的同享,是否是很圓滿呢?
組合繼承據說是JavaScript中最經常使用的繼承體式格局(詳細沒法考據哈).
至此,我們就把js內里的經常使用繼承相識完了,實在也沒有那末難嘛!不過,我們總結一下3種繼承
- 原型鏈繼承,會同享援用屬性
- 組織函數繼承,會獨享一切屬性,包含援用屬性(重點是函數)
- 組合繼承,應用原型鏈繼承要同享的屬性,應用組織函數繼承要獨享的屬性,完成相對圓滿的繼承
上面為何要說相對圓滿呢? 由於本文的題目叫【JS中的繼承(上)】,那肯定是另有
【JS中的繼承(下)】.md)咯,
目前為止,我們只講了3種最基本的繼承,事實上,JavaScript另有很多繼承體式格局,為了讓你不至於進修委靡,所以我盤算離開來說,
假如你沒有誰人耐煩繼承看下去,那末看完這篇關於明白JavaScript的繼承,也是夠用的。然則發起多看兩遍,加深印象,
我學js繼承的時刻,那本犀牛書都被我翻爛了,寫這篇筆墨的時刻,我還在一遍翻一邊寫的呢(噓!)
好了,本日就到這裏,謝謝收看,假如以為對您有效,請給本文的github加個star,萬分謝謝,
別的,github上另有其他一些關於前端的教程和組件,有興緻的童鞋能夠看看,你們的支撐就是我最大的動力。