JavaScript創建對象的幾種方式總結
ECMA把對象定義為:無序屬性的集合,其屬性可以包含基本值、對象或者函數。
1. 使用Object構造函數創建對象
創建自定義對象的最簡單的方式就是創建一個Object的實例,然後再為它添加屬性和方法。
//通過Object構造函數的實例創建對象 var person = new Object(); //添加屬性和方法 person.name = "guo"; person.age = ‘24‘; person.sayName = function(){ console.log(this.name); }; //訪問屬性的兩種方式:點表示法和方括號表示法 console.log(person.name); //"guo" console.log(person[‘age‘]); //24 person.sayName(); //"guo"
2. 使用對象字面量創建一個對象
//使用對象字面量創建一個對象 var person = { name : "guo", age : 24, sayName : function(){ console.log(this.name); } }; console.log(person.name); //"guo" console.log(person[‘age‘]); //24 person.sayName(); //"guo"
3.工廠模式
雖然Object構造函數或者對象字面量都可以用來創建單個對象,但這些方式有個明顯的缺點:使用同一個接口創建對象,會產生大量的重復代碼。為了解決這個問題,提出工廠模式。
//使用工廠模式創建對象,返回帶有屬性和方法的person對象 function createPerson(name, age){ var o = new Object(); o.name = name; o.age = age; o.sayName = function(){ console.log(this.name); };return o; } var person1 = createPerson("guo", 24); person1.sayName(); //"guo"
4.使用自定義構造函數模式創建對象
// 使用自定義構造函數模式創建對象(作為構造函數的函數首字母要大寫,以區別其它函數) function Person(name,age){ this.name=name; this.age=age; this.sayName=function(){ console.log(this.name); }; } var person1 = new Person("guo", 24); person1.sayName(); //"guo”
拓展:要創建Person的新實例,必須使用new操作符。那麽,new操作符具體幹了些什麽呢?
(1)創建一個新(空)對象
(2)將構造函數的作用域賦給新對象(因為this就指向了這個新對象)
(3)執行構造函數中的代碼(為這個新對象添加屬性)
(4)返回新對象
構造函數和其他函數的唯一區別:就在於調用它們的方式不同。任何函數,只要通過new操作符來調用,那它就可以作為構造函數;而任何函數,如果不通過new操作符來調用,那它跟普通函數也不會有什麽區別。這種方式有個缺陷是,上面例子中,sayName這個方法,它的每個實例都是指向不同的函數實例,而不是同一個。即不同實例上的同門函數是不相等的。
為了解決這個問題,提出了原型模式創建對象。
5.使用原型模式創建對象
//使用原型模式創建對象(解決了構造函數中的函數無法復用問題) function Person(){ } Person.prototype = { //對象字面量語法 重寫原型對象 constructor : Person, name : "guo", age : 24, friends:["liu","li"], sayName:function(){ console.log(this.name); } }; //創建實例1 var person1 = new Person(); person1.friends.push("yang"); console.log(person1.friends); //[ ‘liu‘, ‘li‘, ‘yang‘ ] //創建實例2 var person2 = new Person(); console.log(person2.friends); //[ ‘liu‘, ‘li‘, ‘yang‘ ] //原型模式的缺陷 person1.sayName(); //"guo" person2.sayName(); //"guo"
這裏我們發現了一個問題,而是原型模式的缺陷,上面例子中,person1改變數組的值,直接影響了person2中friends數組的值,這是我們不希望看到的。
原型模式最大的問題是由共享的本質所導致的,原型中所有的屬性是被所有實例共享的。對於引用類型的值,也出現了實例共享問題。
為了解決這個問題,提出了組合使用原型模式和構造函數創建對象。
6.組合使用構造函數和原型模式創建對象
顧名思義,構造函數用於定義實例屬性,而原型模式用於定義方法和共享的屬性。
//組合使用原型模式和構造函數創建對象(使用最廣泛、認同度最高) //構造函數用於定義實例屬性,而原型模式用於定義方法和共享的屬性 function Person(name,age){ this.name = name; this.age = age; this.friends = ["liu","li"]; } Person.prototype = { constructor : Person, sayName : function(){ console.log(this.name); } } var person1 = new Person("guo", 24); var person2 = new Person("zhu", 30); person1.friends.push("yang"); console.log(person1.friends); //[ ‘liu‘, ‘li‘, ‘yang‘ ] console.log(person2.friends); //[ ‘liu‘, ‘li‘ ] person1.sayName();//"guo" person2.sayName();//"zhu"
7.動態原型模式
//動態原型模式(具有更好的封裝性) function Person(name, age){ //屬性 this.name = name; this.age = age; this.friends = ["liu","li"]; //方法 if(typeof this.sayName !="function"){ Person.prototype.sayName=function(){ console.log(this.name); }; } } var person1 = new Person("guo", 24); console.log(person1.friends); //[ ‘liu‘, ‘li‘ ] person1.sayName(); //"guo"
另外還有兩個創建對象的方法,寄生構造函數模式和穩妥構造函數模式。由於這兩個函數不是特別常用,這裏就不給出具體代碼了。有興趣可以查看 js高程P160-162
總結
ECMAScript 支持面向對象(OO)編程,但不使用類或者接口。對象可以在代碼執行過程中創建和增強,因此具有動態性而非嚴格定義的實體。在沒有類的情況下,可以采用下列模式創建對象。
(1)工廠模式,使用簡單的函數創建對象,為對象添加屬性和方法,然後返回對象。這個模式後來被構造函數模式所取代。
(2)構造函數模式,可以創建自定義引用類型,可以像創建內置對象實例一樣使用new 操作符。不過,構造函數模式也有缺點,即它的每個成員都無法得到復用,包括函數。由於函數可以不局限於任何對象(即與對象具有松散耦合的特點),因此沒有理由不在多個對象間共享函數。
(3)原型模式,使用構造函數的prototype 屬性來指定那些應該共享的屬性和方法。組合使用構造函數模式和原型模式時,使用構造函數定義實例屬性,而使用原型定義共享的屬性和方法。
JavaScript創建對象的幾種方式總結