- Objct 模式
- 工廠模式
- 構造器模式
- 通過 Function 物件實現
- prototype 模式
- 構造器與原型方式的混合模式
- 動態原型模式
- 混合工廠模式
1.Object 模式
1 2 3 4 5 6 7 8 |
var o1 = {};//字面量的表現形式 var o2 = new Object; var o3 = new Object(); var o4 = new Object(null); var o5 = new Object(undefined); var o6 = Object.create(Object.prototype);//等價於 var o = {};//即以 Object.prototype 物件為一個原型模板,新建一個以這個原型模板為原型的物件 //區別 var o7 = Object.create(null);//建立一個原型為 null 的物件 |
在 chrome 裡檢視各個新建物件的區別:
可以看出前6種模式建立出來的物件都是一樣的,第七種不同點在於其雖然也為 Object 物件但其無任何屬性(包括沒有任何可以繼承的屬性,因為建立的時候沒有指定其原型)
2.工廠模式
1 2 3 4 5 6 7 8 9 10 11 12 13 |
//工廠方法1 通過一個方法來建立物件 利用 arguments 物件獲取引數設定屬性(引數不直觀,容易出現問題) function createCar(){ var oTemp = new Object(); oTemp.name = arguments[0];//直接給物件新增屬性,每個物件都有直接的屬性 oTemp.age = arguments[1]; oTemp.showName = function () { alert(this.name); };//每個物件都有一個 showName 方法版本 return oTemp; } createCar("tom").showName();//在 JS 中沒有傳遞的實參,實際形參值為 undefined(這裡的 age 為 undefined) createCar("tim",80).showName(); alert(createCar("tom") instanceof Object);//true 判斷物件是否 Object 類或子類 |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
//工廠方法2 通過傳參設定屬性(引數直觀明瞭) function createCar(name,age){ var oTemp = new Object(); oTemp.name = name;//直接給物件新增屬性,每個物件都有直接的屬性 oTemp.age = age; oTemp.showName = function () { alert(this.name); };//每個物件都有一個 showName 方法版本 return oTemp; } createCar("tom").showName(); createCar("tim",80).showName(); alert(createCar("tom") instanceof Object);//true 判斷物件是否 Object 類或子類 |
3.構造器模式
1 2 3 4 5 6 7 8 9 10 11 |
//構造器方法1 function Car(sColor,iDoors){ //宣告為構造器時需要將函式名首字母大寫 this.color = sColor; //構造器內直接宣告屬性 this.doors = iDoors; this.showColor = function(){ return this.color; };//每個 Car 物件都有自己的 showColor方法版本 this.showDoor = function () { return this.doors; } } |
使用方法1的問題很明顯,沒辦法是 showDoor 方法重用,每次新建一個物件就要在堆裡新開闢一篇空間.改進如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
//構造器方法2 function showDoor(){ //定義一個全域性的 Function 物件 return this.doors; } function Car(sColor,iDoors){//構造器 this.color = sColor; //構造器內直接宣告屬性 this.doors = iDoors; this.showColor = function(){ return this.color; }; this.showDoor = showDoor();//每個 Car 物件共享同一個 showDoor 方法版本(方法有自己的作用域,不用擔心變數被共享) } alert(new Car("red",2).showColor());//通過構造器建立一個物件並呼叫其物件方法 |
上面出現的問題就是語義不夠清除,體現不出類的封裝性,改進為 prototype 模式
4.通過Function物件實現建立物件
我們知道每宣告一個函式實際是建立了一個Function 例項 JS 函式.
1 2 3 |
function function_name(param1,param2){alert(param1);} //等價於 var function_name = new Function("param1","pram2","alert(param1);"); |
1 2 3 4 5 6 |
var Car2 = new Function("sColor","iDoors", "this.color = sColor;"+ "this.doors = iDoors;"+ "this.showColor = function(){ return this.color; }" ); alert(new Car2("blue",3).showColor()); |
5.prototype模式
- 類通過 prototype 屬性新增的屬性與方法都是繫結在這個類的 prototype 域(實際為一個 Prototype 物件)中,繫結到這個域中的屬性與方法只有一個版本,只會建立一次.
- 類的例項物件可以直接像呼叫自己的屬性一樣呼叫該類的 prototype 域中的屬性與方法,類可以通過呼叫 prototype 屬性來間接呼叫prototype 域內的屬性與方法.
注意:通過類例項化出物件後物件內無 prototype 屬性,但物件可直接像訪問屬性一樣的訪問類的 prototype 域的內容,例項物件有個私有屬性__proto__,__proto__屬性內含有類的 prototype 域內的屬性與方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
方法1 function Car3(){}//用空建構函式設定類名 Car3.prototype.color = "blue";//每個物件都共享相同屬性 Car3.prototype.doors = 3; Car3.prototype.drivers = new Array("Mike","John"); Car3.prototype.showColor = function(){ alert(this.color); };//每個物件共享一個方法版本,省記憶體。 var car3_1 = new Car3(); var car3_2 = new Car3(); alert(car3_1.color);//blue alert(car3_2.color);//blue alert(Car3.prototype.color);//blue car3_1.drivers.push("Bill"); alert(car3_1.drivers);//"Mike","John","Bill" alert(car3_2.drivers);//"Mike","John","Bill" alert(Car3.prototype.drivers);//"Mike","John","Bill" //直接修改例項物件的屬性,解析器會先去找例項物件是否有這個屬性(不會去找例項物件的 _proto_ 屬性內的那些類的 prototype 屬性,而是直接檢視這個例項是否有對應的屬性(與_proto_同級)) //如果沒有則直接給這個例項物件新增該屬性,但不會修改類的prototype域的同名屬性,既例項物件的_proto_屬性內的那些類 prototype 域屬性不會被修改 car3_1.color = "red";//car3_1物件內無名為 color 的物件屬性,故將該屬性新增到該物件上 //解析器對例項物件讀取屬性值的時候會先查詢該例項有無同名的直接屬性 //如果沒有,則查詢__proto__屬性內儲存的那些 當前類的 prototype 域的屬性 //有就返回,無則繼續查詢是否有原型鏈中的對應的方法屬性 //有就返回,無則返回undefined alert(car3_1.color);//red alert(car3_2.color);//blue alert(car3_2.color2);//undefined //直接修改類的 prototype 域內的屬性,不會影響該類的例項物件的物件屬性,但會影響例項物件的_proto_屬性(_proto_屬性記憶體放的是類的 prototype 域的內容) Car3.prototype.color = "black"; alert(car3_1.color);//red 該物件有同名的直接屬性,故不會去_proto_屬性內查詢類的 prototype 域的屬性 alert(car3_2.color);//black 受影響 //直接修改例項物件的方法,解析器會先去找例項物件是否有這個方法(不會去找例項物件的 _proto_ 屬性內的那些類的 prototype 域的方法,而是直接檢視這個例項是否有對應的方法(與_proto_同級)) //如果沒有則直接給這個例項物件新增該方法,但不會修改類的prototype域的同名方法,既例項物件的_proto_屬性內的那些類 prototype 域方法不會被修改 //car3_1物件內無名為 showColor 的物件方法屬性,故將該方法屬性新增到該物件上 car3_1.showColor = function () { alert("new function"); } //解析器對例項物件呼叫方法屬性的時候會先查詢該例項有無同名的直接方法屬性 //如果沒有,則查詢_proto_屬性內儲存的那些 當前類的 prototype 域的方法屬性 //有就返回,無則繼續查詢是否有原型鏈中的對應的方法屬性 //找到就返回,無則報錯 car3_1.showColor();//new function car3_2.showColor();//blue car3_1.abcd();//直接報錯 //直接修改類的 prototype 域內的方法屬性,不會影響該類的例項物件的方法屬性,但會影響例項物件的_proto_屬性(_proto_屬性記憶體放的是類的 prototype 域的內容) Car3.prototype.showColor = function () { alert("second function"); } car3_1.showColor();//new function 該物件有同名的方法屬性,故不會去_proto_屬性內查詢類的 prototype 域的方法屬性 car3_2.showColor();//second function 受影響 |
可以看出使用該方法雖然說打打減少了記憶體的浪費,但依舊有問題,某個物件的屬性一旦改變,所有由該類例項化得到的物件的__proto__內屬性值也會跟著變(實為引用),改進如下
6.構造器方式與原型方式的混合模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
//每個物件有專屬的屬性不會與其他物件共享 function Car4(sColor,iDoors){ this._color = sColor;//私有屬性變數名稱頭加下劃線標識 this._doors = iDoors; this.drivers = new Array("Mike","John");//公有屬性標識 } //所有物件共享一個方法版本,減少記憶體浪費 Car4.prototype.showColor = function () { alert(this._color); }; var car4_1 = new Car4("red",4); var car4_2 = new Car4("blue",3); car4_1.drivers.push("Bill"); alert(car4_1.drivers);//"Mike","John","Bill" alert(car4_2.drivers);//"Mike","John" |
這也是常用的建立物件方式之一
7.動態原型模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
function Car5(sColor,iDoors,iMpg){ this.color = sColor; this.doors = iDoors; this.mpg = iMpg; this.drivers = new Array("Mike","John"); //使用標誌(_initialized)來判斷是否已給原型賦予了任何方法,保證方法永遠只被建立並賦值一次 if(typeof Car5._initialized == "undefined"){//因為這裡的標記是附加在類上,故如果後期直接對其進行修改,還是有可能出現再次建立的情況 Car5.prototype.showColor = function () {//為Car5新增一個存放在 prototype 域的方法 alert(this.color); }; Car5._initialized = true;//設定一個靜態屬性 } } var car5_1 = new Car5("red",3,25); var car5_2 = new Car5("red",3,25); |
這種模式使得定義類像強型別語言例如 java 等語言的定義模式
8.混合工廠模式
1 2 3 4 5 6 7 8 9 10 |
function Car6(){ var oTempCar = new Object; oTempCar.color = "blue"; oTempCar.doors = 4; oTempCar.showColor = function () { alert(this.color); }; return oTempCar; } var car6 = new Car6(); |
由於在 Car6()建構函式內部呼叫了 new 運算子,所以將忽略第二個 new 運算子(位於建構函式之外),
在建構函式內部建立的物件被傳遞迴變數car6,這種方式在物件方法的內部管理方面與經典方式(工廠方法)有著相同的問題.應儘量避免