ES5如何實現繼承


一.原型鏈繼承

原型鏈繼承的原理很簡單,直接讓子類的原型對象指向父類實例,當子類實例找不到對應的屬性和方法時,就會往它的原型對象,也就是父類實例上找,從而實現對父類的屬性和方法的繼承

function Prant() {
   this.name = 'hello'
}
Prant.prototype.getName = function() {
   return this.name;
}
function child() {} // 子類
//讓子類的原型對象指向父類實例, 這樣一來在Child實例中找不到的屬性和方法就會到原型對象(父類實例)上尋找
child.prototype = new Prant();
// 根據原型鏈的規則,順便綁定一下constructor, 這一步不影響繼承, 只是在用到constructor時會需要
child.prototype.constructor = child;
const alias = new child();
alias.name //hello
alias.getName();  //hello

缺點:

  • 由於所有Child實例原型都指向同一個Parent實例, 因此對某個Child實例的父類引用類型變量修改會影響所有的Child實例
  • 在創建子類實例時無法向父類構造傳參, 即沒有實現super()的功能

二.構造函數繼承

構造函數繼承,即在子類的構造函數中執行父類的構造函數,並為其綁定子類的this,讓父類的構造函數把成員屬性和方法都掛到子類的this上去,這樣既能避免實例之間共享一個原型實例,又能向父類構造方法傳參

function Parent(name) {
    this.name = [name]
}
Parent.prototype.getName = function() {
     return this.name
}
function Child() { // 子類
    Parent.call(this, 'hello')
}
const child1 = new Child();
const child2 = new Child();
child1.name[0] = '構造函數繼承';
console.log(child1.name) // 構造函數繼承
console.log(child2.name) //hello
child2.getName(); // 找不到,報錯

缺點:

  • 繼承不到父類原型上的屬性和方法

三.組合式繼承

將以上兩者組合起來使用

function Parent(name) {
    this.name = [name]
}
Parent.prototype.getName = function() {
    return this.name
}
function Child() {
    // 構造函數繼承
    Parent.call(this, 'zhangsan') 
}
//原型鏈繼承
Child.prototype = new Parent()
Child.prototype.constructor = Child

//測試
const child1 = new Child()
const child2 = new Child()
child1.name[0] = 'hello'
console.log(child1.name)          // ['hello']
console.log(child2.name)          // ['zhangsan']
child2.getName()                  // ['zhangsan']

缺點:

  • 每次創建子類實例都執行了兩次構造函數(Parent.call()new Parent()),雖然這並不影響對父類的繼承,但子類創建實例時,原型中會存在兩份相同的屬性和方法,這並不優雅

四.寄生式組合繼承

解決構造函數被執行兩次的問題, 我們將指向父類實例改為指向父類原型, 減去一次構造函數的執行

function Parent(name) {
    this.name = [name]
}
Parent.prototype.getName = function() {
    return this.name
}
function Child() {
    // 構造函數繼承
    Parent.call(this, 'zhangsan') 
}
//原型鏈繼承
// Child.prototype = new Parent()
Child.prototype = Object.create(Parent.prototype)  //將`指向父類實例`改為`指向父類原型`
Child.prototype.constructor = Child

//測試
const child = new Child()
const parent = new Parent()
child.getName()                  // ['zhangsan']
parent.getName()                 // 報錯, 找不到getName()

目前最成熟的繼承方式,js高級體現。

回顧:

說到js繼承,最開始想到的應該是是 原型鏈繼承,通過把子類實例的原型指向父類實例來繼承父類的屬性和方法,但原型鏈繼承的缺陷在於 對子類實例繼承的引用類型的修改會影響到所有的實例對象以及 無法向父類的構造方法傳參
構造函數繼承, 通過在子類構造函數中調用父類構造函數並傳入子類this來獲取父類的屬性和方法,但構造函數繼承也存在缺陷,構造函數繼承 不能繼承到父類原型鏈上的屬性和方法
后面有了組合式繼承,但也有了新的問題,每次都會執行兩次父類的構造方法,最終有了寄生式組合式繼承。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM