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