Javascript中創(chuàng)建對象的幾種方式

一、一般來說诵叁,最簡單的創(chuàng)建方式就是字面量方式

var animal = {
  name:'animal',
  speed:12雁竞,
  run:function(){ 
       alert(this.speed)       
     }
}

或是創(chuàng)建一個Object實例,再給他添加屬性和方法

var animal = new Object()
animal.name = 'animal'
animal.speed = '12'
animal.run = function(){
  alert(this.speed)    
}

缺點:很明顯黎休,當(dāng)需要創(chuàng)建多個對象時浓领,會有大量的代碼重寫出現(xiàn),為了解決這個問題势腮,工廠模式是個很好的辦法联贩。

二、 工廠模式

用函數(shù)封裝實現(xiàn)創(chuàng)建對象具體細(xì)節(jié)的接口捎拯,如:

function createAnimal(name,speed){
    var obj = new Object()
    obj.name = 'animal'
    obj.speed = '12'
   obj.run = function(){
      alert(this.speed)    
    }
return obj
}

函數(shù)createAnimal 根據(jù)傳入的具體參數(shù)來構(gòu)建一個包含必要信息的animal對象泪幌,每次調(diào)用該函數(shù),只需要傳入必要的參數(shù)即可署照。
缺點:每個創(chuàng)建出來的對象的類型都是Object祸泪,不能判斷其歸屬對象

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

創(chuàng)建自定義的構(gòu)造函數(shù)建芙,然后可以設(shè)置自定義對象類型的屬性和方法没隘,如,

function Animal(name,speed){
    this.name = name
    this.speed = speed
    this.run = function(){
        alert(this.speed)
    }
}
var animal1 = new Animal('老虎',120)

注:構(gòu)造函數(shù)以大寫字母開頭,非構(gòu)造函數(shù)以小寫字母開頭
新創(chuàng)建的對象都會有一個constructor(構(gòu)造函數(shù))屬性禁荸,用來標(biāo)明它的對象類型

alert(animal1.constructor == Animal)//true

但檢驗對象類型時右蒲,一般用instanceof操作符

alert(animal1 instanceof Object)//true
alert(animal1 instanceof Animal)//true

這說明例子里創(chuàng)建出來的對象既是Object的實例,又是Animal的實例
缺點:每次實例化時赶熟,實例上的方法就會重新創(chuàng)建一遍瑰妄。相當(dāng)于每次都創(chuàng)建一個Function的實例,然而創(chuàng)建多個完成相同任務(wù)的Function實例完全沒有必要映砖,所以我們可以吧函數(shù)的定義放到構(gòu)造函數(shù)外面來解決這個問題间坐。

function Animal(name,speed){
    this.name = name
    this.speed = speed
    this.run = run
}
function run(){
  alert(this.speed)
}

將run函數(shù)的定義設(shè)置在構(gòu)造函數(shù)之外,也就是全局定義下的函數(shù)邑退,由于run函數(shù)里包含了一個指向函數(shù)的指針竹宋,解決了多個函數(shù)做一件事的問題,擔(dān)心的問題又來了地技,雖然run函數(shù)定義在全局逝撬,但他只應(yīng)該被Animal調(diào)用,應(yīng)該是局部的函數(shù)乓土,但現(xiàn)在其他的對象函數(shù)都可以調(diào)用它,這就暴露了構(gòu)造函數(shù)的內(nèi)部方法,毫無封裝性可言浑玛,原型模式正好可言解決這個問題昼伴。

四呀癣、原型模式

我們每次創(chuàng)建的函數(shù)都會有一個默認(rèn)prototype(原型)屬性,它是一個指向原型對象的指針尽棕,原型對象就是包含構(gòu)造函數(shù)里所有實例共享的屬性和方法的對象。所以通過使用原型對象可以實現(xiàn)屬性和方法的共用彬伦,例:

function Animal(){
}
Animal.prototype.name = "老虎"
Animal.prototype.speed = "120"
Animal.prototype.run = function(){
    alert(this.speed)
}
var  animal1 = new Animal()
animal1.run()//120
var animal2 = new Animal()
animal.run()//120

以之前的Animal構(gòu)造函數(shù)和Animal.prototype創(chuàng)建實例的代碼為例滔悉,下圖展現(xiàn)了各個對象之間的關(guān)系。


image

簡單來說单绑,在創(chuàng)建了自定義的構(gòu)造函數(shù)后回官,該函數(shù)會自動獲得一個prototype屬性,這個屬性指向的就是它的原型對象搂橙。而在默認(rèn)情況下歉提,所有的原型對象也都會自動獲得一個constructor(構(gòu)造函數(shù))屬性,該屬性指向的是該原型對象的構(gòu)造函數(shù)区转。即Animal.prototype.constructor = Animal

判斷一個屬性或是方法是否存在于原型當(dāng)中(isPrototypeOf())

alert(Animal.prototype.isPrototypeOf(animal1))//true
ES5中增加了個新方法苔巨,Object.getPrototypeOf()用于獲得一個對象的原型
alert(Object.getPrototypeOf(animal1) == Animal.prototype)// true
alert(Object.grtPrototypeOf(animal1).name) //'老虎'

如果我們需要修改原型中的值,通過實例重寫是不可以的废离,我們只會在實例中創(chuàng)建一個名稱相同的屬性侄泽,同時屏蔽掉原型中的那個屬性,我們可以試著去驗證看看蜻韭,

var animal1 = new Animal()
var animal2 = new Animal()
animal1.name = "獅子"
alert(animal1.name)''獅子" --實例中新增的屬性
alert(animal2.name)// "老虎" -- 原型中的屬性

所以很自然的我們可以發(fā)現(xiàn)實例屬性讀值的順序:先在實例屬性當(dāng)中搜索該屬性悼尾,如果存在,就返回該值湘捎;如果沒有诀豁,就去實例的原型當(dāng)中搜索。

所以如果我們需要獲取原型當(dāng)中的屬性窥妇,那么實例中就不可以有該屬性舷胜,即便該屬性的值設(shè)為null,但這個屬性依然存在于實例當(dāng)中活翩,我們也無法訪問原型中的該屬性烹骨。delete操作符可以徹底刪除實例中的該屬性,我們也就可以自然的訪問到原型當(dāng)中的該屬性了材泄。例:繼上面例子

delete animal1.name
alert(animal1.name)// "老虎"--原型當(dāng)中的屬性

那當(dāng)我們獲得了一個屬性時沮焕,我們想知道它是來自實例還是來自原型時,該怎么判斷呢拉宗?

hasOwnProperty()方法就可以做到峦树,它可以檢測一個屬性是來自實例還是原型辣辫,只有當(dāng)給定屬性來自于對象的實例中它才回返回true。

var animal1 = new Animal()
var animal2 = new Animal()
alert(animal1.hasOwnProperty("name"))//false
animal1.name = "獅子"
alert(animal1.name)''獅子" --實例中新增的屬性
alert(animal1.hasOwnProperty("name"))//true
alert(animal2.name)// "老虎" -- 原型中的屬性
alert(animal2.hasOwnProperty("name"))//false

但問題來了魁巩,該怎么判斷一個屬性是否是存在于原型鏈當(dāng)中的呢急灭?僅憑hasOwnProperty()返回值為false并不能判斷,因為這個屬性可能既不在實例也不在原型當(dāng)中谷遂。
我們可以使用in操作符葬馋,in操作符會在通過對象能夠訪問到給定屬性時返回true,無論該對象是否存在于實例中還是原型中肾扰。使用hasOwnProperty()方法和in操作符畴嘶,就可以確定該屬性是否存在于原型當(dāng)中,

function hasPrototypeProperty(obj,name){
    return ?obj.hasOwnPreperty(name) && (name in obj)
}

原型 -- 字面量創(chuàng)建

在原型創(chuàng)建對象時集晚,我們可以發(fā)現(xiàn)每次添加屬性和方法時窗悯,都要重復(fù)敲Animal.prototype。為了減少代碼重復(fù)量(省事)甩恼,也從視覺上更好的封裝原型的功能蟀瞧,我們可以用一個包裝了所有屬性和方法的對象字面量來重寫整個原型對象,如:

function Animal(){
}
Animal.prototype = {
    name : "老虎",
    speed:120,
    run : function(){
        alert(this.speed)
   }
}

雖然我們將Animal.prototype設(shè)置為等于一個以字面量形式創(chuàng)建的新對象条摸,最終結(jié)果相同悦污,但有一個問題,constructor不再指向Animal钉蒲,而是Object切端。我們可以手動設(shè)置constructor屬性

Animal.prototype = {
    constructor:Animal,
    name : "老虎",
    speed:120,
    run : function(){
        alert(this.speed)
   }
}

缺點:①沒有為構(gòu)造函數(shù)傳遞參數(shù)初始化顷啼,所有的實例默認(rèn)獲得相同的屬性值
②共享特性踏枣,由于所有的原型屬性都被實例共享,如果某個實例需要更改其中一個屬性钙蒙,其他所有實例的該屬性值也會被修改茵瀑,就不一定是我們想要的結(jié)果了。

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

構(gòu)造函數(shù)模式用于定義實例屬性马昨,原型模式用于定義共享的方法和屬性,這樣每個實例都有自己單獨的實例屬性扛施,同時又有對共享方法屬性的引用鸿捧。

function Animal(name,speed){
    this.name = name
    this.speed = speed
    friends = ["one","two"]
    this.run = function(){
        alert(this.speed)
    }
}
Animal.prototype = {
    constructor : Animal
    family:["tom","jery"]
}
var animal1 = new Animal("老虎",120)
var animal2 = new Animal("獅子",130)
animal1.frirends.push("three")
alert(animal1.friends)// ["one","two","three"]
alert(animal1.friends)// ["one","two"]

動態(tài)原型模式

構(gòu)造函數(shù)初次調(diào)用時,通過判斷某個屬性或方法是否存在疙渣,來決定是否需要初始化原型

function Animal(name,speed){
   this.name = name
   this.speed = speed
   friends = ["one","two"]
   if(typeof this.run != 'function'){
       Animal.prototype.run= function(){
           alert(this.speed)
       }
   }
}

七匙奴、寄生構(gòu)造函數(shù)

除了使用new操作盒并把包裝函數(shù)稱為構(gòu)造函數(shù)外,該模式和工廠模式一模一樣妄荔,他的作用也僅僅是封裝創(chuàng)建對象的代碼泼菌。

function Animal(name,speed){
    var obj = new Object()
   obj.name = name
   obj.speed = speed
   obj.run = function(){
        alert(this.speed)
    }
return obj
}

注:寄生構(gòu)造函數(shù)模式返回的對象與構(gòu)造函數(shù)和構(gòu)造函數(shù)的原型沒有關(guān)系谍肤,所以不能依靠instanceof操作符來確定對象類型

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市灶轰,隨后出現(xiàn)的幾起案子谣沸,更是在濱河造成了極大的恐慌,老刑警劉巖笋颤,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異内地,居然都是意外死亡伴澄,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進(jìn)店門阱缓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來非凌,“玉大人,你說我怎么就攤上這事荆针〕ㄎ耍” “怎么了?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵航背,是天一觀的道長喉悴。 經(jīng)常有香客問我,道長玖媚,這世上最難降的妖魔是什么箕肃? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮今魔,結(jié)果婚禮上勺像,老公的妹妹穿的比我還像新娘。我一直安慰自己错森,他們只是感情好吟宦,可當(dāng)我...
    茶點故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著涩维,像睡著了一般殃姓。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上激挪,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天辰狡,我揣著相機(jī)與錄音,去河邊找鬼垄分。 笑死宛篇,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的薄湿。 我是一名探鬼主播叫倍,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼偷卧,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了吆倦?” 一聲冷哼從身側(cè)響起听诸,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蚕泽,沒想到半個月后晌梨,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡须妻,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年仔蝌,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片荒吏。...
    茶點故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡敛惊,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出绰更,到底是詐尸還是另有隱情瞧挤,我是刑警寧澤,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布儡湾,位于F島的核電站特恬,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏盒粮。R本人自食惡果不足惜鸵鸥,卻給世界環(huán)境...
    茶點故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望丹皱。 院中可真熱鬧妒穴,春花似錦、人聲如沸摊崭。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽呢簸。三九已至矮台,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間根时,已是汗流浹背瘦赫。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留蛤迎,地道東北人确虱。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓,卻偏偏與公主長得像替裆,于是被迫代替她去往敵國和親校辩。 傳聞我的和親對象是個殘疾皇子窘问,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,037評論 2 355

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