JS創(chuàng)建對(duì)象的幾種模式

JS創(chuàng)建對(duì)象的幾種模式及分析對(duì)比

工廠模式

function createPerson(name, age, job){
    //創(chuàng)建對(duì)象
    var o = new Object();
    //添加屬性
    o.name = name;
    o.age = age;
    o.job = job;
    //添加方法
    o.sayName = function(){
        console.log(this.name);
    };
    //返回對(duì)象
    return o;
}

實(shí)例化:

var person1 = createPerson('smith', 25, 'coder');

缺點(diǎn):沒有解決對(duì)象的識(shí)別問題.

構(gòu)造函數(shù)模式

function Person(name, age, job){    //Person也是普通的函數(shù), 但便于與普通函數(shù)區(qū)分, 通常將構(gòu)造函數(shù)首字母大寫
    //添加屬性
    this.name = name;
    this.age = age;
    this.job = job;
    //添加方法
    this.sayName = function(){
        console.log(this.name);
    };
}

通過對(duì)比工廠模式我們發(fā)現(xiàn):

  1. 沒有顯示創(chuàng)建對(duì)象;
  2. 直接將屬性和方法賦給了this對(duì)象,this就是new出來的對(duì)象;
  3. 沒有return語句.

實(shí)例化:

var person1 = new Person('smith', 25, 'coder');

判斷實(shí)例類型:

console.log(person1.constructor === Person);   //true
console.log(person1 instanceof Person);        //true
console.log(person1 instanceof Object);        //true

優(yōu)點(diǎn): 創(chuàng)建自定義的構(gòu)造函數(shù)可以將實(shí)例標(biāo)識(shí)為一種特定的類型

缺點(diǎn): 每個(gè)方法都要在每個(gè)實(shí)例上重新創(chuàng)建一遍, 無法代碼共用.

原型模式

function Person(){}

//Person.prototype為原型對(duì)象,每個(gè)構(gòu)造函數(shù)的protot屬性指向其對(duì)應(yīng)的原型對(duì)象,以下為原型對(duì)象添加屬性和方法
Person.prototype.name = 'smith';
Person.prototype.age = 25;
Person.prototype.job = 'coder';
Person.prototype.sayName = function(){
    console.log(this.name);
}

實(shí)例化:

var person1 = new Person();

獲取原型對(duì)象:

console.log(Object.getPrototypeOf(person1) === Person.prototype);   //true

判斷原型對(duì)象:

console.log(Person.prototype isPrototypeOf(person1))    //true

若為實(shí)例添加的屬性或方法時(shí)與原型對(duì)象里的屬性或方法相同, 添加的屬性或方法會(huì)屏蔽原型對(duì)象中的那個(gè)屬性或方法

function Person(){}

Person.prototype.name = 'smith';
Person.prototype.age = 25;
Person.prototype.job = 'coder';
Person.prototype.sayName = function(){
    console.log(this.name);
};

var person1 = new Person();
var person2 = new Person();

person1.name = 'Lihua';
console.log(person1.name)   //'Lihua'
console.log(person2.name)   //'smith'

通過以上結(jié)果發(fā)現(xiàn): person1.name屬性屏蔽了原型對(duì)象里的name屬性, 阻止了訪問原型中的那個(gè)屬性, 使用delete操作符刪除實(shí)例屬性后, 能夠重新訪問原型中的屬性

function Person(){}
Person.prototype.name= 'smith';
Person.prototype.age = '25';
Person.prototype.job = 'coder';
Person.prototype.sayName = function(){
    console.log(this.name);
}

var person1 = new Person();
var person2 = new Person();

person1.name = 'Lihua';
console.log(person1.sayName());     //Lihua
console.log(person2.sayName());     //smith

delete person1.name;
console.log(person1.sayName());     //smith

delete Object.getPrototypeOf(person1).name;     //刪除原型對(duì)象的name屬性
console.log(person1.sayName());     //undefined
console.log(person2.sayName());     //undefined

判斷屬性是否存在于實(shí)例中:

person1.name = 'Lihua';
console.log(person1.hasOwnProperty('name'))     //ture

原型的動(dòng)態(tài)性

對(duì)原型對(duì)象所做的任何修改能夠立即從實(shí)例上反映出來, 即使是先實(shí)例化后修改原型對(duì)象也是如此

var friend = new Person();
Person.prototype.sayHi = function(){
    console.log('hi');
}

friend.sayHi()      //依然能夠執(zhí)行

JS高程寫到:

盡管對(duì)原型對(duì)象所做的任何修改能夠立即在所有實(shí)例中反映出來, 但重寫整個(gè)原型對(duì)象情況還是不一樣的, 調(diào)用構(gòu)造函數(shù)時(shí)會(huì)為實(shí)例添加一個(gè)指向最初原型的[[prototype]]指針, 把原型修改為另外一個(gè)對(duì)象就切斷了構(gòu)造函數(shù)與最初原型之間的聯(lián)系

function Person(){}
var friend = new Person();

Person.prototype = {
    constructor: Person,
    name: 'Lihua',
    job: 'student',
    sayName: function(){
        console.log(this.name);
    }
};

friend.sayName();   //error

但覺得JS高程這種說法有點(diǎn)欠妥, 比如:

function Person(){}
Person.prototype.job = 'coder';
Person.prototype.sayJob = function(){
    console.log(this.job);
}
var friend = new Person();

Person.prototype = {
    constructor: Person,
    name: 'Lihua',
    job: 'student',
    sayName: function(){
        console.log(this.name);
    }
};
var person = new Person();

person.sayName();   //Lihua
friend.sayJob();    //coder
// person.sayJob();    error

我的理解是:

  1. 原型是一種動(dòng)態(tài)動(dòng)態(tài)的關(guān)系泼各,添加一個(gè)新的屬性或方法到原型中時(shí)旱幼,該屬性或方法會(huì)立即對(duì)所有基于該原型創(chuàng)建的對(duì)象可見,哪怕是修改原型之前創(chuàng)建的對(duì)象;
  2. 將原型重寫為另外一個(gè)對(duì)象后, 會(huì)影響重寫后實(shí)例得到的對(duì)象, 切斷了構(gòu)造函數(shù)與最初原型之間的聯(lián)系; 對(duì)于重寫之前已經(jīng)實(shí)例化的對(duì)象沒有影響, 因?yàn)檫@些對(duì)象里已經(jīng)保存了一個(gè)[[prototype]]指針, 該指針指向重寫之前的原型對(duì)象;

原型對(duì)象的問題

盡管可以在實(shí)例上添加一個(gè)同名屬性從而隱藏那些不需要共享的屬性, 然而對(duì)于包含引用類型的值的屬性來說, 問題就比較突出.

function Person(){}

Person.prototype = {
    constructor: Person,
    name: 'smith',
    job: 'coder',
    friends: ['shelby', 'court'],
    sayName: function(){
        console.log(this.name);
    }
};

var person1 = new Person();
var person2 = new Person();

person1.friends.push('van');

console.log(person1.friends);                       //'shelby,court,van'
console.log(person2.friends);                       //'shelby,court,van'
console.log(person1.friends === person2.friends);   //true

在這種情況下,person1person2friends屬性完全相同, 這不符合常識(shí)(一個(gè)人的朋友怎么可能和另外一個(gè)人的朋友完全相同呢?).

優(yōu)點(diǎn): 所有實(shí)例共享原型對(duì)象的屬性和方法

缺點(diǎn): 原型模式的最大問題正如以上所述, 無法屏蔽原型中數(shù)據(jù)類型為引用類型的屬性

構(gòu)造函數(shù)模式+原型模式

function Person(name, age, job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.friends = ['shelby', 'court'];
}

Person.prototype = {
    constructor: Person,
    sayName: function(){
        console.log(this.name);
    }
}

var person1 = new Person('smith', 25, 'coder');
var person2 = new Person('Lihua', 28, 'worker');

person1.friends.push('van');
console.log(person1.friends);                           //"shelby, court, van"
console.log(person2.friends);                           //"shelby, court"
console.log(person1.friends === person2.friends);       //false
console.log(person1.sayName === person2.sayName);       //true

構(gòu)造函數(shù)模式+原型模式結(jié)合了構(gòu)造函數(shù)模式和原型模式的優(yōu)點(diǎn), 將需要實(shí)例單獨(dú)擁有的屬性放到構(gòu)造函數(shù)里, 將需要實(shí)例共享的方法放到原型里, 這種模式是目前使用最廣泛和認(rèn)同度最高的一種創(chuàng)建自定義類型的方法.

動(dòng)態(tài)原型模式

function Person(name, age, job){
    //屬性
    this.name = name;
    this.age = age;
    this.job = job;

    //方法
    if(typeof this.sayName != 'function'){  //在sayName方法不存在的情況下,將其添加到原型中
        Person.prototype.sayName = function(){
            console.log(this.name);
        };
    }
}

var friend = new Person('smith', 25, 'coder');
friend.sayName();   //smith

動(dòng)態(tài)原型模式把所有信息都封裝到了構(gòu)造函數(shù)中, 通過在構(gòu)造函數(shù)中初始化原型(可選), 保持了同時(shí)使用構(gòu)造函數(shù)和原型的優(yōu)點(diǎn).

寄生構(gòu)造函數(shù)模式

function createPerson(name, age, job){
    //創(chuàng)建對(duì)象
    var o = new Object();
    //添加屬性
    o.name = name;
    o.age = age;
    o.job = job;
    //添加方法
    o.sayName = function(){
        console.log(this.name);
    };
    //返回對(duì)象
    return o;
}

var person1 = new Person('smith', 25, 'coder');
friend.sayName();   //'smith

寄生構(gòu)造函數(shù)模式除了調(diào)用方式與工廠模式不同外new Person('smith', 25, 'coder'),其他完全一樣, 通過在構(gòu)造函數(shù)末尾添加return語句, 重寫了調(diào)用構(gòu)造函數(shù)時(shí)返回的對(duì)象

寄生構(gòu)造函數(shù)模式適合在特殊情況下為對(duì)象創(chuàng)建構(gòu)造函數(shù). 因?yàn)椴煌扑]在產(chǎn)品化的程序中修改原生對(duì)象的原型, 但在某些場(chǎng)合下又的確需要?jiǎng)?chuàng)建一個(gè)具有額外方法的原生對(duì)象:

function SpecialArray(){
    //創(chuàng)建數(shù)組
    var values = new Array();
    //添加值
    values.push.apply(values, arguments);
    //添加方法
    values.toPipedString = function(){
        return this.join('|');
    }

    //返回具有額外方法的數(shù)組
    return values;
}

var colors = new SpecialArray('red', 'blue', 'green');
console.log(colors.toPipedString()) //'red|blue|green'

缺點(diǎn): 返回的對(duì)象與構(gòu)造函數(shù)或與構(gòu)造函數(shù)的原型屬性之間沒有關(guān)系, 因?yàn)?code>return語句重寫了調(diào)用構(gòu)造函數(shù)時(shí)返回的對(duì)象, 也即是構(gòu)造函數(shù)返回的對(duì)象與在構(gòu)造函數(shù)外部創(chuàng)建的對(duì)象沒有什么不同, 不能依賴instanceof操作符確定對(duì)象類型

穩(wěn)妥構(gòu)造函數(shù)模式

function Person(name, age, job){
    //創(chuàng)建要返回的對(duì)象
    var o = new Object();

    //這里定義私有變量和函數(shù)

    //添加方法
    o.sayName = function(){
        console.log(name);
    }

    //返回對(duì)象
    return o;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末宵蛀,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子县貌,更是在濱河造成了極大的恐慌术陶,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,451評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件煤痕,死亡現(xiàn)場(chǎng)離奇詭異梧宫,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)摆碉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,172評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門塘匣,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人巷帝,你說我怎么就攤上這事忌卤。” “怎么了楞泼?”我有些...
    開封第一講書人閱讀 164,782評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵驰徊,是天一觀的道長(zhǎng)笤闯。 經(jīng)常有香客問我,道長(zhǎng)棍厂,這世上最難降的妖魔是什么颗味? 我笑而不...
    開封第一講書人閱讀 58,709評(píng)論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮勋桶,結(jié)果婚禮上脱衙,老公的妹妹穿的比我還像新娘。我一直安慰自己例驹,他們只是感情好捐韩,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,733評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著鹃锈,像睡著了一般荤胁。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上屎债,一...
    開封第一講書人閱讀 51,578評(píng)論 1 305
  • 那天仅政,我揣著相機(jī)與錄音,去河邊找鬼盆驹。 笑死圆丹,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的躯喇。 我是一名探鬼主播辫封,決...
    沈念sama閱讀 40,320評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼廉丽!你這毒婦竟也來了倦微?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,241評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤正压,失蹤者是張志新(化名)和其女友劉穎欣福,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體焦履,經(jīng)...
    沈念sama閱讀 45,686評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡拓劝,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,878評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了嘉裤。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片凿将。...
    茶點(diǎn)故事閱讀 39,992評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖价脾,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情笛匙,我是刑警寧澤侨把,帶...
    沈念sama閱讀 35,715評(píng)論 5 346
  • 正文 年R本政府宣布犀变,位于F島的核電站,受9級(jí)特大地震影響秋柄,放射性物質(zhì)發(fā)生泄漏获枝。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,336評(píng)論 3 330
  • 文/蒙蒙 一骇笔、第九天 我趴在偏房一處隱蔽的房頂上張望省店。 院中可真熱鬧,春花似錦笨触、人聲如沸懦傍。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,912評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽粗俱。三九已至,卻和暖如春虚吟,著一層夾襖步出監(jiān)牢的瞬間寸认,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,040評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工串慰, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留偏塞,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,173評(píng)論 3 370
  • 正文 我出身青樓邦鲫,卻偏偏與公主長(zhǎng)得像灸叼,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子掂碱,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,947評(píng)論 2 355

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