詳解JavaScript基于面向?qū)ο笾畡?chuàng)建對(duì)象(2)
接著上文《詳解JavaScript基于面向?qū)ο笾畡?chuàng)建對(duì)象(1)》繼續(xù)學(xué)習(xí)。
4、原型方式
我們創(chuàng)建的每個(gè)函數(shù)都有一個(gè)通過(guò)prototype(原型)屬性,這個(gè)屬性是一個(gè)對(duì)象,它的用途是包含可以由特定類(lèi)型的所有實(shí)例共享的屬性和方法。邏輯上可以這么理解:prototypt通過(guò)條用構(gòu)造函數(shù)而創(chuàng)建的那個(gè)對(duì)象的原型對(duì)象。使用原型的好處就是可以讓所有對(duì)象實(shí)例共享它所包含的屬性和方法。也就是說(shuō),不必在構(gòu)造函數(shù)中定義對(duì)象信息,而是直接將這些信息添加到原型中。
原型方式利用了對(duì)象的prototype 屬性,可以把它看成創(chuàng)建新對(duì)象所依賴(lài)的原型。這里,首先用空構(gòu)造函數(shù)來(lái)設(shè)置函數(shù)名。然后所有的屬性和方法都被直接賦予prototype屬性。我重寫(xiě)了前面的例子,代碼如下:
function Car() { }; //將所有的屬性的方法都賦予prototype屬性 Car.prototype.color = "blue"; Car.prototype.doors = 4; Car.prototype.mpg = 25; Car.prototype.showColor = function() { return this.color; }; var Car1 = new Car(); var Car2 = new Car(); document.write(Car1.showColor()+"<br/>");//輸出:blue document.write(Car2.showColor());//輸出:blue
在這段代碼中,首先定義構(gòu)造函數(shù)Car(),其中無(wú)任何代碼。接下來(lái)的幾行代碼,通過(guò)給Car的 prototype 屬性添加屬性去定義Car對(duì)象的屬性。調(diào)用new Car()時(shí),原型的所有屬性都被立即賦予要?jiǎng)?chuàng)建的對(duì)象,意味著所有Car實(shí)例存放的都是指向 showColor() 函數(shù)的指針。從語(yǔ)義上講,所有屬性看起來(lái)都屬于一個(gè)對(duì)象,因此解決了前面的工廠方式和構(gòu)造函數(shù)方式存在的問(wèn)題。
此外,使用這種方式,還能用 instanceof 運(yùn)算符檢查給定變量指向的對(duì)象的類(lèi)型:
原型方式看起來(lái)是個(gè)不錯(cuò)的解決方案。遺憾的是,它并不盡如人意。首先,這個(gè)構(gòu)造函數(shù)沒(méi)有參數(shù)。使用原型方式,不能通過(guò)給構(gòu)造函數(shù)傳遞參數(shù)來(lái)初始化屬性的值,因?yàn)镃ar1和Car2的color屬性都等于 "blue",doors屬性都等于4,mpg屬性都等于25。這意味著必須在對(duì)象創(chuàng)建后才能改變屬性的默認(rèn)值,這點(diǎn)很令人討厭,但還沒(méi)完。真正的問(wèn)題出現(xiàn)在屬性指向的是對(duì)象,而不是函數(shù)時(shí)。函數(shù)共享不會(huì)造成問(wèn)題,但對(duì)象卻很少被多個(gè)實(shí)例共享。請(qǐng)思考下面的例子:
function Car() { };//定義一個(gè)空構(gòu)造函數(shù),且不能傳遞參數(shù) Car.prototype.color = "blue"; Car.prototype.doors = 4; Car.prototype.mpg = 25; Car.prototype.drivers = new Array("Mike","John"); Car.prototype.showColor = function() { return this.color; }; var Car1 = new Car(); var Car2 = new Car(); Car1.drivers.push("Bill"); document.write(Car1.drivers+"<br/>");//輸出:Mike,John,Bill document.write(Car2.drivers);//輸出 :Mike,John,Bill
上面的代碼中,屬性drivers是指向Array對(duì)象的指針,該數(shù)組中包含兩個(gè)名"Mike"和 "John"。由于 drivers是引用值,Car的兩個(gè)實(shí)例都指向同一個(gè)數(shù)組。這意味著給Car1.drivers添加值 "Bill",在 Car2.drivers 中也能看到。輸出這兩個(gè)指針中的任何一個(gè),結(jié)果都是顯示字符串 "Mike,John,Bill"。由于創(chuàng)建對(duì)象時(shí)有這么多問(wèn)題,你一定會(huì)想,是否有種合理的創(chuàng)建對(duì)象的方法呢?答案是有,需要聯(lián)合使用構(gòu)造函數(shù)和原型方式。
5、混合的構(gòu)造函數(shù)/原型方式(推薦使用)
混合使用構(gòu)造函數(shù)方式和原型方式,就可像用其他程序設(shè)計(jì)語(yǔ)言一樣創(chuàng)建對(duì)象。這種概念非常簡(jiǎn)單,即用構(gòu)造函數(shù)定義對(duì)象的所有非函數(shù)屬性,用原型方式定義對(duì)象的函數(shù)屬性(方法)。結(jié)果是,所有函數(shù)都只創(chuàng)建一次,而每個(gè)對(duì)象都具有自己的對(duì)象屬性實(shí)例。我們重寫(xiě)了前面的例子,代碼如下:
function Car(Color,Doors,Mpg) { this.color = Color; this.doors = Doors; this.mpg = Mpg; this.drivers = new Array("Mike","John"); }; Car.prototype.showColor = function() { return this.color; }; var Car1 = new Car("red",4,23); var Car2 = new Car("blue",3,25); Car1.drivers.push("Bill"); document.write(Car1.drivers+"<br/>");//輸出:Mike,John,Bill documnet.write(Car2.drivers);//輸出:Mike,John
現(xiàn)在就更像創(chuàng)建一般對(duì)象了。所有的非函數(shù)屬性都在構(gòu)造函數(shù)中創(chuàng)建,意味著又能夠用構(gòu)造函數(shù)的參數(shù)賦予屬性默認(rèn)值了。因?yàn)橹粍?chuàng)建showColor()函數(shù)的一個(gè)實(shí)例,所以沒(méi)有內(nèi)存浪費(fèi)。此外,給Car1的drivers數(shù)組添加 "Bill" 值,不會(huì)影響到Car2的數(shù)組,所以輸出這些數(shù)組的值時(shí),Car1.drivers 顯示的是 "Mike,John,Bill",而 Car2.drivers 顯示的是 "Mike,John"。因?yàn)槭褂昧嗽头绞?,所以仍然能利?instanceof運(yùn)算符來(lái)判斷對(duì)象的類(lèi)型。
這種方式是ECMAScript采用的主要方式,它具有其他方式的特性,卻沒(méi)有他們的副作用。不過(guò),有些開(kāi)發(fā)者仍覺(jué)得這種方法不夠完美。
6、動(dòng)態(tài)原型方式
對(duì)于習(xí)慣使用其他語(yǔ)言的開(kāi)發(fā)者來(lái)說(shuō),使用混合的構(gòu)造函數(shù)/原型方式感覺(jué)不那么和諧。畢竟,定義類(lèi)時(shí),大多數(shù)面向?qū)ο笳Z(yǔ)言都對(duì)屬性和方法進(jìn)行了視覺(jué)上的封裝。請(qǐng)考慮下面的 Java 類(lèi):
class Car { public String color = "blue"; public int doors = 4; public int mpg = 25; public Car(String color, int doors, int mpg) { this.color = color; this.doors = doors; this.mpg = mpg; } public void showColor() { System.out.println(color); } }
Java很好地打包了Car類(lèi)的所有屬性和方法,因此看見(jiàn)這段代碼就知道它要實(shí)現(xiàn)什么功能,它定義了一個(gè)對(duì)象的信息。批評(píng)混合的構(gòu)造函數(shù)/原型方式的人認(rèn)為,在構(gòu)造函數(shù)內(nèi)部找屬性,在其外部找方法的做法不合邏輯。因此,他們?cè)O(shè)計(jì)了動(dòng)態(tài)原型方法,以提供更友好的編碼風(fēng)格。
動(dòng)態(tài)原型方法的基本想法與混合的構(gòu)造函數(shù)/原型方式相同,即在構(gòu)造函數(shù)內(nèi)定義非函數(shù)屬性,而函數(shù)屬性則利用原型屬性定義。唯一的區(qū)別是賦予對(duì)象方法的位置。下面是用動(dòng)態(tài)原型方法重寫(xiě)的Car:
function Car(Color,Doors,Mpg) { this.color = Color; this.doors = Doors; this.mpg = Mpg; this.drivers = new Array("Mike","John"); //如果Car對(duì)象中的_initialized為undefined,表明還沒(méi)有為Car的原型添加方法 if (typeof Car._initialized == "undefined") { Car.prototype.showColor = function() { return this.color; }; Car._initialized = true; //設(shè)置為true,不必再為prototype添加方法 } } var Car1 = new Car("red",4,23);//生成一個(gè)Car對(duì)象 var Car2 = new Car("blue",3,25); Car1.drivers.push("Bill");//向Car1對(duì)象實(shí)例的drivers屬性添加一個(gè)元素 document.write(Car1.drivers+"<br/>");//輸出:Mike,John,Bill document.write(Car2.drivers);//輸出:Mike,John
直到檢查typeof Car._initialize是否等于"undefined"之前,這個(gè)構(gòu)造函數(shù)都未發(fā)生變化。這行代碼是動(dòng)態(tài)原型方法中最重要的部分。如果這個(gè)值未定義,構(gòu)造函數(shù)將用原型方式繼續(xù)定義對(duì)象的方法,然后把 Car._initialized設(shè)置為true。如果這個(gè)值定義了(它的值為 true時(shí),typeof 的值為Boolean),那么就不再創(chuàng)建該方法。簡(jiǎn)而言之,該方法使用標(biāo)志(_initialized)來(lái)判斷是否已給原型賦予了任何方法。該方法只創(chuàng)建并賦值一次,傳統(tǒng)的 OOP開(kāi)發(fā)者會(huì)高興地發(fā)現(xiàn),這段代碼看起來(lái)更像其他語(yǔ)言中的類(lèi)定義了。
我們應(yīng)該采用哪種方式呢?
如前所述,目前使用最廣泛的是混合的構(gòu)造函數(shù)/原型方式。此外,動(dòng)態(tài)原型方式也很流行,在功能上與構(gòu)造函數(shù)/原型方式等價(jià)??梢圆捎眠@兩種方式中的任何一種。不過(guò)不要單獨(dú)使用經(jīng)典的構(gòu)造函數(shù)或原型方式,因?yàn)檫@樣會(huì)給代碼引入問(wèn)題。總之JS是基于面向?qū)ο蟮囊婚T(mén)客戶(hù)端腳本語(yǔ)言,我們?cè)趯W(xué)習(xí)它的面向?qū)ο蠹夹g(shù)的時(shí)候要的留意JS與其他嚴(yán)謹(jǐn)性高的程序語(yǔ)言的不同。也要正確使用JS創(chuàng)建對(duì)象的合理的方式,推薦使用構(gòu)造函數(shù)與原型方式的混合方式創(chuàng)建對(duì)象實(shí)例。這樣可以避免許多不必要的麻煩。
以上就是JavaScript基于面向?qū)ο笾畡?chuàng)建對(duì)象的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助。
- js面向?qū)ο笾R?jiàn)創(chuàng)建對(duì)象的幾種方式(工廠模式、構(gòu)造函數(shù)模式、原型模式)
- 從面試題學(xué)習(xí)Javascript 面向?qū)ο螅▌?chuàng)建對(duì)象)
- js面向?qū)ο?多種創(chuàng)建對(duì)象方法小結(jié)
- 學(xué)習(xí)javascript面向?qū)ο?掌握創(chuàng)建對(duì)象的9種方式
- 詳解JavaScript基于面向?qū)ο笾畡?chuàng)建對(duì)象(1)
- JavaScript面向?qū)ο蟪绦蛟O(shè)計(jì)創(chuàng)建對(duì)象的方法分析
- JavaScript 三種創(chuàng)建對(duì)象的方法
- JS 創(chuàng)建對(duì)象(常見(jiàn)的幾種方法)
- js創(chuàng)建對(duì)象的幾種常用方式小結(jié)(推薦)
- javascript面向?qū)ο髣?chuàng)建對(duì)象的方式小結(jié)
相關(guān)文章
微信小程序+騰訊地圖開(kāi)發(fā)實(shí)現(xiàn)路徑規(guī)劃繪制
這篇文章主要介紹了微信小程序+騰訊地圖開(kāi)發(fā)實(shí)現(xiàn)路徑規(guī)劃繪制,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-05-05Java 正則表達(dá)式學(xué)習(xí)總結(jié)和一些小例子
字符串處理是許多程序中非常重要的一部分,它們可以用于文本顯示,數(shù)據(jù)表示,查找鍵和很多目的.在Unix下,用戶(hù)可以使用正則表達(dá)式的強(qiáng)健功能實(shí)現(xiàn)這些 目的2012-09-09JS實(shí)現(xiàn)json數(shù)組排序操作實(shí)例分析
這篇文章主要介紹了JS實(shí)現(xiàn)json數(shù)組排序操作,結(jié)合實(shí)例形式分析了javascript針對(duì)json數(shù)組的遍歷、排序簡(jiǎn)單操作技巧,需要的朋友可以參考下2019-10-10javascript基礎(chǔ)進(jìn)階_深入剖析執(zhí)行環(huán)境及作用域鏈
下面小編就為大家?guī)?lái)一篇javascript基礎(chǔ)進(jìn)階_深入剖析執(zhí)行環(huán)境及作用域鏈。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-09-09JavaScript創(chuàng)建閉包的兩種方式的優(yōu)劣與區(qū)別分析
這篇文章主要介紹了JavaScript創(chuàng)建閉包的兩種方式的優(yōu)劣與區(qū)別分析的相關(guān)資料,需要的朋友可以參考下2015-06-06js實(shí)現(xiàn)登錄注冊(cè)框手機(jī)號(hào)和驗(yàn)證碼校驗(yàn)(前端部分)
這篇文章主要為大家詳細(xì)介紹了js實(shí)現(xiàn)登錄注冊(cè)框手機(jī)號(hào)和驗(yàn)證碼校驗(yàn)的前端部分代碼,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-09-09js不間斷滾動(dòng)的簡(jiǎn)單實(shí)現(xiàn)
下面小編就為大家?guī)?lái)一篇js不間斷滾動(dòng)的簡(jiǎn)單實(shí)現(xiàn)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-06-06使用coffeescript編寫(xiě)node.js項(xiàng)目的方法匯總
Node.js 基于JavaScript編寫(xiě)應(yīng)用,JavaScript是我的主要開(kāi)發(fā)語(yǔ)言。CoffeeScript是編譯為JavaScript的編程語(yǔ)言。CoffeeScript是一個(gè)非常高階的語(yǔ)言,將JavaScript、Ruby和Python中我最?lèi)?ài)的部分結(jié)合在了一起。小編給大家介紹下使用coffeescript編寫(xiě)node.js項(xiàng)目的方法2015-08-08