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

一、一般來(lái)說(shuō),最簡(jiǎn)單的創(chuàng)建方式就是字面量方式

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

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

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

缺點(diǎn):很明顯,當(dāng)需要?jiǎng)?chuàng)建多個(gè)對(duì)象時(shí),會(huì)有大量的代碼重寫出現(xiàn),為了解決這個(gè)問(wèn)題,工廠模式是個(gè)很好的辦法。

二、 工廠模式

用函數(shù)封裝實(shí)現(xiàn)創(chuàng)建對(duì)象具體細(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ù)來(lái)構(gòu)建一個(gè)包含必要信息的animal對(duì)象,每次調(diào)用該函數(shù),只需要傳入必要的參數(shù)即可。
缺點(diǎn):每個(gè)創(chuàng)建出來(lái)的對(duì)象的類型都是Object,不能判斷其歸屬對(duì)象

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

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

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)建的對(duì)象都會(huì)有一個(gè)constructor(構(gòu)造函數(shù))屬性,用來(lái)標(biāo)明它的對(duì)象類型

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

但檢驗(yàn)對(duì)象類型時(shí),一般用instanceof操作符

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

這說(shuō)明例子里創(chuàng)建出來(lái)的對(duì)象既是Object的實(shí)例,又是Animal的實(shí)例
缺點(diǎn):每次實(shí)例化時(shí),實(shí)例上的方法就會(huì)重新創(chuàng)建一遍。相當(dāng)于每次都創(chuàng)建一個(gè)Function的實(shí)例,然而創(chuàng)建多個(gè)完成相同任務(wù)的Function實(shí)例完全沒(méi)有必要,所以我們可以吧函數(shù)的定義放到構(gòu)造函數(shù)外面來(lái)解決這個(gè)問(wèn)題。

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ù)里包含了一個(gè)指向函數(shù)的指針,解決了多個(gè)函數(shù)做一件事的問(wèn)題,擔(dān)心的問(wèn)題又來(lái)了,雖然run函數(shù)定義在全局,但他只應(yīng)該被Animal調(diào)用,應(yīng)該是局部的函數(shù),但現(xiàn)在其他的對(duì)象函數(shù)都可以調(diào)用它,這就暴露了構(gòu)造函數(shù)的內(nèi)部方法,毫無(wú)封裝性可言,原型模式正好可言解決這個(gè)問(wèn)題。

四、原型模式

我們每次創(chuàng)建的函數(shù)都會(huì)有一個(gè)默認(rèn)prototype(原型)屬性,它是一個(gè)指向原型對(duì)象的指針,原型對(duì)象就是包含構(gòu)造函數(shù)里所有實(shí)例共享的屬性和方法的對(duì)象。所以通過(guò)使用原型對(duì)象可以實(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)建實(shí)例的代碼為例,下圖展現(xiàn)了各個(gè)對(duì)象之間的關(guān)系。


image

簡(jiǎn)單來(lái)說(shuō),在創(chuàng)建了自定義的構(gòu)造函數(shù)后,該函數(shù)會(huì)自動(dòng)獲得一個(gè)prototype屬性,這個(gè)屬性指向的就是它的原型對(duì)象。而在默認(rèn)情況下,所有的原型對(duì)象也都會(huì)自動(dòng)獲得一個(gè)constructor(構(gòu)造函數(shù))屬性,該屬性指向的是該原型對(duì)象的構(gòu)造函數(shù)。即Animal.prototype.constructor = Animal

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

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

如果我們需要修改原型中的值,通過(guò)實(shí)例重寫是不可以的,我們只會(huì)在實(shí)例中創(chuàng)建一個(gè)名稱相同的屬性,同時(shí)屏蔽掉原型中的那個(gè)屬性,我們可以試著去驗(yàn)證看看,

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

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

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

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

那當(dāng)我們獲得了一個(gè)屬性時(shí),我們想知道它是來(lái)自實(shí)例還是來(lái)自原型時(shí),該怎么判斷呢?

hasOwnProperty()方法就可以做到,它可以檢測(cè)一個(gè)屬性是來(lái)自實(shí)例還是原型,只有當(dāng)給定屬性來(lái)自于對(duì)象的實(shí)例中它才回返回true。

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

但問(wèn)題來(lái)了,該怎么判斷一個(gè)屬性是否是存在于原型鏈當(dāng)中的呢??jī)H憑hasOwnProperty()返回值為false并不能判斷,因?yàn)檫@個(gè)屬性可能既不在實(shí)例也不在原型當(dāng)中。
我們可以使用in操作符,in操作符會(huì)在通過(guò)對(duì)象能夠訪問(wèn)到給定屬性時(shí)返回true,無(wú)論該對(duì)象是否存在于實(shí)例中還是原型中。使用hasOwnProperty()方法和in操作符,就可以確定該屬性是否存在于原型當(dāng)中,

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

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

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

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

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

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

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

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

構(gòu)造函數(shù)模式用于定義實(shí)例屬性,原型模式用于定義共享的方法和屬性,這樣每個(gè)實(shí)例都有自己?jiǎn)为?dú)的實(shí)例屬性,同時(shí)又有對(duì)共享方法屬性的引用。

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"]

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

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

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)建對(duì)象的代碼。

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ù)模式返回的對(duì)象與構(gòu)造函數(shù)和構(gòu)造函數(shù)的原型沒(méi)有關(guān)系,所以不能依靠instanceof操作符來(lái)確定對(duì)象類型

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容