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
使用構造函數,每一個方法都要在每一個實例上從新建立一遍,不一樣實例上的同名函數是不相等的,致使內存浪費。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 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 });
因爲在原型中查找值的過程是一次搜索,所以咱們對原型對象所作的任何修改都可以當即從實例上反映出來——即便是先建立了實例後修改原型也照樣如此。
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的對象。
穩妥構造函數遵循與寄生構造函數相似的模式,但有兩點不一樣:
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 }