前言
創(chuàng)新互聯(lián)堅(jiān)持“要么做到,要么別承諾”的工作理念,服務(wù)領(lǐng)域包括:網(wǎng)站制作、網(wǎng)站設(shè)計(jì)、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣等服務(wù),滿足客戶(hù)于互聯(lián)網(wǎng)時(shí)代的點(diǎn)軍網(wǎng)站設(shè)計(jì)、移動(dòng)媒體設(shè)計(jì)的需求,幫助企業(yè)找到有效的互聯(lián)網(wǎng)解決方案。努力成為您成熟可靠的網(wǎng)絡(luò)建設(shè)合作伙伴!
俗話說(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`類(lèi)型,幾乎類(lèi)型區(qū)分可言??!你說(shuō)無(wú)法區(qū)分類(lèi)型,就無(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è)類(lèi)型的對(duì)象實(shí)例,其他的我一概不管(注意到?jīng)]有,這里已經(jīng)有點(diǎn)類(lèi)型的概念了,感覺(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類(lèi)型,所以是false) console.log(a1 instanceof Person) //false(a1也顯然不是Person類(lèi)型,所以同樣是false)
上面這段代碼證明:構(gòu)造函數(shù)模式的確可以做到對(duì)象類(lèi)型的區(qū)分。那么該模式是不是已經(jīng)完美了呢,然而并不是,我們來(lái)一起看看下面的代碼:
//接著上面的代碼 console.log(p1.sayName === a1.sayName) //false
發(fā)現(xiàn)問(wèn)題了嗎?`p1`的`sayName`竟然和`a1`的`sayName`不是同一個(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ì)有解釋?zhuān)?/p>
這個(gè)`constructor`是什么?它是一個(gè)類(lèi)似于指針的引用,指向該`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ì)象
2.當(dāng)我們這樣設(shè)置`prototype`時(shí),其實(shí)已經(jīng)將原先`Person.prototype`給切斷了,然后又重新引用了另外一個(gè)對(duì)象
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一定不要忘了??! age: 30 }
畫(huà)外音:“說(shuō)到這里,你還沒(méi)有講原型模式是如何實(shí)現(xiàn)屬性與方法的共享啊”,不要急,馬上開(kāi)始:
對(duì)象實(shí)例-構(gòu)造函數(shù)-原型,三者是什么樣的關(guān)系呢?
看明白這張圖的意思嗎?
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í)例修改原型上的屬性(基本值,非引用類(lèi)型值或方法時(shí),其他實(shí)例也會(huì)受到影響
原因就是,當(dāng)實(shí)例自身的基本值屬性與原型上的重名時(shí),實(shí)例就會(huì)創(chuàng)建該屬性,留著今后自己使用,而原型上的屬性不會(huì)被修改;但如果屬性是引用類(lèi)型值,如:`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)建自定義類(lèi)型的方法
-----------------
下面要介紹的幾個(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)理解一下:
分析:
1.`p1`實(shí)例創(chuàng)建,此時(shí)原型沒(méi)有`sayName`方法,那我們就為原型添加一個(gè)
2.隨后,我們以字面量的形式重寫(xiě)了原型,這時(shí)舊的原型并沒(méi)有被銷(xiāo)毀,而且它和`p1`還保持著聯(lián)系
3.之后的實(shí)例,也就是這里的`p2`,都是與新原型保持聯(lián)系;所以`p1`、`p2`有各自的構(gòu)造器原型,即使它們的構(gòu)造器是同一個(gè)
所以切記:當(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ō)話:
我們發(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
那么寄生構(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í)例類(lèi)型,因?yàn)樵撃J缴傻膶?shí)例,它的構(gòu)造函數(shù)都是Object,原型都是Object.prototype
穩(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ú)緣
結(jié)語(yǔ)
以上就是對(duì)js中創(chuàng)建對(duì)象的方式的總結(jié),希望對(duì)大家有所幫助
這篇基于JS對(duì)象創(chuàng)建常用方式及原理分析就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持創(chuàng)新互聯(lián)。