JS 總結之原型繼承方式收錄

圖片描述

以一個父類爲前提條件,列舉 js 繼承的繼承方式:

function Person (age) {
  this.age = age || 18
}
Person.prototype.sleep = function () {
  console.log('sleeping')
}

🍖 方式 1:原型鏈繼承(不推薦)

function Programmer() {}

Programmer.prototype = new Person ()
Programmer.prototype.code = function () {
  console.log('coding')
}

let jon = new Programmer()
jon.code() // coding
jon.sleep() // sleeping

jon instanceof Person // true
jon instanceof Programmer // true

Object.getPrototypeOf(jon) // Person {age: 18, code: ƒ}
jon.__proto__ // Person {age: 18, code: ƒ}

缺點:

  1. 無法向父類構造函數傳參
  2. 父類的所有屬性被共享,只要一個實例修改了屬性,其他所有的子類實例都會被影響

🌭 方式 2:借用構造函數(經典繼承)(不推薦)

複製父類構造函數內的屬性

function Programmer(name) {
  Person.call(this)
  this.name = name
}
let jon = new Programmer('jon')
jon.name // jon
jon.age // 18

jon.sleep() // Uncaught TypeError: jon.sleep is not a function
jon instanceof Person // false
jon instanceof Programmer // true

優點:

  1. 可以爲父類傳參
  2. 避免了共享屬性

缺點:

  1. 只是子類的實例,不是父類的實例
  2. 方法都在構造函數中定義,每次創建實例都會創建一遍方法

🍤 方式 3:組合繼承(推薦)

組合 原型鏈繼承借用構造函數繼承

function Programmer(age, name) {
  Person.call(this, age)
  this.name = name
}

Programmer.prototype = new Person()
Programmer.prototype.constructor = Programmer // 修復構造函數指向

let jon = new Programmer(18, 'jon')
jon.age // 18
jon.name // jon

let flash = new Programmer(22, 'flash')
flash.age // 22
flash.name // flash

jon.age // 18

jon instanceof Person // true
jon instanceof Programmer // true
flash instanceof Person // true
flash instanceof Programmer // true

優點:融合原型鏈繼承和構造函數的優點,是 JavaScript 中最常用的繼承模式

缺點:調用了兩次父類構造函數

🌮 方式 4:原型式繼承(不推薦)

function create(o) {
  function F() {}
  F.prototype = o
  return new F()
}

let obj = {
  gift: ['a', 'b']
}

let jon = create(obj)
let xiaoming = create(obj)

jon.gift.push('c')
xiaoming.gift // ['a', 'b', 'c']

缺點:共享了屬性和方法

🍳 方式 5:寄生式繼承(不推薦)

創建一個僅用於封裝繼承過程的函數,該函數在內部以某種形式來做增強對象,最後返回對象

function createObj (o) {
  var clone = Object.create(o)
  clone.sayName = function () {
    console.log('hi')
  }
  return clone
}

缺點:跟借用構造函數模式一樣,每次創建對象都會創建一遍方法

🍧 方式 6:寄生組合繼承(最佳)

子類構造函數複製父類的自身屬性和方法,子類原型只接受父類的原型屬性和方法:

function create(prototype) {
  function Super() {}
  Super.prototype = prototype
  return new Super()
}

function Programmer(age, name) {
  Person.call(this, age)
  this.name = name
}

Programmer.prototype = create(Person.prototype)
Programmer.prototype.constructor = Programmer // 修復構造函數指向

let jon = new Programmer(18, 'jon')
jon.name // jon

進階封裝:


function create(prototype) {
  function Super() {}
  Super.prototype = prototype
  return new Super()
}

function prototype(child, parent) {
  let prototype = create(parent.prototype)
  prototype.constructor = child // 修復構造函數指向
  child.prototype = prototype
}

function Person (age) {
  this.age = age || 18
}
Person.prototype.sleep = function () {
  console.log('sleeping')
}

function Programmer(age, name) {
  Person.call(this, age)
  this.name = name
}

prototype(Programmer, Person)

let jon = new Programmer(18, 'jon')
jon.name // jon

引用《JavaScript 高級程序設計》中對寄生組合式繼承的誇讚就是:

這種方式的高效率體現它只調用了一次 Parent 構造函數,並且因此避免了在 Parent.prototype 上面創建不必要的、多餘的屬性。與此同時,原型鏈還能保持不變;因此,還能夠正常使用 instanceof 和 isPrototypeOf。開發人員普遍認爲寄生組合式繼承是引用類型最理想的繼承範式。

🍜 方式 7:ES6 extends(最佳)

// 父類
class Person {
  constructor(age) {
    this.age = age
  }
  sleep () {
    console.log('sleeping')
  }
}

// 子類
class Programmer extends Person {
  constructor(age, name) {
    super(age)
    this.name = name
  }
  code () {
    console.log('coding')
  }
}

let jon = new Programmer(18, 'jon')
jon.name // jon
jon.age // 18

let flash = new Programmer(22, 'flash')
flash.age // 22
flash.name // flash

jon instanceof Person // true
jon instanceof Programmer // true
flash instanceof Person // true
flash instanceof Programmer // true

優點:不用手動設置原型。

缺點:新語法,只要部分瀏覽器支持,需要轉爲 ES5 代碼。

🚀 參考

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章