已經(jīng)忘了多久沒拿起javascirpt的書本了省艳,無聊中拾起快被我遺忘在角落的書本<javascript高級程序設(shè)計>。
尼古拉斯竟然把這么奇葩的js面向?qū)ο笳f的這么好
從最原始的工廠模板說起
function createPerson(name,age){
?var o = new Object();
?o.name = name;
?o.age = age;
o.sayName = function(){
? console.log(this.name);
}
?return o;
}
這樣塌忽,but...等等拍埠,如何知道創(chuàng)建的對象是什么類型的失驶?
構(gòu)造函數(shù)模式
function Person(name,age){
this.name = name;
this.age = age;
this.sayName = function(){
}
var person1 = new Person("三毛",25);
簡潔了不少土居,問題又來了:難道我每次new一個對象,就要重新創(chuàng)建個sayName函數(shù)嗎嬉探?為什么這么說擦耀?
函數(shù)也是對象,每次實例化Person時涩堤,都會實例化一個sayName Function眷蜓。也就是說:每個Person的示例,都有一個不同的sayName Function實例胎围。何必如此吁系?
理解原型模式
原型模式很好的解決了上述問題,這么說吧白魂,每個function都有一個prototype屬性(我們叫原型)汽纤,指向一個原型對象。
所以福荸,我們可以把公共的方法屬性都加到它上面蕴坪。
function Person(){
}
Person.prototype.name = "三毛";
Person.prototype.age = 25;
Person.prototype.sayName = function(){
?console.log(this.name);
}
把屬性方法加到了函數(shù)的prototype(實際上也是個對象)上,后續(xù)創(chuàng)建的實例對象都從這個原型對象上繼承了屬性和方法。
然而背传,它也不是完美的呆瞻。原型對象的問題也是顯而易見的:由于所有的實例都是共享原型對象的方法屬性,那么意思就是說所有的實例共享了一份屬性方法径玖!那創(chuàng)建的實例還有個毛用痴脾?
聯(lián)想到前面我們提過的構(gòu)造函數(shù)模式,我們可以把構(gòu)造函數(shù)+原型模式結(jié)合起來挺狰。
構(gòu)造函數(shù)負(fù)責(zé)自有的屬性明郭,而原型對象負(fù)責(zé)公共的方法,一種新的模式應(yīng)運而生:
組合模式
Person.prototype.sayName = function(){
console.log(this.name);
}
完美解決了構(gòu)造函數(shù)模式和原型模式的問題丰泊。
再談?wù)刯avascript繼承
在“理解原型模式”中薯定,已經(jīng)談到了js的原型模式,而js的繼承就是依靠原型鏈來實現(xiàn)的瞳购。
js原型鏈
我們來簡單回顧下前面講過的內(nèi)容:
1 每個構(gòu)造函數(shù)都有一個原型對象话侄;
2 原型對象包含一個指向構(gòu)造函數(shù)的指針(證明自己是從哪來的);
3 構(gòu)造函數(shù)的實例(這么說可能有點不準(zhǔn)確)包含一個指向原型對象的指針学赛。
有點暈年堆,感覺有點像三角戀,看個實例:
function SuperType(){
this.property = true;
}
SuperType.prototype.getSuperValue = function(){
?return this.property;
}
function SubType(){
?this.subproperty = false;
}
//繼承盏浇,使SubType的原型指向SuperType的原型
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function(){
?return this.subproperty;
}
var instance = new SubType();
console.log(instance.getSuperValue());//true,實際上是SuperType的值
看以看出变丧,單獨使用原型鏈實現(xiàn)繼承,實際上生成的實例都是共享一份屬性或者方法的绢掰。
那么痒蓬,實例就沒有意義了。
那如果借用構(gòu)造函數(shù)呢滴劲?
我們知道攻晒,構(gòu)造函數(shù)只能實現(xiàn)對實例屬性的繼承,沒辦法實現(xiàn)方法的繼承班挖,所以如果單獨使用構(gòu)造函數(shù)模式鲁捏,方法就需要都寫在構(gòu)造函數(shù)里面,函數(shù)復(fù)用就無從談起了萧芙。
組合繼承
組合繼承借用了原型鏈實現(xiàn)原型屬性和方法的繼承给梅,又可以通過構(gòu)造函數(shù)實現(xiàn)實例屬性的繼承∷荆可以說既實現(xiàn)了函數(shù)復(fù)用动羽,又保證每個實例都有自己的屬性。
function SuperType(name){
?this.name = name;
?this.colors = ["red", "blue"];
}
SuperType.prototype.sayName = function(){
?console.log(this.name);
}
function SubType(name,age){
?SuperType.call(this,name);//在當(dāng)前實例環(huán)境下執(zhí)行SuperType構(gòu)造函數(shù)->2
?this.age = age;
}
SubType.prototype = new SuperType();//->1
SubType.prototype.sayAge = function(){
?console.log(this.age);
}
看以看到盟榴,這種模式結(jié)合了兩者的優(yōu)點曹质,又彌補(bǔ)了相互的不足。可以說羽德,是一種不錯的用于實現(xiàn)繼承的方法几莽。
但是,這種方法并不是最好的宅静,我們來詳細(xì)看一下:
1 為了實現(xiàn)繼承章蚣,第一點調(diào)用了SuperType的構(gòu)造函數(shù),這樣SubType.prototype繼承了兩個屬性
2 SubType構(gòu)造函數(shù)內(nèi)部姨夹,又調(diào)用了一次SubType構(gòu)造函數(shù)纤垂,顯得有些多余了
寄生組合繼承
通過借用構(gòu)造函數(shù)來繼承屬性,通過原型鏈來繼承方法磷账。
function object(super){
?var func = function(){};
?func.prototype = super;
?var o = new func();
?return o;
}
function inheritPrototype(sub,super){
?var prototype = object(super.prototype);
?prototype.constructor = sub;
?sub.prototype = prototype;
}
這是寄生組合繼承的簡單形式峭沦。
通過接受子構(gòu)造函數(shù)和父構(gòu)造函數(shù),第一步創(chuàng)建父類型原型的一個副本逃糟;
第二步為副本添加constructor屬性吼鱼,最后再把新的原型賦給子原型,完美實現(xiàn)了繼承關(guān)系绰咽。