JavaScript建立對象的7種模式總結

一、工廠模式

 1 function createPerson(name, age, job){
 2     var o = new Object();
 3     o.name = name;
 4     o.age = age;
 5     o.job = job;
 6     o.sayName = function(){
 7         alert(this.name);
 8     };    
 9     return o;
10 }
11 
12 var person1 = createPerson("Nicholas", 29, "Software Engineer");
13 var person2 = createPerson("Greg", 27, "Doctor");

函數createPerson()可以根據接受的參數來構建一個包含全部必要信息的Person對象。能夠無數次地調用這個函數,而每次它都會返回一個包含三個屬性一個方法的對象。工廠模式雖然解決了建立多個類似對象的問題,但卻沒有解決對象識別的問題(即怎樣知道一個對象的類型)。瀏覽器

二、構造函數模式

 1 function Person(name, age, job){
 2     this.name = name;
 3     this.age = age;
 4     this.job = job;
 5     this.sayName = function(){
 6         alert(this.name);
 7     };    
 8 }
 9 
10 var person1 = new Person("Nicholas", 29, "Software Engineer");
11 var person2 = new Person("Greg", 27, "Doctor");
12 
13 person1.sayName();   //"Nicholas"
14 person2.sayName();   //"Greg"
15 
16 alert(person1 instanceof Object);  //true
17 alert(person1 instanceof Person);  //true
18 alert(person2 instanceof Object);  //true
19 alert(person2 instanceof Person);  //true
20 
21 alert(person1.constructor == Person);  //true
22 alert(person2.constructor == Person);  //true
23 
24 alert(person1.sayName == person2.sayName);  //false        

與工廠模式的不一樣之處:函數

  • 沒有顯式地建立對象;
  • 直接將屬性和方法賦給了this對象;
  • 沒有return語句。

缺點:this

使用構造函數,每一個方法都要在每一個實例上從新建立一遍,不一樣實例上的同名函數是不相等的,致使內存浪費。spa

三、原型模式

咱們建立的每一個函數都有一個prototype(原型)屬性,這個屬性是一個指針,指向一個對象,而這個對象的用途是包含能夠由特定類型的全部實例共享的屬性和方法。prototype

 1 function Person(){
 2 }
 3 
 4 Person.prototype.name = "Nicholas";
 5 Person.prototype.age = 29;
 6 Person.prototype.job = "Software Engineer";
 7 Person.prototype.sayName = function(){
 8     alert(this.name);
 9 };
10 
11 var person1 = new Person();
12 person1.sayName();   //"Nicholas"
13 
14 var person2 = new Person();
15 person2.sayName();   //"Nicholas"
16 
17 alert(person1.sayName == person2.sayName);  //true
18 
19 alert(Person.prototype.isPrototypeOf(person1));  //true
20 alert(Person.prototype.isPrototypeOf(person2));  //true
21 
22 //only works if Object.getPrototypeOf() is available
23 if (Object.getPrototypeOf){
24     alert(Object.getPrototypeOf(person1) == Person.prototype);  //true
25     alert(Object.getPrototypeOf(person1).name);  //"Nicholas"
26 }

新對象的這些屬性和方法是由全部實例共享的,因此person1和person2訪問的都是同一組屬性和同一個sayName()函數。指針

雖然能夠經過對象實例訪問保存在原型中的值,但卻不能經過對象實例重寫原型中的值。code

若是咱們在實例中添加了一個與實例原型中的一個屬性同名的屬性,那麼該屬性將會屏蔽原型中的那個屬性。對象

 1 function Person(){
 2 }
 3 
 4 Person.prototype.name = "Nicholas";
 5 Person.prototype.age = 29;
 6 Person.prototype.job = "Software Engineer";
 7 Person.prototype.sayName = function(){
 8     alert(this.name);
 9 };
10 
11 var person1 = new Person();
12 var person2 = new Person();
13 
14 person1.name = "Greg";
15 alert(person1.name);   //"Greg" – from instance
16 alert(person2.name);   //"Nicholas" – from prototype

(1)更簡單的原型語法

 1 function Person(){
 2 }
 3 
 4 Person.prototype = {
 5     name : "Nicholas",
 6     age : 29,
 7     job: "Software Engineer",
 8     sayName : function () {
 9         alert(this.name);
10     }
11 };
12 
13 var friend = new Person();
14 
15 alert(friend instanceof Object);  //true
16 alert(friend instanceof Person);  //true
17 alert(friend.constructor == Person);  //false
18 alert(friend.constructor == Object);  //true

注意:blog

這種方法下,constructor屬性再也不指向Person。每建立一個函數,就會同時建立它的prototype對象,這個對象也會自動得到constructor屬性。而咱們在上面代碼使用的語法,本質上徹底重寫了默認的prototype對象,所以constructor屬性也就變成了新對象的constructor屬性(指向object構造函數),再也不指向Person函數。ip

若是constructor的值真的很重要,能夠像下面這樣特地將它設置回適當的值。

1 Person.prototype = {
2  constructor : Person, 3     name : "Nicholas",
4     age : 29,
5     job: "Software Engineer",
6     sayName : function () {
7         alert(this.name);
8     }
9 };

注意:以這種方式設置constructor屬性會致使它的[[Enumerable]]特性被設置爲true。默認狀況下,原生的constructor屬性是不可枚舉的。所以若是你使用兼容ECMA Script 5的Javascript引擎,能夠試一試Object.defineProperty()。

1 //重設構造函數,只適用於ECMAScript 5兼容的瀏覽器
2 Object.defineProperty(Person.property, "constructor", {
3     enumerable: false,
4     value: Person
5 });

(2)原型的動態性

因爲在原型中查找值的過程是一次搜索,所以咱們對原型對象所作的任何修改都可以當即從實例上反映出來——即便是先建立了實例後修改原型也照樣如此。

1 var friend = new Person();
2 
3 Person.prototype.sayHi = function(){
4     alert("hi");
5 };
6 
7 friend.sayHi();   //"hi" (沒有問題!)

但若是是重寫整個原型對象,那麼狀況就不同。咱們知道,調用構造函數時會爲實例添加一個指向最初原型的[[Prototype]]指針,而把原型修改成另外一個對象就等於切斷了構造函數與最初原型之間的聯繫。

 1 function Person(){
 2 }
 3 
 4 var friend = new Person();
 5         
 6 Person.prototype = {
 7     constructor: Person,
 8     name : "Nicholas",
 9     age : 29,
10     job : "Software Engineer",
11     sayName : function () {
12         alert(this.name);
13     }
14 };
15 
16 friend.sayName();   //error

原型模式的缺點是共享性

 1 function Person(){
 2 }
 3 
 4 Person.prototype = {
 5     constructor: Person,
 6     name : "Nicholas",
 7     age : 29,
 8     job : "Software Engineer",
 9     friends : ["Shelby", "Court"],
10     sayName : function () {
11         alert(this.name);
12     }
13 };
14 
15 var person1 = new Person();
16 var person2 = new Person();
17 
18 person1.friends.push("Van");
19 
20 alert(person1.friends);    //"Shelby,Court,Van"
21 alert(person2.friends);    //"Shelby,Court,Van"
22 alert(person1.friends === person2.friends);  //true

四、組合使用構造函數模式和原型模式

構造函數模式用於定義實例屬性,而原型模式用於定義方法和共享的屬性。

 1 function Person(name, age, job){
 2     this.name = name;
 3     this.age = age;
 4     this.job = job;
 5     this.friends = ["Shelby", "Court"];
 6 }
 7 
 8 Person.prototype = {
 9     constructor: Person,
10     sayName : function () {
11         alert(this.name);
12     }
13 };
14 
15 var person1 = new Person("Nicholas", 29, "Software Engineer");
16 var person2 = new Person("Greg", 27, "Doctor");
17 
18 person1.friends.push("Van");
19 
20 alert(person1.friends);    //"Shelby,Court,Van"
21 alert(person2.friends);    //"Shelby,Court"
22 alert(person1.friends === person2.friends);  //false
23 alert(person1.sayName === person2.sayName);  //true

這種構造函數與原型混成的模式,是目前在ECMAScript中使用最普遍、認同度最高的一種建立自定義類型的方法。能夠說,這是用來定義引用類型的一種默認模式。

五、動態原型模式

 1 function Person(name, age, job){
 2 
 3     //properties
 4     this.name = name;
 5     this.age = age;
 6     this.job = job;
 7     
 8     //methods
 9     if (typeof this.sayName != "function"){ 10     
11         Person.prototype.sayName = function(){ 12             alert(this.name); 13  }; 14         
15  } 16 }
17 
18 var friend = new Person("Nicholas", 29, "Software Engineer");
19 friend.sayName();

加粗代碼只會在初次調用構造函數時纔會執行。這裏對原型所作的修改,可以當即在全部實例中獲得反映。

六、寄生構造函數模式

這種模式的基本思想是建立一個函數,該函數的做用僅僅是封裝建立對象的代碼,而後在返回新建立的對象;但從表面上看,這個函數又很像是典型的構造函數。

 1 function Person(name, age, job){
 2     var o = new Object();
 3     o.name = name;
 4     o.age = age;
 5     o.job = job;
 6     o.sayName = function(){
 7         alert(this.name);
 8     };    
 9     return o;
10 }
11 
12 var friend = new Person("Nicholas", 29, "Software Engineer");
13 friend.sayName();  //"Nicholas"

七、穩妥構造函數模式

所謂穩妥對象,指的是沒有公共屬性,並且其方法也不引用this的對象。

穩妥構造函數遵循與寄生構造函數相似的模式,但有兩點不一樣:

  • 新建立對象的實例方法不引用this;
  • 不使用new操做符調用構造函數。
 1 function Person(name, age, job){
 2 
 3     //建立要返回的對象
 4     var o = new Object();
 5 
 6     //能夠在這裏定義私有變量和函數
 7 
 8     //添加方法
 9     o.sayName = function(){
10         alert(name);
11     };
12 
13     //返回對象
14     return o;
15 }
相關文章
相關標籤/搜索