JS系列之如何創(chuàng)建對(duì)象

前言

俗話說(shuō)“在js語(yǔ)言中蒸健,一切都對(duì)象”淮韭,而且創(chuàng)建對(duì)象的方式也有很多種窗慎,所以今天我們做一下梳理

最簡(jiǎn)單的方式

JavaScript創(chuàng)建對(duì)象最簡(jiǎn)單的方式是:對(duì)象字面量形式或使用Object構(gòu)造函數(shù)

對(duì)象字面量形式
var person = new Object();
    person.name = "jack";
    person.sayName = function () {
    alert(this.name)
}
使用Object構(gòu)造函數(shù)
var person = {
    name: "jack";
    sayName: function () {
      alert(this.name)
    }
}

明顯缺點(diǎn):創(chuàng)建多個(gè)對(duì)象時(shí)铺坞,會(huì)出現(xiàn)代碼重復(fù),于是乎职车,‘工廠模式’應(yīng)運(yùn)而生

工廠模式

通俗一點(diǎn)來(lái)理解工廠模式瘫俊,工廠:“我創(chuàng)建一個(gè)對(duì)象,創(chuàng)建的過(guò)程全由我來(lái)負(fù)責(zé)悴灵,但任務(wù)完成后扛芽,就沒(méi)我什么事兒了啊O(∩_∩)O哈哈~”

function createPerson (name) {
  var o = new Object();
  o.name = name;
  o.sayName = function () {
    alert(this.name)
  }
  return o
}

var p1 = new createPerson("jack");

明顯缺點(diǎn):所有的對(duì)象實(shí)例都是Object類型,幾乎類型區(qū)分可言盎鳌川尖!你說(shuō)無(wú)法區(qū)分類型,就無(wú)法區(qū)分啊茫孔,我偏不信叮喳!那咱們就來(lái)看代碼吧:

var p1 = new createPerson("jack");
var p2 = new createPerson("lucy");

console.log(p1 instanceof Object);  //true
console.log(p2 instanceof Object);  //true

你看被芳,是不是這個(gè)理兒;所以為了解決這個(gè)問(wèn)題馍悟,我們采用‘構(gòu)造函數(shù)模式’

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

構(gòu)造函數(shù)模式畔濒,就是這個(gè)函數(shù)我只管創(chuàng)建某個(gè)類型的對(duì)象實(shí)例,其他的我一概不管(注意到?jīng)]有锣咒,這里已經(jīng)有點(diǎn)類型的概念了侵状,感覺(jué)就像是在搞小團(tuán)體嘛)

function Person (name) {
  this.name = name;
  this.sayName = function () {
    alert(this.name)
  }
}

function Animal (name) {
  this.name = name;
  this.sayName = function () {
    alert(this.name)
  }
}

var p1 = new Person("jack")
p1.sayName()  //"jack"

var a1 = new Animal("doudou")
a1.sayName()  //"doudou"

console.log(p1 instanceof Person)  //true
console.log(a1 instanceof Animal)  //true
console.log(p1 instanceof Animal)  //false(p1顯然不是Animal類型,所以是false)
console.log(a1 instanceof Person)  //false(a1也顯然不是Person類型毅整,所以同樣是false)

上面這段代碼證明:構(gòu)造函數(shù)模式的確可以做到對(duì)象類型的區(qū)分趣兄。那么該模式是不是已經(jīng)完美了呢,然而并不是悼嫉,我們來(lái)一起看看下面的代碼:

//接著上面的代碼
console.log(p1.sayName === a1.sayName)  //false

發(fā)現(xiàn)問(wèn)題了嗎艇潭?p1sayName竟然和a1sayName不是同一個(gè),這說(shuō)明什么承粤?說(shuō)明‘構(gòu)造函數(shù)模式’根本就沒(méi)有‘公用’的概念暴区,創(chuàng)建的每個(gè)對(duì)象實(shí)例都有自己的一套屬性和方法闯团,‘屬性是私有的’辛臊,這個(gè)我們可以理解,但方法你都要自己搞一套房交,這就有點(diǎn)沒(méi)必要了
明顯缺點(diǎn):上面已經(jīng)描述了彻舰,為了解決這個(gè)問(wèn)題,又出現(xiàn)了一種新模式‘原型模式’候味,該模式簡(jiǎn)直就是一個(gè)階段性的跳躍刃唤,下面我們來(lái)看分一下‘原型模式’

原型模式

這里要記住一句話:構(gòu)造函數(shù)中的屬性和方法在每個(gè)對(duì)象實(shí)例之間都不是共享的,都是各自搞一套白群;而要想實(shí)現(xiàn)共享尚胞,就要將屬性和方法存到構(gòu)造函數(shù)的原型中。這句話什么意思呢帜慢?下面我們來(lái)詳細(xì)解釋
當(dāng)建立一個(gè)構(gòu)造函數(shù)時(shí)(普通函數(shù)亦然)笼裳,會(huì)自動(dòng)生成一個(gè)prototype(原型),構(gòu)造函數(shù)與prototype是一對(duì)一的關(guān)系粱玲,并且此時(shí)prototype中只有一個(gè)constructor屬性(哪有躬柬,明明還有一個(gè)__proto__呢,這個(gè)我們先不在此討論抽减,后面會(huì)有解釋)

WX20170527-205641@2x.png

這個(gè)constructor是什么允青?它是一個(gè)類似于指針的引用,指向該prototype的構(gòu)造函數(shù)卵沉,并且該指針在默認(rèn)的情況下是一定存在的

console.log(Person.prototype.constructor === Person)  //true

剛才說(shuō)過(guò)prototype自動(dòng)生成的颠锉,其實(shí)還有另外一種手動(dòng)方式來(lái)生成prototype

function Person (name) {
  this.name = name
}
Person.prototype = {
  //constructor: Person,
  age: 30
}
console.log(Person.prototype)  //Object {age: 30}
console.log(Person.prototype.constructor === Person)  //false

Tips:為了證明的確可以為構(gòu)造函數(shù)手動(dòng)創(chuàng)建prototype法牲,這里給prototype加了name屬性。
可能你已經(jīng)注意到了一個(gè)問(wèn)題琼掠,這行代碼:

console.log(Person.prototype.constructor === Person)  //false

結(jié)果為什么是false敖源?大哥眉枕,剛才的prototype是默認(rèn)生成的恶复,然后我們又用了另外一種方式:手動(dòng)設(shè)置。具體分析一下手動(dòng)設(shè)置的原理:
1.構(gòu)造函數(shù)的prototype其實(shí)也是一個(gè)對(duì)象

WX20170527-212325@2x.png

2.當(dāng)我們這樣設(shè)置prototype時(shí)速挑,其實(shí)已經(jīng)將原先Person.prototype給切斷了谤牡,然后又重新引用了另外一個(gè)對(duì)象

WX20170527-212837@2x.png

3.此時(shí)構(gòu)造函數(shù)可以找到prototype,但prototype找不到構(gòu)造函數(shù)了

Person.prototype = {
  //constructor: Person,  // 因?yàn)閏onstructor屬性姥宝,我沒(méi)聲明啊翅萤,prototype就是利用它來(lái)找到構(gòu)造函數(shù)的,你竟然忘了聲明
  age: 30
}

4.所以腊满,要想顯示手動(dòng)設(shè)置構(gòu)造函數(shù)的原型套么,又不失去它們之間的聯(lián)系,我們就要這樣:

function Person (name) {
  this.name = name
}
Person.prototype = {
  constructor: Person,  //constructor一定不要忘了L嫉啊胚泌!
  age: 30
}

畫(huà)外音:“說(shuō)到這里,你還沒(méi)有講原型模式是如何實(shí)現(xiàn)屬性與方法的共享啊”肃弟,不要急玷室,馬上開(kāi)始:

對(duì)象實(shí)例-構(gòu)造函數(shù)-原型,三者是什么樣的關(guān)系呢笤受?

WX20170527-214615@2x.png

WX20170527-215233@2x.png

看明白這張圖的意思嗎穷缤?
1.當(dāng)對(duì)象實(shí)例訪問(wèn)一個(gè)屬性時(shí)(方法依然),如果它自身沒(méi)有該屬性箩兽,那么它就會(huì)通過(guò)__proto__這條鏈去構(gòu)造函數(shù)的prototype上尋找
2.構(gòu)造函數(shù)與原型是一對(duì)一的關(guān)系津肛,與對(duì)象實(shí)例是一對(duì)多的關(guān)系,而并不是每創(chuàng)建一個(gè)對(duì)象實(shí)例汗贫,就相應(yīng)的生成一個(gè)prototype
這就是原型模式的核心所在身坐,結(jié)論:在原型上聲明屬性或方法,可以讓對(duì)象實(shí)例之間共用它們

然后原型模式就是完美的嗎芳绩?并不是掀亥,它有以下兩個(gè)主要問(wèn)題:
問(wèn)題1:如果對(duì)象實(shí)例有與原型上重名的屬性或方法,那么妥色,當(dāng)訪問(wèn)該屬性或方法時(shí)搪花,實(shí)例上的會(huì)屏蔽原型上的

function Person (name) {
  this.name = name
}
Person.prototype = {
  constructor: Person,
  name: 'lucy'
}
var p1 = new Person('jack');
console.log(p1.name);  //jack

問(wèn)題2:由于實(shí)例間是共享原型上的屬性和方法的,所以當(dāng)其中一個(gè)對(duì)象實(shí)例修改原型上的屬性(基本值,非引用類型值或方法時(shí)撮竿,其他實(shí)例也會(huì)受到影響

WX20170527-222024@2x.png

原因就是吮便,當(dāng)實(shí)例自身的基本值屬性與原型上的重名時(shí),實(shí)例就會(huì)創(chuàng)建該屬性幢踏,留著今后自己使用髓需,而原型上的屬性不會(huì)被修改;但如果屬性是引用類型值房蝉,如:Array僚匆、Object,當(dāng)發(fā)生重名時(shí)搭幻,實(shí)例是不會(huì)拷貝一份新的留給自己使用的咧擂,還是堅(jiān)持實(shí)例間共享,所以就會(huì)出現(xiàn)上圖中的情況

以上兩個(gè)問(wèn)題就是原型模式的明顯缺點(diǎn)檀蹋,為了改掉這些缺點(diǎn)松申,我們一般會(huì)采用一種組合模式“組合使用構(gòu)造函數(shù)模式和原型模式”,其實(shí)在原型模式這一節(jié)俯逾,該模式已經(jīng)有所應(yīng)用了

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

這種模式可謂是集構(gòu)造函數(shù)模式和原型模式之所長(zhǎng)贸桶,用構(gòu)造函數(shù)模式來(lái)定義對(duì)象實(shí)例的屬性或方法,而共享的屬性或方法就交給原型模式

function Person (name) {
  this.name = name  //實(shí)例的屬性桌肴,在構(gòu)造函數(shù)中聲明
}

Person.prototype = {
  constructor: Person,
  sayName: function () {  //共享的方法存在原型中
    alert(this.name)
  }
}

注:此模式目前是ECMAScript中使用最廣泛皇筛、認(rèn)同度最高的一種創(chuàng)建自定義類型的方法


下面要介紹的幾個(gè)模式是針對(duì)不同場(chǎng)景的,而不是說(shuō)組合使用構(gòu)造函數(shù)模式和原型模式有什么缺點(diǎn)识脆,又用這幾個(gè)模式來(lái)彌補(bǔ)设联,不是這樣的

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

特點(diǎn):共享的方法是在構(gòu)造函數(shù)中檢測(cè)并聲明的,原型并沒(méi)有被顯示創(chuàng)建

function Person (name) {
  this.name = name;
  if (typeof this.sayName !== 'function') {  //檢查方法是否存在
      console.log('sayName方法不存在')
      Person.prototype.sayName = function () {
      alert(this.name)
    }
  } else {
    console.log('sayName方法已存在')
  }
}

var p1 = new Person('jack');  //'sayName方法不存在'
p1.sayName(); //因?yàn)閟ayName不存在灼捂,我們來(lái)創(chuàng)建它,所以這里輸出'jack'
var p2 = new Person('lucy');  //'sayName方法已存在'
p2.sayName();  //這時(shí)sayName已存在换团,所以輸出'lucy'

當(dāng)Person構(gòu)造函數(shù)第一次被調(diào)用時(shí)悉稠,Person.prototype上就會(huì)被添加sayName方法;《Javascript高級(jí)程序設(shè)計(jì)》一書(shū)說(shuō)到:使用動(dòng)態(tài)原型模式時(shí)艘包,不能使用對(duì)象字面量重寫(xiě)原型的猛。我們來(lái)理解一下:

WX20170529-123047@2x.png

分析:
1.p1實(shí)例創(chuàng)建,此時(shí)原型沒(méi)有sayName方法想虎,那我們就為原型添加一個(gè)
2.隨后卦尊,我們以字面量的形式重寫(xiě)了原型,這時(shí)舊的原型并沒(méi)有被銷毀舌厨,而且它和p1還保持著聯(lián)系
3.之后的實(shí)例岂却,也就是這里的p2,都是與新原型保持聯(lián)系;所以p1躏哩、p2有各自的構(gòu)造器原型署浩,即使它們的構(gòu)造器是同一個(gè)

WX20170529-122827@2x.png

所以切記:當(dāng)我們采用動(dòng)態(tài)原型模式時(shí),千萬(wàn)不要以字面量的形式重寫(xiě)原型

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

了解此模式之前扫尺,我們先來(lái)想一個(gè)問(wèn)題:構(gòu)造函數(shù)為什么要用new關(guān)鍵字調(diào)用筋栋?代碼說(shuō)話:

WX20170530-173851@2x.png
WX20170530-174054@2x.png

我們發(fā)現(xiàn)什么?如果不是new方法調(diào)用構(gòu)造函數(shù)正驻,那么就要顯式的return弊攘,否則構(gòu)造函數(shù)就不會(huì)有返回值;但如果使用new姑曙,那就沒(méi)有這個(gè)問(wèn)題了

下面我們?cè)賮?lái)看寄生構(gòu)造函數(shù)模式:

function Person (name) {
  var o = new Object();
  o.name = name;
  o.sayName = function () {
    alert(this.name)
  };
  return o
}

var p1 = new Person('jack');  //與工廠模式唯一不同之處:使用new調(diào)用
p1.sayName(); //jack

其實(shí)new不new都無(wú)所謂肴颊,因?yàn)槲覀円呀?jīng)顯式的return o

WX20170531-204035@2x.png

那么寄生構(gòu)造函數(shù)模式到底有什么應(yīng)用場(chǎng)景呢?據(jù)《javascript高級(jí)程序設(shè)計(jì)》一書(shū)記載渣磷,舉例:如果我們想創(chuàng)建一個(gè)具有額外方法的特殊數(shù)組婿着,那么我們可以這樣做:

function SpecialArray () {
  var values = new Array();
  Array.prototype.push.apply(values,arguments);
  values.toPipedString = function () {
    return this.join('|')
  }
  return values
}

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

最后重要的一點(diǎn):該模式和構(gòu)造函數(shù)和原型無(wú)緣,也就是不能區(qū)分實(shí)例類型醋界,因?yàn)樵撃J缴傻膶?shí)例竟宋,它的構(gòu)造函數(shù)都是Object,原型都是Object.prototype

WX20170531-205208@2x.png

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

該模式與寄生構(gòu)造函數(shù)相比形纺,主要有兩點(diǎn)不同:
1.創(chuàng)建對(duì)象實(shí)例的方法不引用this
2.不使用new操作符調(diào)用構(gòu)造函數(shù)
按照穩(wěn)妥構(gòu)造函數(shù)的要求丘侠,可以將前面的Person構(gòu)造函數(shù)重寫(xiě)如下:

function Person (name) {
  var o = new Object();
  o.sayName = function () {
    alert(name)  //這里其實(shí)涉及到了閉包的知識(shí),因此產(chǎn)生了私有屬性的概念
  }
  return o
}

此模式最適合在一些安全的環(huán)境中(這些環(huán)境中會(huì)禁止使用this和new)逐样,同理蜗字,此模式與構(gòu)造函數(shù)和原型也無(wú)緣

以上就是對(duì)js中創(chuàng)建對(duì)象的方式的總結(jié),希望對(duì)大家有所幫助
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末脂新,一起剝皮案震驚了整個(gè)濱河市挪捕,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌争便,老刑警劉巖级零,帶你破解...
    沈念sama閱讀 221,695評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異滞乙,居然都是意外死亡奏纪,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)斩启,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)序调,“玉大人,你說(shuō)我怎么就攤上這事兔簇》⒕睿” “怎么了硬耍?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,130評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)朴摊。 經(jīng)常有香客問(wèn)我默垄,道長(zhǎng),這世上最難降的妖魔是什么甚纲? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,648評(píng)論 1 297
  • 正文 為了忘掉前任口锭,我火速辦了婚禮,結(jié)果婚禮上介杆,老公的妹妹穿的比我還像新娘鹃操。我一直安慰自己,他們只是感情好春哨,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,655評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布荆隘。 她就那樣靜靜地躺著,像睡著了一般赴背。 火紅的嫁衣襯著肌膚如雪椰拒。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,268評(píng)論 1 309
  • 那天凰荚,我揣著相機(jī)與錄音燃观,去河邊找鬼。 笑死便瑟,一個(gè)胖子當(dāng)著我的面吹牛缆毁,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播到涂,決...
    沈念sama閱讀 40,835評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼脊框,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了践啄?” 一聲冷哼從身側(cè)響起浇雹,我...
    開(kāi)封第一講書(shū)人閱讀 39,740評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎往核,沒(méi)想到半個(gè)月后箫爷,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,286評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡聂儒,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,375評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了硫痰。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片衩婚。...
    茶點(diǎn)故事閱讀 40,505評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖效斑,靈堂內(nèi)的尸體忽然破棺而出非春,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布奇昙,位于F島的核電站护侮,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏储耐。R本人自食惡果不足惜羊初,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,873評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望什湘。 院中可真熱鬧长赞,春花似錦、人聲如沸闽撤。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,357評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)哟旗。三九已至贩据,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間闸餐,已是汗流浹背饱亮。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,466評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留绎巨,地道東北人近尚。 一個(gè)月前我還...
    沈念sama閱讀 48,921評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像场勤,于是被迫代替她去往敵國(guó)和親戈锻。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,515評(píng)論 2 359

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