創(chuàng)建對象(二)——原型模式

原文地址:創(chuàng)建對象(二)——原型模式

理解原型對象

我們創(chuàng)建的每一個(gè)函數(shù)都有一個(gè)默認(rèn)的prototype屬性屯伞,它指向一個(gè)對象豪直,對象中默認(rèn)的有一個(gè)叫做constructor的屬性,指向這個(gè)函數(shù)本身末融。

如上圖暇韧,右側(cè)的這個(gè)方框就是函數(shù)function的原型,也就是說prototype屬性指向的這個(gè)對象就是原型巧婶。
當(dāng)構(gòu)造函數(shù)創(chuàng)建出一個(gè)新實(shí)例后,該實(shí)例會(huì)默認(rèn)具有一個(gè)__proto__屬性英岭,這個(gè)屬性指向構(gòu)造函數(shù)的原型對象眼滤。因此,如果我們把屬性和方法都添加到原型對象中诅需,不同的實(shí)例就可以訪問到相同的屬性和方法了堰塌。

    function Person() {
    }
    Person.prototype.name = "wanghan";
    Person.prototype.age = 20;
    Person.prototype.getName = function () {
        console.log(this.name);
    };
    var fun1 = new Person();
    var fun2 = new Person();
    console.log(fun1.name);   //wanghan
    console.log(fun2.age);    //20
    fun1.getName();           //wanghan
    fun2.getName();           //wanghan
    console.log(fun1.getName == fun2.getName);  //true

我們先聲明了一個(gè)空的構(gòu)造函數(shù)Person分衫,將一些屬性和方法添加到了Person函數(shù)的prototype屬性中,然后實(shí)例化了兩個(gè)對象牵现,根據(jù)后面的輸出我們可以確定兩個(gè)實(shí)例訪問的是相同的對象邀桑,它們共享這些屬性和方法。下圖展示了例子中各個(gè)對象的關(guān)系:

實(shí)例與原型中屬性的糾葛

當(dāng)為對象實(shí)例添加一個(gè)屬性時(shí)贼急,這個(gè)屬性就會(huì)屏蔽原型對象中保存的同名屬性捏萍。換句話說,添加這個(gè)屬性只會(huì)阻止我們?nèi)ピL問原型中的那個(gè)屬性走敌,但不會(huì)修改那個(gè)屬性逗噩。你也可以理解為,當(dāng)我們訪問實(shí)例對象時(shí)机打,會(huì)優(yōu)先訪問它自身的屬性和方法。

    function Person() {
    }
    Person.prototype.name = "wanghan";
    Person.prototype.age = 20;
    Person.prototype.getName = function () {
        console.log(this.name);
    };
    var fun1 = new Person();
    var fun2 = new Person();
    fun1.name = "Tom";
    console.log(fun1.name);   //Tom
    console.log(fun2.name);   //wanghan
    fun1.getName();           //Tom
    fun2.getName();           //wanghan

在這個(gè)例子中残邀,我們給實(shí)例fun1添加了屬性name="Tom"

  • 訪問fun1.name時(shí)驱闷,先在實(shí)例fun1中尋找屬性name空免,找到之后返回Tom就不必再搜索原型了。
  • 而訪問fun2.name時(shí)蹋砚,先在實(shí)例fun2中沒有找到屬性name,于是搜索原型循榆,結(jié)果找到了值為wanghan的屬性name,驗(yàn)證了在實(shí)例中添加屬性不會(huì)修改原型中的同名屬性秧饮。
  • 訪問fun1.getName()時(shí)泽篮,在在實(shí)例fun1中沒有找到方法getName(),搜索原型找到了這個(gè)方法帽撑,執(zhí)行其中的代碼,此時(shí)方法中的this已經(jīng)指向了實(shí)例(回憶一下操作符new調(diào)用構(gòu)造函數(shù)經(jīng)歷的過程)历恐,因此輸出的是實(shí)例屬性name的值Tom

判斷屬性的位置

  • hasOwnProperty()方法只在給定屬性存在于實(shí)例對象中時(shí)返回true弱贼。
  • 單獨(dú)使用in操作符時(shí)磷蛹,無論屬性存在于實(shí)例中還是原型中,只要能夠訪問到味咳,就會(huì)返回true

如果實(shí)例和原型中都存在著一個(gè)相同屬性责嚷,結(jié)合這兩種方法我們就可以判斷,我們訪問到的這個(gè)同名屬性到底是實(shí)例中的還是原型中的罕拂。

    function Person() {
    }
    Person.prototype.name = "wanghan";
    Person.prototype.age = 20;
    var fun = new Person();
    fun.name = "Tom";
    console.log("name" in fun);               //true
    console.log(fun.hasOwnProperty("name"));  //true    --name來自實(shí)例
    console.log("age" in fun);                //true
    console.log(fun.hasOwnProperty("age"));   //false   --age來自原型

更簡單的原型語法

在前面的例子中,我們給原型對象添加對象的時(shí)候衷掷,輸入了好多遍Person.prototype柿菩,其實(shí)這些不必要的輸入都是可以避免的,最常見的方法就是以對象字面量的形式創(chuàng)建對象懦胞。

    function Person() {
    }
    Person.prototype={
        name: "wanghan",
        age: 20,
        getName: function () {
            console.log(this.name)
        }
    };
    var fun = new Person();
    console.log(fun.name);  //wanghan
    console.log(fun.age);   //20
    fun.getName();           //wanghan

這樣創(chuàng)建對象是不是很輕松,而且輸出的結(jié)果跟之前相比并沒有什么變化医瘫。但是這里有一點(diǎn)需要注意旧困,重寫原型對象的實(shí)質(zhì)是吼具,我們重建了一個(gè)對象賦值給了函數(shù)的prototype矩距,所以新建的這個(gè)原型對象中默認(rèn)的constructor屬性也是新建的,它不指向構(gòu)造函數(shù)Person陡蝇,但是必要情況下我們可以手動(dòng)讓它指向Person哮肚。

    function Person() {
    }
    Person.prototype={
        constructor: Person,   //看這里
        name: "wanghan",
        age: 20,
        getName: function () {
            console.log(this.name)
        }
    };

重寫原型對象的弊端

由于實(shí)例與原型之間的松散連接關(guān)系,即使我們先創(chuàng)建實(shí)例恼策,再給原型對象添加屬性潮剪,我們也照樣可以訪問到這些屬性。

    function Person() {
    }
    var fun = new Person();
    Person.prototype.name ="wanghan";
    console.log(fun.name);  //wanghan

但是重寫原型對象之后就不一樣了抗碰。

    function Person() {
    }
    var fun = new Person();
    Person.prototype={
        constructor: Person,
        name: "wanghan"
    };
    console.log(fun.name);   //undefined

我們前面說過弧蝇,重寫原型對象實(shí)際上是新建了一個(gè)新的對象賦值給構(gòu)造函數(shù)的prototype折砸。如果是先創(chuàng)建一個(gè)實(shí)例骤视,那么實(shí)例指向最開始的一個(gè)只含有constructor屬性的原型對象,即使隨后又新建了一個(gè)原型對象睹逃,它的指向也不會(huì)再發(fā)生變化祷肯。


要想解決這個(gè)問題就要牢記佑笋,要在重寫原型對象之后新建實(shí)例,這樣實(shí)例指向的就是重寫之后的原型對象蒋纬。

    function Person() {
    }
    Person.prototype={
        constructor: Person,
        name: "wanghan"
    };
    var fun = new Person();
    console.log(fun.name);   //wanghan

原型對象的問題

原型模式也不是沒有缺點(diǎn)。

  • 原型中的所有屬性和方法都是被實(shí)例所共享的关摇,共享方法(函數(shù))是非常合適的碾阁,對于那些基本值的屬性也還說的過去,但是對于包含引用類型值得屬性來說宪睹,問題就非常突出了蚕钦。
    function Person() {
    }
    Person.prototype={
        constructor: Person,
        name: "wanghan",
        friends: ["zhangmin","yangfan"]
    };
    var fun1 = new Person();
    var fun2 = new Person();
    fun1.friends.push("Tom");
    console.log(fun1.friends);   //["zhangmin", "yangfan", "Tom"]
    console.log(fun2.friends);   //["zhangmin", "yangfan", "Tom"]

原型對象中有一個(gè)字符串?dāng)?shù)組,然后創(chuàng)建了兩個(gè)實(shí)例命贴,操作實(shí)例fun1向原型對象的數(shù)組中又添加了一個(gè)字符串Tom胸蛛,由于數(shù)組是引用類型值葬项,并且兩個(gè)實(shí)例共享原型對象中的屬性,所以我們剛剛的修改也會(huì)在實(shí)例fun2中體現(xiàn)出來民珍。這個(gè)問題正是我們極少看到有人單獨(dú)使用原型模式的原因所在。

  • 原型對象直接在原型對象里定義了屬性值嚷量,使所有實(shí)例默認(rèn)情況下都取得相同的屬性值,要克服這一缺點(diǎn)可以組合使用原型模式和構(gòu)造函數(shù)模式嗜历。

后記

我畫框圖是用電腦自帶的畫圖軟件畫的你敢信,舉得例子也是自己手打出來的梨州,運(yùn)行正確之后就剪切到markdown上面田轧。眼看著快要寫完了,時(shí)間也快凌晨兩點(diǎn)了傻粘,喜滋滋地準(zhǔn)備收尾,誰知道電腦突然一黑岛请,重啟了警绩。再次打開markdown之后我就絕望了肩祥,就剩下前兩段擺在上面。我冷靜了二十分鐘混狠,發(fā)了個(gè)朋友圈疾层,然后重寫到凌晨四點(diǎn)。嗨呀痛黎,好氣呀。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末掖蛤,一起剝皮案震驚了整個(gè)濱河市井厌,隨后出現(xiàn)的幾起案子致讥,更是在濱河造成了極大的恐慌垢袱,老刑警劉巖港柜,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異姚糊,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)救恨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進(jìn)店門肠槽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來奢啥,“玉大人,你說我怎么就攤上這事桩盲。” “怎么了捞蛋?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵拟杉,是天一觀的道長量承。 經(jīng)常有香客問我,道長撕捍,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任贞言,我火速辦了婚禮阀蒂,結(jié)果婚禮上弟蚀,老公的妹妹穿的比我還像新娘酗失。我一直安慰自己,他們只是感情好捶闸,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布拖刃。 她就那樣靜靜地躺著,像睡著了一般兑牡。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上均函,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天苞也,我揣著相機(jī)與錄音,去河邊找鬼如迟。 笑死,一個(gè)胖子當(dāng)著我的面吹牛牛哺,可吹牛的內(nèi)容都是我干的劳吠。 我是一名探鬼主播巩趁,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼议慰,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了草讶?” 一聲冷哼從身側(cè)響起炉菲,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤坤溃,失蹤者是張志新(化名)和其女友劉穎嘱丢,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體汁政,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡缀旁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年并巍,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片嘶窄。...
    茶點(diǎn)故事閱讀 37,997評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡柄冲,死狀恐怖忠蝗,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情阁最,我是刑警寧澤,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布姜盈,位于F島的核電站,受9級(jí)特大地震影響馏颂,放射性物質(zhì)發(fā)生泄漏棋傍。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一亿絮、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧派昧,春花似錦斗锭、人聲如沸帮毁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽绳慎。三九已至杏愤,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間邓了,已是汗流浹背蛇受。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工把将, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人宗收。 一個(gè)月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子璃俗,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評論 2 345

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