Javascript中幾種較為流行的繼承方式

開篇

從'嚴格'意義上說,javascript并不是一門真正的面向?qū)ο笳Z言窿锉。這種說法原因一般都是覺得javascript作為一門弱類型語言與類似java或c#之類的強型語言的繼承方式有很大的區(qū)別责掏,因而默認它就是非主流的面向?qū)ο蠓绞矫睿踔辆褂泻芏鄷鴮⑵涿枋鰹?非完全面向?qū)ο?語言脖阵。其實個人覺得,什么方式并不重要荷辕,重要的是否具有面向?qū)ο蟮乃枷耄fjavascript不是面向?qū)ο笳Z言的件豌,往往都可能沒有深入研究過javascript的繼承方式疮方,故特撰此文以供交流。

為何需要利用javascript實現(xiàn)繼承

早期pc機器的性能確實不敢恭維茧彤,所有的壓力全在服務(wù)器端骡显,客戶端瀏覽器純屬擺設(shè)。再加上那時流行的table布局以及電話線的上網(wǎng)方式導(dǎo)致瀏覽一個網(wǎng)頁十分的卡;而今互聯(lián)網(wǎng)時代飛速發(fā)展惫谤,個人電腦硬件得到了極大提升壁顶,客戶端瀏覽器的性能也十分的酸爽,web開發(fā)的模式也在悄悄改變:服務(wù)端不再像以前那樣“辛苦”溜歪,取而代之的是盡可能的讓瀏覽器承擔(dān)更多的任務(wù)若专,如此一來,壓力分攤到每個客戶端上蝴猪,企業(yè)不但節(jié)省成本调衰,隨之也讓web前端開發(fā)變的更加有趣--越來越多的前端框架層出不窮,甚至出現(xiàn)了前端的MVC模型拯腮。在這種背景下窖式,javascript的角色已經(jīng)絕對不是只做一些簡單的驗證,發(fā)送一些請求或者操作一些DOM动壤,更多的需要擔(dān)任類似于路由層和業(yè)務(wù)層的角色萝喘。相反,javascript需要做大量的邏輯性任務(wù)琼懊,這里面就包括前臺數(shù)據(jù)的抽離(即model)阁簸,而只有運用面向?qū)ο蟮乃季S才能很好的對抽離數(shù)據(jù)進行處理,因此繼承就在這里顯得舉足輕重哼丈。

從一個簡單的需求開始

現(xiàn)從前臺抽離一個model名為Person启妹,其有基本屬性name和age,默認每個人都會說話醉旦,因此將說話的功能say放在了原型對象上饶米,以供每個實例享用。現(xiàn)在對于Man來說车胡,它需要繼承Person的基本屬性檬输,并且在此基礎(chǔ)上添加自己特有的屬性。

function Person (name, age) {
    this.name = name;
    this.age = age;
}
Person.prototype.say = function(){
    console.log('hello, my name is ' + this.name);
};
function Man() {
    //my own properties
}

下面介紹幾種主流的繼承方式匈棘。

1.原型鏈繼承

function Person (name, age) {
    this.name = name;
    this.age = age;
}
Person.prototype.say = function(){
    console.log('hello, my name is ' + this.name);
};
function Man() {
}
Man.prototype = new Person('pursue');
var man1 = new Man();
man1.say(); //hello, my name is pursue
var man2 = new Man();
console.log(man1.say === man2.say);//true
console.log(man1.name === man2.name);//true

這種繼承方式很直接丧慈,為了獲取Person的所有屬性方法(實例上的和原型上的),直接將父類的實例new Person('pursue')賦給了子類的原型主卫,其實子類的實例man1,man2本身是一個完全空的對象逃默,所有的屬性和方法都得去原型鏈上去找,因而找到的屬性方法都是同一個簇搅。
所以直接利用原型鏈繼承是不現(xiàn)實的完域。

2.利用構(gòu)造函數(shù)繼承

function Person (name, age) {
    this.name = name;
    this.age = age;
}
Person.prototype.say = function(){
    console.log('hello, my name is ' + this.name);
};
function Man(name, age) {
    Person.apply(this, arguments);
}
//Man.prototype = new Person('pursue');
var man1 = new Man('joe');
var man2 = new Man('david');
console.log(man1.name === man2.name);//false
man1.say(); //say is not a function

這里子類的在構(gòu)造函數(shù)里利用了apply去調(diào)用父類的構(gòu)造函數(shù),從而達到繼承父類屬性的效果瘩将,比直接利用原型鏈要好的多筒主,至少每個實例都有自己那一份資源关噪,但是這種辦法只能繼承父類的實例屬性,因而找不到say方法乌妙,為了繼承父類所有的屬性和方法,則就要修改原型鏈建钥,從而引入了組合繼承方式藤韵。

3.組合繼承

function Person (name, age) {
    this.name = name;
    this.age = age;
}
Person.prototype.say = function(){
    console.log('hello, my name is ' + this.name);
};
function Man(name, age) {
    Person.apply(this, arguments);
}
Man.prototype = new Person();
var man1 = new Man('joe');
var man2 = new Man('david');
console.log(man1.name === man2.name);//false
console.log(man1.say === man2.say);//true
man1.say(); //hello, my name is joe

需要注意的是man1和man2的實例屬性其實是覆蓋了原型屬性,但是并沒要覆蓋掉原型上的say方法(因為它們沒有)熊经,所以這里man1.say === man2.say依然返回true泽艘,因而需要十分小心沒有覆蓋掉的原型屬性,因為它是所有實例共有的镐依。

4.寄生組合繼承

說實話我真不知道下面的這種形式叫這名字匹涮,但是它確實是最流行,最經(jīng)典的javascript的繼承方式槐壳。
其實然低,只需要明白原型對象的結(jié)構(gòu)即可:

function Person (name, age) {
            this.name = name;
            this.age = age;
        }
Person.prototype.say = function(){
    console.log('hello, my name is ' + this.name);
};
function Man(name, age) {
    Person.apply(this, arguments);
}
Man.prototype = Object.create(Person.prototype);//a.
Man.prototype.constructor = Man;//b.
var man1 = new Man('pursue');
var man2 = new Man('joe');
console.log(man1.say == man2.say);
console.log(man1.name == man2.name);

其實寄生組合繼承和上面的組合繼承區(qū)別僅在于構(gòu)造子類原型對象的方式上(a.和b.),這里用到了Object.creat(obj)方法务唐,該方法會對傳入的obj對象進行淺拷貝雳攘,類似于:

function create(obj){
    function T(){};
    T.prototype = obj;
    return new T();
}

因此,a.會將子類的原型對象與父類的原型對象進行很好的連接枫笛,而并不像一般的組合繼承那樣直接對子類的原型進行復(fù)制(如Man.prototype = new Person();),這樣只是很暴力的在對屬性進行覆蓋吨灭。而寄生組合繼承方式則對實例屬性和原型屬性分別進行了繼承,在實現(xiàn)上更加合理刑巧。

注意:代碼b.并不會改變instanceof的結(jié)果喧兄,但是對于需要用到construcor的場景,這么做更加嚴謹啊楚。

注:本文與Github同步Blog請戳

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末吠冤,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子特幔,更是在濱河造成了極大的恐慌咨演,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,686評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蚯斯,死亡現(xiàn)場離奇詭異薄风,居然都是意外死亡,警方通過查閱死者的電腦和手機拍嵌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,668評論 3 385
  • 文/潘曉璐 我一進店門遭赂,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人横辆,你說我怎么就攤上這事撇他。” “怎么了?”我有些...
    開封第一講書人閱讀 158,160評論 0 348
  • 文/不壞的土叔 我叫張陵困肩,是天一觀的道長划纽。 經(jīng)常有香客問我,道長锌畸,這世上最難降的妖魔是什么勇劣? 我笑而不...
    開封第一講書人閱讀 56,736評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮潭枣,結(jié)果婚禮上比默,老公的妹妹穿的比我還像新娘。我一直安慰自己盆犁,他們只是感情好命咐,可當(dāng)我...
    茶點故事閱讀 65,847評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著谐岁,像睡著了一般醋奠。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上翰铡,一...
    開封第一講書人閱讀 50,043評論 1 291
  • 那天钝域,我揣著相機與錄音,去河邊找鬼锭魔。 笑死例证,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的迷捧。 我是一名探鬼主播织咧,決...
    沈念sama閱讀 39,129評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼漠秋!你這毒婦竟也來了笙蒙?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,872評論 0 268
  • 序言:老撾萬榮一對情侶失蹤庆锦,失蹤者是張志新(化名)和其女友劉穎捅位,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體搂抒,經(jīng)...
    沈念sama閱讀 44,318評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡艇搀,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,645評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了求晶。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片焰雕。...
    茶點故事閱讀 38,777評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖芳杏,靈堂內(nèi)的尸體忽然破棺而出矩屁,到底是詐尸還是另有隱情辟宗,我是刑警寧澤,帶...
    沈念sama閱讀 34,470評論 4 333
  • 正文 年R本政府宣布吝秕,位于F島的核電站泊脐,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏烁峭。R本人自食惡果不足惜晨抡,卻給世界環(huán)境...
    茶點故事閱讀 40,126評論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望则剃。 院中可真熱鬧,春花似錦如捅、人聲如沸棍现。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,861評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽己肮。三九已至,卻和暖如春悲关,著一層夾襖步出監(jiān)牢的瞬間谎僻,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,095評論 1 267
  • 我被黑心中介騙來泰國打工寓辱, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留艘绍,地道東北人。 一個月前我還...
    沈念sama閱讀 46,589評論 2 362
  • 正文 我出身青樓秫筏,卻偏偏與公主長得像诱鞠,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子这敬,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,687評論 2 351

推薦閱讀更多精彩內(nèi)容