Javascript創(chuàng)建對(duì)象的的方式

對(duì)象:即是一系列屬性和方法的集合。

  • 1.字面量方式
  • 2.工廠模式
  • 3.構(gòu)造函數(shù)
  • 4.原型模式
  • 5.組合使用構(gòu)造函數(shù)和原型模式
  • 6.動(dòng)態(tài)原型模式

以上六種方式是javascript創(chuàng)建對(duì)象的方法,每種方法都有自己的特點(diǎn),下面是對(duì)每種方式的具體介紹。

1.字面量方式

    var obj = {};
        obj.name = "tom";
        obj.age = 18;
        obj.say = function(){
            console.log("my name is "+this.name);
    };

上面這種字面量創(chuàng)建對(duì)象的方式,是我們?cè)诰帉?code>Javascript代碼時(shí),經(jīng)常用來創(chuàng)建對(duì)象的方式。它和以下代碼是等價(jià)的。

    var obj = new Object();
        obj.name = "tom";
        obj.age = 18;
        obj.say = function(){
            console.log("my name is "+this.name);
    };

使用字面量方式創(chuàng)建對(duì)象,顯示很方便,當(dāng)我們需要時(shí),隨時(shí)創(chuàng)建即可。但是它們只適合創(chuàng)建少量的對(duì)象。當(dāng)我們需要?jiǎng)?chuàng)建很多對(duì)象時(shí),顯然這種方式是不實(shí)用的。

2.工廠模式

當(dāng)我們需要?jiǎng)?chuàng)建出多個(gè)對(duì)象的時(shí)候,通過字面量方式創(chuàng)建對(duì)象的方法顯然是不合理的。我們希望有這樣一個(gè)容器,當(dāng)我們需要?jiǎng)?chuàng)建對(duì)象時(shí),我們只需將數(shù)據(jù)扔進(jìn)去,返回給我們的就是一個(gè)個(gè)對(duì)象,雖然對(duì)象的屬性值不盡相同,但是他們都擁有共同的屬性。工廠模式就是為我們提供這個(gè)服務(wù)的。

    function Factory(name,age){
        var obj = {};
        obj.name = name;
        obj.age = age;
        obj.say = function(){
            console.log("my age is "+this.age+"old");
        }
        return obj;//創(chuàng)建的對(duì)象以返回值的方式返回給我們。
    }

當(dāng)我們需要?jiǎng)?chuàng)建對(duì)象的時(shí)候,我們將對(duì)象的私有數(shù)據(jù)扔進(jìn)Factory這個(gè)機(jī)器工廠即可

代碼示例:

    var person1 = Factory("jack",18);
    var person2 = Factory("mark",22);

工廠方式的問題:使用工廠模式能夠創(chuàng)建一個(gè)包含所有信息的對(duì)象,可以無數(shù)次的調(diào)用的這個(gè)函數(shù)。雖然其解決了創(chuàng)建多個(gè)相似對(duì)象的問題,但卻沒有解決對(duì)象識(shí)別的問題,因?yàn)閯?chuàng)建出來的對(duì)象始終是個(gè)Object實(shí)例(即如何得知一個(gè)對(duì)象的類型)。

    console.log(person1.constructor);//object()
    console.log(person1 instanceof Factory)//false

對(duì)象的constructor屬性,指向創(chuàng)建該對(duì)象的構(gòu)造函數(shù)。
instanceof是用來判斷一個(gè)對(duì)象是否為一個(gè)構(gòu)造函數(shù)的實(shí)例。

3.構(gòu)造函數(shù)

在上文中,我們可以通過 new Object() 來實(shí)例化對(duì)象,Object()Array()Date()都是Javascript為我們提供的原生構(gòu)造函數(shù),我們可以通過這些構(gòu)造函數(shù)來創(chuàng)建出不同類型的對(duì)象。

    var arr = new Array();//創(chuàng)建一個(gè)數(shù)組對(duì)象
    var date= new Date();//創(chuàng)建一個(gè)時(shí)間對(duì)象

除了JS為我們提供的原生構(gòu)造函數(shù),我們也可以自己創(chuàng)建一個(gè)構(gòu)造函數(shù)。

    function Friend(name,age,job){
        this.name = name;
        this.age = age;
        this.job = job;
        this.introduce = function(){
            console.log("my name is "+this.name+",my job is "+this.job);
        };
    };
    
    var friend1 = new Friend("Bob",17,"student");
    var friend2 = new Friend("Alens",25,"teacher");
    console.log(friend1.introduce==friend2.introduce)//false

構(gòu)造函數(shù)的特點(diǎn)

  • 1.函數(shù)名首字母大寫,從而區(qū)別與普通的函數(shù)。
  • 2.在構(gòu)造函數(shù)中使用this關(guān)鍵字
  • 3.創(chuàng)建對(duì)象的時(shí)候使用new關(guān)鍵字

當(dāng)我們通過new+構(gòu)造函數(shù)實(shí)例化一個(gè)對(duì)象的時(shí)候,實(shí)際上代碼會(huì)執(zhí)行以下操作。

    var friend1 = new Friend("Bob",17,"student");;
    //1.首先會(huì)先創(chuàng)建一個(gè)實(shí)例化對(duì)象friend1
    //2.然后將構(gòu)造函數(shù)Friend的作用域賦給該對(duì)象(this即指向該對(duì)象),也就相當(dāng)于為該對(duì)象添加以下屬性
    friend1.name = "Bob";
    friend1.age = 17;
    firend1.job = "student";
    //3.最后將該對(duì)象返回

通過構(gòu)造函數(shù)創(chuàng)建出來的對(duì)象,一眼看上去,感覺和工廠模式創(chuàng)建對(duì)象并沒有什么區(qū)別,但是通過這種方式創(chuàng)建出來的對(duì)象,我們可以明確的判斷創(chuàng)建該對(duì)象的構(gòu)造函數(shù)。

    console.log(friend1.constructor);//function Friend()
    console.log(friend1 instanceof Friend)//true

通過構(gòu)造函數(shù)創(chuàng)建對(duì)象的方式,既解決了字面量方式創(chuàng)建對(duì)象的局限性(無法適應(yīng)創(chuàng)建多個(gè)對(duì)象),也解決了工廠模式創(chuàng)建出來對(duì)象的不可識(shí)別類型的欠缺性。但是,當(dāng)我們?cè)谕ㄟ^構(gòu)造函數(shù)創(chuàng)建多個(gè)對(duì)象的時(shí)候,雖然他們的屬性是不同的,但是這些對(duì)象卻擁有共同的方法introduce,這樣的話,創(chuàng)建多少個(gè)對(duì)象,就會(huì)有多少個(gè)方法。這樣的話,顯得有些多余,并且對(duì)計(jì)算機(jī)內(nèi)存也不利。

使用構(gòu)造函數(shù)的主要問題,就是每個(gè)方法都要在每個(gè)實(shí)例上重新創(chuàng)建一遍。——《JavaScript高級(jí)程序設(shè)計(jì)(第3版)》

我們可以將這些對(duì)象共有的方法,定義在構(gòu)造函數(shù)外面,有一個(gè)變量來引用該方法。這樣的話,創(chuàng)建出來的每一個(gè)對(duì)象,都可以享用該方法。

function Friend(name, age, job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.introduce = introduce; 
}  
function introduce(){
     console.log("my name is "+this.name+",my job is "+this.job)
} 
var person1 = new Friend("Bob",17,"student");
var person2 = new Friend("Alens",25,"teacher"); 
console.log(person1.introduce === person2.introduce);  //true

雖然這種做法,可以解決內(nèi)存消耗,實(shí)現(xiàn)多個(gè)對(duì)象共享一個(gè)方法,但是顯然這種做法是不利于整體代碼的閱讀性。并且,構(gòu)造函數(shù)也被硬生生的拆開了。

4.原型模式

原型模式,是Javascript中的一種設(shè)計(jì)模式。指的是每一個(gè)構(gòu)造函數(shù),都有自己的一個(gè)原型對(duì)象prototype,而通過該構(gòu)造函數(shù)創(chuàng)建出來的對(duì)象,都有一個(gè)屬性__proto__,該屬性指向創(chuàng)建該對(duì)象的構(gòu)造函數(shù)的原型對(duì)象。即__proto__指向構(gòu)造函數(shù)的prototype對(duì)象。正是這一連接賦予了JS對(duì)象屬性的動(dòng)態(tài)搜索特性:如果在對(duì)象本身找不到某個(gè)屬性,那么就會(huì)通過這個(gè)連接到其原型對(duì)象中去找。

示例代碼:


//先聲明一個(gè)無參數(shù)、無內(nèi)容的“空”構(gòu)造函數(shù)
function Person() {
}

//使用對(duì)象字面量語法重寫Person的原型對(duì)象
Person.prototype = {
    name: 'jack',
    age: '25',
    job: 'front-end web developer',
    sayName: function () {
       console.log(this.name);
    }
};

//因?yàn)樯厦媸褂脤?duì)象字面量的方式完全重寫了原型對(duì)象,
//導(dǎo)致初始原型對(duì)象(Person.prototype)與構(gòu)造函數(shù)(Person)之間的聯(lián)系(constructor)被切斷,
//因此需要手動(dòng)進(jìn)行連接
Object.defineProperty(Person.prototype, 'constructor', {
    enumerable: false,
    value: Person
});

//測(cè)試
var person1 = new Person();
var person2 = new Person();
person2.name = 'Faker';//將person2對(duì)象的名字改為自身的名字
console.log(person1.name);    //'jack'
console.log(person2.name);    //'Faker'
console.log(person1.sayName());    //'jack'
console.log(person2.sayName());    //'Faker'
console.log(person1.sayName === person2.sayName);    //true

可以看到,通過原型模式,我們同樣可以輕松地創(chuàng)建對(duì)象,而且可以像“繼承”一般得到我們?cè)谠蛯?duì)象中定義的默認(rèn)屬性,在此基礎(chǔ)上,我們也可以對(duì)該對(duì)象隨意地添加或修改屬性及值。此外,通過上面最后一句測(cè)試代碼還可以看出,其函數(shù)實(shí)現(xiàn)了完美的引用共享,從這一點(diǎn)上來說,原型模式真正解決了構(gòu)造函數(shù)模式不能共享內(nèi)部方法引用的問題。

原型模式看起來不錯(cuò),不過它也不是沒有缺點(diǎn)。第一,它不像構(gòu)造函數(shù)模式那樣,初始化時(shí)即提供參數(shù),這使得所有新創(chuàng)建的實(shí)例在一開始時(shí)長(zhǎng)得一模一樣;第二,封裝性欠佳;第三,對(duì)于包含引用類型值的屬性,會(huì)導(dǎo)致不應(yīng)該出現(xiàn)的屬性共享。

對(duì)于第三個(gè)缺點(diǎn),用代碼更能說明問題:

function Person() {
}
Person.prototype = {
    constructor: Person,    //這樣恢復(fù)連接會(huì)導(dǎo)致該屬性的[[Enumerable]]特性變?yōu)閠rue。上面的Object.defineProperty()才是完美寫法。
    name: 'Chuck',
    age: '25',
    job: 'Software Engineer',
    friends: ['Frank', 'Lambert'],
    sayName: function () {
        console.log(this.name);
    }
};
var person1 = new Person();
var person2 = new Person();
person1.friends.push('Lily');
console.log(person1.friends);    //["Frank", "Lambert", "Lily"]
console.log(person2.friends);    //["Frank", "Lambert", "Lily"]

一般而言,我們都希望各個(gè)對(duì)象各有各的屬性和值,相互沒有影響。可像上面示例一樣,原型模式共享了不應(yīng)該共享的屬性,這絕對(duì)不會(huì)是我們想要的結(jié)果

5.組合使用構(gòu)造函數(shù)和原型模式

通過對(duì)上面幾種創(chuàng)建對(duì)象方法的分析,我們希望是否有一種方式,能夠在快速創(chuàng)建多個(gè)對(duì)象的同時(shí),讓這些對(duì)象既擁有自己的私有屬性,也擁有一些共有的方法。同時(shí),也不破壞構(gòu)造函數(shù)創(chuàng)建對(duì)象的純粹性。

代碼示例:

    function Person(name, age, job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.friends = ['Frank', 'Lambert'];
}
Person.prototype = {
    constructor: Person,
    sayName: function(){
        console.log(this.name)
    }
};
var person1 = new Person('tom', 25, 'Software Engineer');
var person2 = new Person('cindy', 18, 'doctor'); 
person1.friends.push('Lily');
console.log(person1.friends);    //["Frank", "Lambert", "Lily"]
console.log(person2.friends);    //["Frank", "Lambert"]
console.log(person1.sayName === person2.sayName);    //true

通過這種方式創(chuàng)建出來的對(duì)象,使得對(duì)象實(shí)例擁有自己可完全支配的全部屬性,同時(shí)還共享了方法引用以節(jié)省內(nèi)存開銷。

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

到上一步的“組合模式”為止,就功能和性能上而言可以說已經(jīng)達(dá)到我們的要求了,現(xiàn)在我們考慮是否可以對(duì)代碼進(jìn)一步優(yōu)化,畢竟“組合模式”有兩段代碼,起碼封裝性看起來不夠好。

我們把需要共享的函數(shù)引用通過原型封裝在構(gòu)造函數(shù)中,在調(diào)用構(gòu)造函數(shù)初始化對(duì)象實(shí)例的同時(shí)將該函數(shù)追加到原型對(duì)象中。當(dāng)然,為了避免重復(fù)定義,需要加一個(gè)if判斷。代碼如下:

function Person(name, age, job, friends){
    this.name = name;
    this.age =  age;
    this.job = job;
    this.friends = friends;
    if (typeof Person.prototype.sayName !== 'function') {
        Person.prototype.sayName = function(){
            return this.name;
        }
    }
}

var person1 = new Person('chuck', 25, 'Software Engineer', ['A','B','C']);
console.log(person1.sayName());    //'chuck'

以上六種創(chuàng)建對(duì)象的方式,是我們?cè)诰帉懘a時(shí),經(jīng)常用到的方法,具體要用哪中方法,還是要根據(jù)實(shí)際情況決定的。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,527評(píng)論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,687評(píng)論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,640評(píng)論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,957評(píng)論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,682評(píng)論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,011評(píng)論 1 329
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,009評(píng)論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,183評(píng)論 0 290
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,714評(píng)論 1 336
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,435評(píng)論 3 359
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,665評(píng)論 1 374
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,148評(píng)論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,838評(píng)論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,251評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,588評(píng)論 1 295
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,379評(píng)論 3 400
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,627評(píng)論 2 380

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