JS中的繼續(上)

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種繼承

  1. 原型鏈繼承,會同享援用屬性
  2. 組織函數繼承,會獨享一切屬性,包含援用屬性(重點是函數)
  3. 組合繼承,應用原型鏈繼承要同享的屬性,應用組織函數繼承要獨享的屬性,完成相對圓滿的繼承

上面為何要說相對圓滿呢? 由於本文的題目叫【JS中的繼承(上)】,那肯定是另有
【JS中的繼承(下)】.md)咯,
目前為止,我們只講了3種最基本的繼承,事實上,JavaScript另有很多繼承體式格局,為了讓你不至於進修委靡,所以我盤算離開來說,
假如你沒有誰人耐煩繼承看下去,那末看完這篇關於明白JavaScript的繼承,也是夠用的。然則發起多看兩遍,加深印象,
我學js繼承的時刻,那本犀牛書都被我翻爛了,寫這篇筆墨的時刻,我還在一遍翻一邊寫的呢(噓!)

好了,本日就到這裏,謝謝收看,假如以為對您有效,請給本文的github加個star,萬分謝謝,
別的,github上另有其他一些關於前端的教程和組件,有興緻的童鞋能夠看看,你們的支撐就是我最大的動力。

    原文作者:noahlam
    原文地址: https://segmentfault.com/a/1190000014476341
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞