第一種:創建0bject對象實例
創建自定義對象的最簡單方法就是創建一個Object的實例,再為他添加屬性和方法。
1 var people = new Object(); 2 people.name = "yewenxiang"; 3 people.age = 24; 4 people.sayName = function(){ 5 console.log(this.name); 6 }
還可以使用對象字面量語法創建一個對象,等價於上面的代碼
1 var people = { 2 name:"yewenxiang", 3 age:24, 4 sayName:function(){ 5 console.log(this.name); 6 } 7 }
這個方法是new Object() 來創建一個people對象的實例,有一個很大的問題:
- 使用同一個接口創建了很多對象,會產生大量重復的代碼。
第二種:工廠模式
為了解決上面問題發明了用函數來創建一個對象的方法:
1 function People(name,age){ 2 var o = new Object(); 3 o.name = name; 4 o.age = age; 5 o.sayName = function(){ 6 console.log(this.name); 7 } 8 return o; 9 } 10 var people1 = People("yewenxiang",24); 11 console.log(people1.name); 12 people1.sayName();
函數People()能接收兩個參數,可以無限次的調用這個函數,每次都會返回一個對象,對象的基本屬性為傳入函數的參數,解決了創建多個相似對象導致代碼臃腫的問題。但是不能識別創建的對象類型,創建的對象的構造函數全部是Object。
-
console.log(people1 instanceof Object); //true
第三種:構造函數模式
構造函數模式可以用來創建自定義類型的對象,原生的構造函數類型有Object Array String等等。
1 function People(name,age){ 2 this.name = name; 3 this.age = age; 4 this.sayName = function(){ 5 console.log(this.name); 6 } 7 } 8 var people1 = new People("yewenxiang",24); 9 var people2 = new People("xiangwang",23); 10 console.log(people1.name); 11 console.log(people1 instanceof People); //true 12 console.log(people1 instanceof Object); //true
這個例子中創建的對象實例既是Object對象的實例,也是自定義構造函數People 的實例。
構造函數缺點:
- 每個方法都要在實例上面重新創建一遍。也就是實例化對象的sayName屬性中存的函數地址不同,雖然他們調用后的結果可能一樣。
上面的構造函數可以這么定義
1 function People(name,age){ 2 this.name = name; 3 this.age = age; 4 this.sayName = new Function(){ 5 console.log(this.name); 6 } 7 }
看第四行代碼。每次實例化一個對象,sayName屬性都指向了一個不同的函數引用,也就是指向了由Function構造函數創建的不同實例。
1 function People(name,age){ 2 this.name = name; 3 this.age = age; 4 this.sayName = function(){ 5 console.log(this.name); 6 } 7 } 8 var people1 = new People("yewenxiang",24); 9 var people2 = new People("yewenxiang",23); 10 console.log(people1.sayName == people2.sayName); //false 11 console.log(people1.sayName() == people2.sayName()); //true
第四種:原型模式
我們創建的每個函數都有一個prototype屬性,這個屬性是一個指針,指向了原型對象。這個原型對象的作用是由自定義的構造函數所有實例化的對象共享的屬性和方法.
1 function People(){ 2 } 3 People.prototype.name = "yewenxiang"; 4 People.prototype.age = "23"; 5 People.prototype.sayName = function(){ 6 console.log(this.name); 7 } 8 var people1 = new People(); 9 var people2 = new People(); 10 console.log(people1.sayName == people2.sayName); // true
- people1和people實例對象上沒有sayName這個方法,就去原型上找,兩個實例找的都是原型上存放的同一個sayName方法的引用,也就是地址,所以他們是相等的,不管是調用后的值,還是地址。
簡單的原型寫法:
用一個包含所有屬性和方法的對象字面量來重寫整個原型。
1 function People(){ 2 } 3 People.prototype = { 4 name:"yewenxiang", 5 age:24, 6 sayName:function(){ 7 console.log(this.name); 8 } 9 };
但是有個問題,因為是用對象字面量來重寫了People.prototype對象,People.prototype.constructor 指向了Object這個構造函數,不再指向People構造函數,如下圖虛線為變化前,實線為變化后。
1 function People(){ 2 } 3 People.prototype = { 4 name:"yewenxiang", 5 age:24, 6 sayName:function(){ 7 console.log(this.name); 8 } 9 } 10 var people1 = new People(); 11 console.log(people1.prototype); 12 console.log(People.prototype.constructor); //輸出Object構造函數 13 console.log(people1 instanceof People); //true 14 console.log(people1 instanceof Object); //true
雖然可以使用instanceof 來檢測對象的類型,但是無法使用people1.constructor 來確定對象了,因為所有實例都會使用People.prototype原型上的constructor屬性,而這個屬性指向Object構造函數。
instanceof 檢測的原理:
- A instanceof B
- 這里A 和 B 是兩個對象,沿着A的__proto__這條線找,同時沿着B的prototype這條線找,如果兩條線能找到同一個對象,那么返回true,如果終點還未重合,則返回false
- 上面的 People.propotype 和people.__proto__的指向沒有變都是指向 People.propotype這個原型,所以返回了true。
可以給People.prototype = {constructor:People}來解決這個問題,但是重設了constructor屬性的值,導致他變成了可枚舉的狀態,原生的是不可枚舉的,可以使用Object.defineProperty來定義這個屬性
1 function People(){ 2 } 3 People.prototype = { 4 name:"yewenxiang", 5 age:24, 6 sayName:function(){ 7 console.log(this.name); 8 } 9 } 10 Object.defineProperty(People.prototype,"constructor",{ 11 enumerable:false, // 不可枚舉 12 value:People 13 }); 14 var people1 = new People(); 15 console.log(people1.prototype); 16 console.log(people1.constructor); //輸出People構造函數 17 console.log(people1 instanceof People); //true 18 console.log(people1 instanceof Object); //true
原型模式的問題:
- 省略了為構造函數傳遞初始化參數,結果所有的實例在默認情況下取得相同的屬性值。
- 原型中所有的屬性被很多實例共享,這種共享對函數很合適,對於引用類型值的屬性來說會產生問題,如數組類型,其中一個實例化對象改變了這個屬性的值,其他實例化對象上全部改變了。
第五種:組合使用構造函數模式和原型模式
使用的技巧:
- 構造函數上定義實例各自私有的屬性。
- 原型上定義方法和共享的屬性。
1 function People(name,age){ 2 this.name = name; 3 this.age = age; 4 } 5 People.prototype.sayName = function(){ 6 console.log(this.name); 7 } 8 var people1 = new People("yewenxiang",24);