004 創(chuàng)建對(duì)象之工廠(chǎng)模式和構(gòu)造函數(shù)模式

JavaScript 中創(chuàng)建對(duì)象的方式有很多,比如對(duì)象字面量模式或者使用 Object 創(chuàng)建:

// 創(chuàng)建 obj1 對(duì)象
let obj1 = {
  name:"",
  showName(){ return this.name }
}

// 創(chuàng)建 obj2 對(duì)象
let obj2 = new Object()
obj2.name = ""
obj2.showName = function(){ return this.name }

使用這兩種方式(特別是對(duì)象字面量方式)創(chuàng)建對(duì)象十分方便,可以拿來(lái)即用。但也有一些缺點(diǎn):

  • 過(guò)程過(guò)于繁瑣,如果需要?jiǎng)?chuàng)建多個(gè)對(duì)象,就需要書(shū)寫(xiě)多次創(chuàng)建代碼
  • 封裝性不夠,因?yàn)榘凑粘R?guī)理念, 對(duì)象應(yīng)該由一個(gè)公共的接口(類(lèi)、函數(shù))來(lái)進(jìn)行統(tǒng)一創(chuàng)建,需要?jiǎng)?chuàng)建對(duì)象時(shí),直接初始化某個(gè)類(lèi)或者調(diào)用函數(shù)來(lái)進(jìn)行創(chuàng)建。

上面的兩個(gè)缺陷都指向了一點(diǎn):我們需要一個(gè)函數(shù)(類(lèi))來(lái)進(jìn)行對(duì)象創(chuàng)建,基于這個(gè)理念,出現(xiàn)了使用工廠(chǎng)模式來(lái)創(chuàng)建對(duì)象的方式。

工廠(chǎng)模式

工廠(chǎng)模式很簡(jiǎn)單,對(duì)原料(原生 Object 對(duì)象)進(jìn)行一些加工(參數(shù)),然后返回一個(gè)產(chǎn)品(被加工后的對(duì)象)。

function createPerson(name,age){
  // 創(chuàng)建一個(gè)原生對(duì)象
  let o = new Object()
  o.name = name;
  o.age = age
  return o;
}

如果我們需要?jiǎng)?chuàng)建某個(gè)對(duì)象,只需調(diào)用相應(yīng)的工廠(chǎng)函數(shù):

let p1 = createPerson("MIKE",20)
let p2 = createPerson("JACK",22)

工廠(chǎng)模式的缺點(diǎn)

工廠(chǎng)模式的解決了批量創(chuàng)建對(duì)象的問(wèn)題,但也有一個(gè)明顯的缺點(diǎn):沒(méi)有“類(lèi)”的概念,除了能夠批量創(chuàng)建對(duì)象,無(wú)法對(duì)這些對(duì)象進(jìn)行判斷,無(wú)法知道這些對(duì)象是由誰(shuí)(類(lèi))創(chuàng)建出來(lái)的。基于這個(gè)問(wèn)題,出現(xiàn)了構(gòu)造函數(shù)模式。

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

函數(shù)是 JavaScript 中的一等公民,可以做很多事情,其中有一項(xiàng)功能就是可以被 new 操作符調(diào)用。在 ES6 之前,JavaScript 中是沒(méi)有 class 關(guān)鍵字的,于是有了通過(guò) new 操作符來(lái)調(diào)用函數(shù)創(chuàng)建一個(gè)對(duì)象的方式,很明顯,通過(guò) new 操作符調(diào)用的函數(shù)就是所謂的“類(lèi)”。

function Person(name,age){
  this.name = name;
  this.age = age;
  this.intro = function(){
    console.log(`name:${this.name},age:${this.age}`)
  }
}

接下來(lái)通過(guò) Person 類(lèi)來(lái)創(chuàng)建對(duì)象:

let p1 = new Person("MIKE",20)
let p2 = new Person("JACK","22")

構(gòu)造函數(shù)中的 this 關(guān)鍵字就指向了當(dāng)前被創(chuàng)建的對(duì)象。
通過(guò)這種方式創(chuàng)建對(duì)象以后,我們就可以知道對(duì)象是被哪個(gè)“類(lèi)”創(chuàng)建的了。

p1 instanceof Person //true
p1 instanceof Object //true
p1.constructor === Person //true

當(dāng)對(duì)象被創(chuàng)建后,其會(huì)擁有一個(gè) constructor 屬性,指向其的構(gòu)造函數(shù)。不過(guò)由于 JavaScript 太靈活了,constructor 屬性是可以被修改的,因此通過(guò) constructor 來(lái)對(duì)對(duì)象的類(lèi)進(jìn)行判斷是不準(zhǔn)確的,使用
instanceof 操作符更加可靠。

p1.constructor = Array
p1.constructor // Array
p1.constructor === Person //false
p1 instanceof Person // true

構(gòu)造函數(shù)創(chuàng)建對(duì)象的流程

使用構(gòu)造函數(shù)創(chuàng)建對(duì)象,大概有如下幾個(gè)流程:

  • 創(chuàng)建一個(gè)新對(duì)象
  • 將構(gòu)造函數(shù)的作用域賦值給這個(gè)對(duì)象(因此 this 就指向了這個(gè)對(duì)象)
  • 執(zhí)行構(gòu)造函數(shù)中的代碼,為對(duì)象添加屬性方法
  • 函數(shù)執(zhí)行完畢,對(duì)象被銷(xiāo)毀

構(gòu)造函數(shù)作為函數(shù)

構(gòu)造函數(shù)本身也是函數(shù),因此其可以作為函數(shù)調(diào)用,由于構(gòu)造函數(shù)中使用 this 關(guān)鍵字,我們可以通過(guò)構(gòu)造函數(shù)為某個(gè)對(duì)象進(jìn)行賦值。this 為哪個(gè)對(duì)象賦值,決定于這個(gè)函數(shù)執(zhí)行時(shí)的上下文。

function Person(name,age){
  this.name = name;
  this.age = age;
  this.intro = function(){
    console.log(`name:${this.name},age:${this.age}`)
  }
}

let p1 = Person("MIKE","20")
p1 //undefined
window.name //"MIKE"
window.age //"20"

如果不指定函數(shù)的上下文,默認(rèn)為 window 對(duì)象,因此 Person 函數(shù)執(zhí)行時(shí),為 window 對(duì)象添加了屬性和方法。

let o = {}
Person.call(o,"JACK","22")
o //{name:"JACK",age:"22"}

這里明確指定函數(shù)的上下文為對(duì)象 o 后,調(diào)用 Person 函數(shù)就為 o 對(duì)象添加屬性方法了。

構(gòu)造函數(shù)模式的問(wèn)題

使用構(gòu)造函數(shù)模式創(chuàng)建對(duì)象看起來(lái)是個(gè)不錯(cuò)的方式,但這樣有沒(méi)有缺陷呢?也是有的。這個(gè)缺陷就在于每次使用構(gòu)造函數(shù)創(chuàng)建對(duì)象時(shí),都會(huì)為每個(gè)對(duì)象重新創(chuàng)建一份屬性和方法的副本,無(wú)法實(shí)現(xiàn)復(fù)用。
為什么會(huì)這樣呢?因?yàn)闃?gòu)造函數(shù)本質(zhì)也是一個(gè)函數(shù),函數(shù)在運(yùn)行時(shí)會(huì)有一個(gè)獨(dú)立的作用域,創(chuàng)建多個(gè)對(duì)象時(shí)會(huì)多次調(diào)用構(gòu)造函數(shù),并把這些函數(shù)的作用域賦值給對(duì)象,然后為對(duì)象添加屬性。但是這些函數(shù)的作用域是獨(dú)立的,因此我們?cè)跇?gòu)造函數(shù)體內(nèi)所做的任何變量聲明、函數(shù)聲明,都會(huì)在運(yùn)行時(shí)重新創(chuàng)建一次,然后添加到對(duì)象上。

function Person(name,age){
  this.name = name;
  this.age = age;
  this.intro = function(){
    console.log(`name:${this.name},age:${this.age}`)
  }
}

let p1 = new Person("MIKE","20")
let p2 = new Person("JACK","22")
p1.intro === p2.intro // false

以上就是使用構(gòu)造函數(shù)創(chuàng)建對(duì)象無(wú)法實(shí)現(xiàn)復(fù)用的原因,既然無(wú)法復(fù)用的原因是由于在獨(dú)立作用于中創(chuàng)建變量和函數(shù),那我們把這些變量和函數(shù)放到函數(shù)的獨(dú)立作用于之外不就可以了?確實(shí)如此。

function Person(name,age){
  this.name = name
  this.age = age
  this.intro = intro
}

function intro(){
  console.log(`name:${this.name},age:${this.age}`)
}


let p1 = new Person("MIKE","20")
let p2 = new Person("JACK","22")
p1.intro === p2.intro // true

上面的 intro 方法就實(shí)現(xiàn)了代碼復(fù)用,節(jié)約了資源。但這種方式也是有問(wèn)題的:增加了全局變量,而且破壞了封裝性。后面將會(huì)介紹更多創(chuàng)建對(duì)象的方法,一步步進(jìn)行完善。

完。

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

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

  • 普通創(chuàng)建對(duì)象和字面量創(chuàng)建對(duì)象不足之處:雖然 Object 構(gòu)造函數(shù)或?qū)ο笞置媪慷伎梢杂脕?lái)創(chuàng)建單個(gè)對(duì)象,但這些方式有...
    believedream閱讀 2,414評(píng)論 2 18
  • 博客內(nèi)容:什么是面向?qū)ο鬄槭裁匆嫦驅(qū)ο竺嫦驅(qū)ο缶幊痰奶匦院驮瓌t理解對(duì)象屬性創(chuàng)建對(duì)象繼承 什么是面向?qū)ο?面向?qū)ο?..
    _Dot912閱讀 1,445評(píng)論 3 12
  • 設(shè)計(jì)模式匯總 一、基礎(chǔ)知識(shí) 1. 設(shè)計(jì)模式概述 定義:設(shè)計(jì)模式(Design Pattern)是一套被反復(fù)使用、多...
    MinoyJet閱讀 3,961評(píng)論 1 15
  • 于2017年四月份加入實(shí)驗(yàn)室學(xué)習(xí)中,以下是我對(duì)實(shí)驗(yàn)室管理方面的一些建議,可能不太成熟,望指點(diǎn)。 學(xué)習(xí)管理1 發(fā)布學(xué)...
    QinRenMin閱讀 704評(píng)論 2 0
  • 做夢(mèng)的時(shí)候,清清楚楚知道自己正在做夢(mèng),也有些人剛好相反,以為自己醒來(lái),其實(shí)還在做夢(mèng)。
    大方demi閱讀 244評(píng)論 1 2