在以前的總結中,咱們詳細分析了原型《JS 總結之原型》,原型很大做用用於模擬繼承,這一次,咱們來聊原型繼承的幾種方式。git
以一個父類爲前提條件,列舉 js 繼承的繼承方式:es6
function Person (age) {
this.age = age || 18
}
Person.prototype.sleep = function () {
console.log('sleeping')
}
複製代碼
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: ƒ}
複製代碼
缺點:github
複製父類構造函數內的屬性瀏覽器
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
複製代碼
優勢:函數
缺點:ui
組合 原型鏈繼承 和 借用構造函數繼承。this
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 中最經常使用的繼承模式spa
缺點:調用了兩次父類構造函數prototype
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']
複製代碼
缺點:共享了屬性和方法設計
建立一個僅用於封裝繼承過程的函數,該函數在內部以某種形式來作加強對象,最後返回對象
function createObj (o) {
var clone = Object.create(o)
clone.sayName = function () {
console.log('hi')
}
return clone
}
複製代碼
缺點:跟借用構造函數模式同樣,每次建立對象都會建立一遍方法
子類構造函數複製父類的自身屬性和方法,子類原型只接受父類的原型屬性和方法:
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。開發人員廣泛認爲寄生組合式繼承是引用類型最理想的繼承範式。
// 父類
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 代碼。