一:繼承的幾種方式
繼承需要一個父類盅视,在這里我們先把他定義出來:
// 定義一個動物類
function Animal (name) {
// 屬性
this.name = name || 'Animal';
this.sleep = function(){
console.log(this.name + '正在睡覺!');
}
}
// 原型方法
Animal.prototype.eat = function(food) {
console.log(this.name + '正在吃:' + food);
};
1旦万,原型鏈繼承
原型鏈繼承的核心是闹击,將父類的實(shí)例作為子類的原型
function Cat(){
this.name = 'cat'
}
Cat.prototype = new Animal();
Cat.prototype.name = 'cat';//將Cat的原型名字設(shè)為Cat,這句話必須放在new之后
Cat.prototype.constructor = Cat;//將Cat的原型鏈constructer指向Cat成艘,防止出現(xiàn)混亂
// Test Code
var cat = new Cat();
console.log(cat.name);// cat
console.log(cat.eat('fish'));//cat正在吃fish
console.log(cat.sleep());//cat正在睡覺
console.log(cat instanceof Animal); //true
console.log(cat instanceof Cat); //true
特點(diǎn)
- 非常純粹的繼承關(guān)系赏半,實(shí)例是子類的實(shí)例,也是父類的實(shí)例
- 父類新增原型方法/原型屬性淆两,子類都能訪問到
- 簡單断箫,易于實(shí)現(xiàn)
缺點(diǎn)
- 可以在Cat構(gòu)造函數(shù)中,為Cat實(shí)例增加實(shí)例屬性秋冰。如果要新增原型屬性和方法仲义,則必須放在new Animal()這樣的語句之后執(zhí)行。
- 無法實(shí)現(xiàn)多繼承
- 來自原型對象的"引用屬性":(如數(shù)組或者對象的話)剑勾,是所有實(shí)例共享的
- 創(chuàng)建子類實(shí)例時埃撵,無法向父類構(gòu)造函數(shù)傳參
對于第四點(diǎn)舉個例子:
function Parents(name){ this.name=name; }
Children.prototype=new Parents("Hello");
那么此時,Children 類就擁有了 name=“Hello” 屬性虽另,而 Children 類的實(shí)例對象 c1暂刘、c2、c3 等等只能被迫接受這個 name 屬性捂刺。Children 是 "Hello" 的擁有者而 c1谣拣、 c2、c3不是叠萍!
2芝发,構(gòu)造繼承
function Cat(name){
Animal.call(this);
this.name = name || 'Tom';
}
// Test Code
var cat = new Cat('fujiawei');
console.log(cat.name);//fujiawei
console.log(cat.sleep());//fujiawei 正在睡覺
console.log(cat.eat());//Uncaught TypeError: cat.eat is not a function( 缺點(diǎn)是第三個 )
console.log(cat instanceof Animal); // false
console.log(cat instanceof Cat); // true
特點(diǎn)
- 解決了1中绪商,子類實(shí)例共享父類引用屬性的問題
- 創(chuàng)建子類實(shí)例時苛谷,可以向父類傳遞參數(shù)
- 可以實(shí)現(xiàn)多繼承(call多個父類對象)
缺點(diǎn)
- 實(shí)例并不是父類的實(shí)例,只是子類的實(shí)例格郁,這就造成了缺點(diǎn)三
- 只能繼承父類的實(shí)例屬性和方法腹殿,不能繼承原型屬性/方法
- 無法實(shí)現(xiàn)函數(shù)復(fù)用,每個子類都有父類實(shí)例函數(shù)的副本例书,影響性能
3锣尉,實(shí)例繼承
function Cat(name){
var instance = new Animal();
instance.name = name || 'Tom';
return instance;
}
// Test Code
var cat = new Cat();
console.log(cat.name);//Tom
console.log(cat.sleep());//Tom正在睡覺
console.log(cat.eat('apple'));//Tom正在吃:apple
console.log(cat instanceof Animal); // true
console.log(cat instanceof Cat); // false
特點(diǎn)
- 不限制調(diào)用方式,不管是new 子類()還是子類(),返回的對象具有相同的效果
var cat = new Cat()
var cat = Cat()
//1,2步調(diào)用的效果相同
缺點(diǎn)
- 實(shí)例是父類的實(shí)例决采,不是子類的實(shí)例
- 不支持多繼承
4自沧,拷貝繼承
function Cat(name){
var animal = new Animal();
for(var p in animal){
Cat.prototype[p] = animal[p];
}
Cat.prototype.name = name || 'Tom';
}
// Test Code
var cat = new Cat();
console.log(cat.name);// Tom
console.log(cat.sleep());// Tom正在睡覺!
console.log(cat.eat('apple'));// Tom正在吃:apple
console.log(cat instanceof Animal); // false
console.log(cat instanceof Cat); // true
優(yōu)點(diǎn)
- 支持多繼承
缺點(diǎn)
- 效率較低,內(nèi)存占用高(因為要拷貝父類的屬性)
- 無法獲取父類不可枚舉的方法(不可枚舉方法拇厢,不能使用for in 訪問到)
5爱谁,組合繼承
function Cat(name){
Animal.call(this);
this.name = name || 'Tom';
}
Cat.prototype = new Animal();
// Test Code
var cat = new Cat();
console.log(cat.name);//Tom
console.log(cat.sleep());//Tom正在睡覺
console.log(cat.eat('apple'));//Tom正在吃:apple
console.log(cat instanceof Animal); // true
console.log(cat instanceof Cat); // true
特點(diǎn)
- 彌補(bǔ)了方式2的缺陷,可以繼承實(shí)例屬性/方法孝偎,也可以繼承原型屬性/方法
- 既是子類的實(shí)例访敌,也是父類的實(shí)例
- 不存在引用屬性共享問題
- 可傳參
- 函數(shù)可復(fù)用
缺點(diǎn)
- 調(diào)用了兩次父類構(gòu)造函數(shù),生成了兩份實(shí)例(子類實(shí)例將子類原型上的那份屏蔽了)
6衣盾,寄生組合繼承
function Cat(name){
Animal.call(this);
this.name = name || 'Tom';
}
(function(){
// 創(chuàng)建一個沒有實(shí)例方法的類
var Super = function(){};
Super.prototype = Animal.prototype;
//將實(shí)例作為子類的原型
Cat.prototype = new Super();
})();
Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // true
console.log(cat instanceof Cat); //true
特點(diǎn)
- 堪稱完美
缺點(diǎn)
- 實(shí)現(xiàn)較為復(fù)雜
最主要的是創(chuàng)建一個空函數(shù)F()寺旺,并將其原型設(shè)置為父級構(gòu)造器,然后我們既可以用new F()來創(chuàng)建一些不包含父對象屬性的對象势决,同時可以從父對象的prototype中繼承一切了阻塑;