簡述
開始前先定一個父類用于繼承
function Persion(name){
this.name = name || 'Persion';
this.opt = {
a: 1,
b: 2
};
this.eat = function(){
console.log(this.name + '..eating..')
}
}
Persion.prototype.getName = function(){
return this.name;
}
1. 原型鏈繼承
核心:將父類的實例作為子類的原型
function Author() {}
Author.prototype = new Persion();
Author.prototype.constructor = Author;
var xxy = new Author()
console.log(xxy.name) // Persion
xxy.eat() // Persion eating
console.log(xxy.getName()) // Persion
console.log(xxy instanceof Persion); // true
console.log(xxy instanceof Author); // true
優(yōu)點:
- 實例是子類的實例榕暇,也是父類的實例
- 父類新增原型方法/原型屬性筋帖,子類都能訪問到
缺點:
- 無法實現(xiàn)多繼承
- 來自原型對象的引用屬性是所有實例共享的
var db = new Author()
xxy.opt.a = 22
console.log(xxy.opt.a) // 22
console.log(db.opt) //22
- 創(chuàng)建子類實例時,無法向父類構(gòu)造函數(shù)傳參
2. 構(gòu)造繼承
核心:使用父類的構(gòu)造函數(shù)來增強子類實例涡相,等于復(fù)制父類的實例屬性給子類
function Author(){
Persion.call(this, name);
}
var xxy = new Author()
console.log(xxy instanceof Persion); // false
console.log(xxy instanceof Author); // true
優(yōu)點:
- 解決了原型鏈繼承的缺點2
- 創(chuàng)建子類實例時斜做,可以向父類傳遞參數(shù)
- 可以實現(xiàn)多繼承(call多個父類對象)
缺點:
- 實例并不是父類的實例疤祭,只是子類的實例
- 只能繼承父類的實例屬性和方法,不能繼承原型屬性/方法
- 無法實現(xiàn)函數(shù)復(fù)用茵瘾,每個子類都有父類實例函數(shù)的副本礼华,影響性能
3. 實例繼承
核心:為父類實例添加新特性,作為子類實例返回
function Author(name){
var instance = new Persion(name)
return instance
}
var xxy = new Author()
console.log(xxy instanceof Persion); // true
console.log(xxy instanceof Author); // false
優(yōu)點:
- 不限調(diào)用方式
缺點:
- 不支持多繼承
- 實例是父類的實例拗秘,不是子類的實例
4. 拷貝繼承
function Author(name){
var instance = new Persion(name)
for (var p in instance) {
Author.prototype[p] = instance[p];
}
}
var xxy = new Author('XXY')
console.log(xxy instanceof Persion); // true
console.log(xxy instanceof Author); // false
優(yōu)點:
- 支持多繼承
缺點:
- 效率較低圣絮,內(nèi)存占用高(因為要拷貝父類的屬性)
- 無法獲取父類不可枚舉的方法(不可枚舉方法,不能使用for in 訪問到)
5. 組合繼承
核心:通過調(diào)用父類構(gòu)造雕旨,繼承父類的屬性并保留傳參的優(yōu)點扮匠,然后通過將父類實例作為子類原型,實現(xiàn)函數(shù)復(fù)用
function Author(name, books){
Persion.call(this, name);
this.books = books;
}
Author.prototype = new Persion();
Author.prototype.constructor = Author;
Author.prototype.getBooks = function(){
return this.books;
}
var xxy = new Author('XXY')
- 使用
call
或apply
方法凡涩,將父對象的構(gòu)造函數(shù)綁定在子對象上棒搜。 - 任何一個
prototype
對象都有一個constructor
屬性,指向它的構(gòu)造函數(shù)活箕,第17行的目的是為了防止繼承鏈的紊亂力麸。
優(yōu)點:
- 彌補了
構(gòu)造繼承
的缺陷,可以繼承實例屬性/方法育韩,也可以繼承原型屬性/方法 - 既是子類的實例克蚂,也是父類的實例
- 不存在引用屬性共享問題
- 可傳參
- 函數(shù)可復(fù)用
缺點:
- 調(diào)用了兩次父類構(gòu)造函數(shù),生成了兩份實例(子類實例將子類原型上的那份屏蔽了)
6. 寄生組合繼承
核心:通過寄生方式座慰,砍掉父類的實例屬性陨舱,這樣,在調(diào)用兩次父類的構(gòu)造的時候版仔,就不會初始化兩次實例方法/屬性游盲,避免的組合繼承的缺點
提示:推薦使用
function Author(name){
Persion.call(this, name);
}
(function(){
// 創(chuàng)建一個沒有實例方法的類
var Super = function(){};
Super.prototype = Persion.prototype;
//將實例作為子類的原型
Author.prototype = new Super();
Author.prototype.constructor = Author;
})();
var xxy = new Author('XXY')
為了簡化類的聲明,可以把派生子類的整個過程包裝在一個extend的函數(shù)中蛮粮。
function extend(subClass, superClass) {
var F = function(){};
F.prototype = superClass.prototype;
subClass.prototype = new F();
subClass.prototype.constructor = subClass;
}
但是益缎,這樣存在一個問題。就是同名函數(shù)會被復(fù)寫然想,可以添加一個superclass
屬性來解決這個問題莺奔。
function extend(subClass, superClass) {
var F = function() {};
F.prototype = superClass.prototype;
subClass.prototype = new F();
subClass.prototype.constructor = subClass;
subClass.superclass = superClass.prototype; // 添加superclass屬性
if(superClass.prototype.constructor == Object.prototype.constructor) {
superClass.prototype.constructor = superClass;
}
}
function Author(name, books){
Author.superclass.constructor.call(this, name);
this.books = books;
}
extend(Author, Persion);
Author.prototype.getBooks = function(){
return this.books;
}
有了superclass
屬性,子類便可定義跟父類重名的原型方法变泄,使用其原有函數(shù)的邏輯令哟。