跟我學(xué)習(xí)javascript創(chuàng)建對象(類)的8種方法
8中javascript創(chuàng)建對象(類)的方法,依依介紹給大家,希望大家喜歡。
1. 使用Object構(gòu)造函數(shù)來創(chuàng)建一個對象
下面代碼創(chuàng)建了一個person對象,并用兩種方式打印出了Name的屬性值。
var person = new Object(); person.name="kevin"; person.age=31; alert(person.name); alert(person["name"])
上述寫法的另外一種表現(xiàn)形式是使用對象字面量創(chuàng)建一個對象,不要奇怪person[“5”],這里是合法的;另外使用這種加括號的方式字段之間是可以有空格的如person[“my age”].
var person = { name:"Kevin", age:31, 5:"Test" }; alert(person.name); alert(person["5"]);
雖然Object 構(gòu)造函數(shù)或?qū)ο笞置媪慷伎梢杂脕韯?chuàng)建單個對象,但這些方式有個明顯的缺點:使用同一個接口創(chuàng)建很多對象,會產(chǎn)生大量的重復(fù)代碼。為解決這個問題,人們開始使用工廠模式的一種變體。
2、工廠模式
工廠模式是軟件工程領(lǐng)域一種廣為人知的設(shè)計模式,這種模式抽象了創(chuàng)建具體對象的過程,考慮到在ECMAScript 中無法創(chuàng)建類,開發(fā)人員就發(fā)明了一種函數(shù),用函數(shù)來封裝以特定接口創(chuàng)建對象的細節(jié),如下面的例子所示。
function createPerson(name, age, job){ var o = new Object(); o.name = name; o.age = age; o.job = job; o.sayName = function(){ alert(this.name); }; return o; } var person1 = createPerson("Nicholas", 29, "Software Engineer"); var person2 = createPerson("Greg", 27, "Doctor");
工廠模式雖然解決了創(chuàng)建多個相似對象的問題,但卻沒有解決對象識別的問題(即怎樣知道一個對象的類型)。隨著JavaScript
的發(fā)展,又一個新模式出現(xiàn)了。
3. 構(gòu)造函數(shù)模式
像Object 和Array 這樣構(gòu)造函數(shù),在運行時會自動出現(xiàn)在執(zhí)行環(huán)境中。此外,也可以創(chuàng)建自定義的構(gòu)造函數(shù),從而定義自定義對象類型的屬性和方法。例如,可以使用構(gòu)造函數(shù)模式將前面的例子重寫如下?!?/p>
function Person(name, age, job){ this.name = name; this.age = age; this.job = job; this.sayName = function(){ alert(this.name); }; } var person1 = new Person("Nicholas", 29, "Software Engineer"); var person2 = new Person("Greg", 27, "Doctor");
在這個例子中,Person()函數(shù)取代了createPerson()函數(shù)。我們注意到,Person()中的代碼除了與createPerson()中相同的部分外,還存在以下不同之處:
- 沒有顯式地創(chuàng)建對象;
- 直接將屬性和方法賦給了this 對象;
- 沒有return 語句。
要創(chuàng)建Person 的新實例,必須使用new 操作符。以這種方式調(diào)用構(gòu)造函數(shù)實際上會經(jīng)歷以下4個步驟:
(1) 創(chuàng)建一個新對象;
(2) 將構(gòu)造函數(shù)的作用域賦給新對象(因此this 就指向了這個新對象);
(3) 執(zhí)行構(gòu)造函數(shù)中的代碼(為這個新對象添加屬性);
(4) 返回新對象。
在前面例子的最后,person1 和person2 分別保存著Person 的一個不同的實例。這兩個對象都有一個constructor(構(gòu)造函數(shù))屬性,該屬性指向Person,如下所示。
alert(person1.constructor == Person); //true alert(person2.constructor == Person); //true
對象的constructor 屬性最初是用來標識對象類型的。但是,提到檢測對象類型,還是instanceof操作符要更可靠一些。我們在這個例子中創(chuàng)建的所有對象既是Object 的實例,同時也是Person的實例,這一點通過instanceof 操作符可以得到驗證。
alert(person1 instanceof Object); //true alert(person1 instanceof Person); //true alert(person2 instanceof Object); //true alert(person2 instanceof Person); //true
創(chuàng)建自定義的構(gòu)造函數(shù)意味著將來可以將它的實例標識為一種特定的類型;而這正是構(gòu)造函數(shù)模式勝過工廠模式的地方。在這個例子中,person1 和person2 之所以同時是Object 的實例,是因為所有對象均繼承自O(shè)bject。
構(gòu)造函數(shù)的問題
構(gòu)造函數(shù)模式雖然好用,但也并非沒有缺點。使用構(gòu)造函數(shù)的主要問題,就是每個方法都要在每個實例上重新創(chuàng)建一遍。
ECMAScript 中的函數(shù)是對象,因此每定義一個函數(shù),也就是實例化了一個對象。從邏輯角度講,此時的構(gòu)造函數(shù)也可以這樣定義。
function Person(name, age, job){ this.name = name; this.age = age; this.job = job; this.sayName = new Function("alert(this.name)"); // 與聲明函數(shù)在邏輯上是等價的 }
從這個角度上來看構(gòu)造函數(shù),更容易明白每個Person 實例都包含一個不同的Function 實例(以顯示name 屬性)的本質(zhì)。說明白些,以這種方式創(chuàng)建函數(shù),會導(dǎo)致不同的作用域鏈和標識符解析,但創(chuàng)建Function 新實例的機制仍然是相同的。因此,不同實例上的同名函數(shù)是不相等的,以下代碼可以證明這一點。
alert(person1.sayName == person2.sayName); //false
然而,創(chuàng)建兩個完成同樣任務(wù)的Function 實例的確沒有必要;況且有this 對象在,根本不用在執(zhí)行代碼前就把函數(shù)綁定到特定對象上面。因此,大可像下面這樣,通過把函數(shù)定義轉(zhuǎn)移到構(gòu)造函數(shù)外部來解決這個問題。
function Person(name, age, job){ this.name = name; this.age = age; this.job = job; this.sayName = sayName; } function sayName(){ alert(this.name); } var person1 = new Person("Nicholas", 29, "Software Engineer"); var person2 = new Person("Greg", 27, "Doctor");
如果對象需要定義很多方法,那么就要定義很多個全局函數(shù),于是我們這個自定義的引用類型就絲毫沒有封裝性可言了。好在,這些問題可以通過使用原型模式來解決。
4、原型模式
function Person(){ } Person.prototype.name = "Nicholas"; Person.prototype.age = 29; Person.prototype.job = "Software Engineer"; Person.prototype.sayName = function(){ alert(this.name); }; var person1 = new Person(); person1.sayName(); //"Nicholas" var person2 = new Person(); person2.sayName(); //"Nicholas" alert(person1.sayName == person2.sayName); //true
要理解原型對象,可見我的另一篇博客:JavaScript prototype詳解
前面例子中每添加一個屬性和方法就要敲一遍Person.prototype。為減少不必要的輸入,也為了從視覺上更好地封裝原型的功能,更常見的做法是用一個包含所有屬性和方法的對象字面量來重寫整個原型對象,如下面的例子所示。
function Person(){ } Person.prototype = { name : "Nicholas", age : 29, job: "Software Engineer", sayName : function () { alert(this.name); } };
在上面的代碼中,我們將Person.prototype 設(shè)置為等于一個以對象字面量形式創(chuàng)建的新對象。最終結(jié)果相同,但有一個例外:constructor 屬性不再指向Person 了。前面曾經(jīng)介紹過,每創(chuàng)建一個函數(shù),就會同時創(chuàng)建它的prototype 對象,這個對象也會自動獲得constructor 屬性。而我們在這里使用的語法,本質(zhì)上完全重寫了默認的prototype 對象,因此constructor 屬性也就變成了新對象的constructor 屬性(指向Object 構(gòu)造函數(shù)),不再指向Person 函數(shù)。此時,盡管instanceof操作符還能返回正確的結(jié)果,但通過constructor 已經(jīng)無法確定對象的類型了,如下所示。
var friend = new Person(); alert(friend instanceof Object); //true alert(friend instanceof Person); //true alert(friend.constructor == Person); //false alert(friend.constructor == Object); //true
在此,用instanceof 操作符測試Object 和Person 仍然返回true,但constructor 屬性則等于Object 而不等于Person 了。如果constructor 的值真的很重要,可以像下面這樣特意將它設(shè)置回適當?shù)闹怠?/p>
function Person(){ } Person.prototype = { constructor : Person, name : "Nicholas", age : 29, job: "Software Engineer", sayName : function () { alert(this.name); } };
需要注意一點就是:實例中的指針僅指向原型,而不指向構(gòu)造函數(shù)。
原型對象的問題:原型模式也不是沒有缺點。首先,它省略了為構(gòu)造函數(shù)傳遞初始化參數(shù)這一環(huán)節(jié),結(jié)果所有實例在默認情況下都將取得相同的屬性值。雖然這會在某種程度上帶來一些不方便,但還不是原型的最大問題。原型模式的最大問題是由其共享的本性所導(dǎo)致的。
function Person(){ } Person.prototype = { constructor: Person, name : "Nicholas", age : 29, job : "Software Engineer", friends : ["Shelby", "Court"], sayName : function () { alert(this.name); } }; var person1 = new Person(); var person2 = new Person(); person1.friends.push("Van"); alert(person1.friends); //"Shelby,Court,Van" alert(person2.friends); //"Shelby,Court,Van" alert(person1.friends === person2.friends); //true
5、組合使用構(gòu)造函數(shù)模式和原型模式(最常用)
創(chuàng)建自定義類型的最常見方式,就是組合使用構(gòu)造函數(shù)模式與原型模式。構(gòu)造函數(shù)模式用于定義實例屬性,而原型模式用于定義方法和共享的屬性。結(jié)果,每個實例都會有自己的一份實例屬性的副本,但同時又共享著對方法的引用,最大限度地節(jié)省了內(nèi)存。另外,這種混成模式還支持向構(gòu)造函數(shù)傳遞參數(shù);可謂是集兩種模式之長。
function Person(name, age, job){ this.name = name; this.age = age; this.job = job; this.friends = ["Shelby", "Court"]; } Person.prototype = { constructor : Person, sayName : function(){ alert(this.name); } } var person1 = new Person("Nicholas", 29, "Software Engineer"); var person2 = new Person("Greg", 27, "Doctor"); person1.friends.push("Van"); alert(person1.friends); //"Shelby,Count,Van" alert(person2.friends); //"Shelby,Count" alert(person1.friends === person2.friends); //false alert(person1.sayName === person2.sayName); //true
6、動態(tài)原型模式
有其他OO 語言經(jīng)驗的開發(fā)人員在看到獨立的構(gòu)造函數(shù)和原型時,很可能會感到非常困惑。動態(tài)原型模式正是致力于解決這個問題的一個方案,它把所有信息都封裝在了構(gòu)造函數(shù)中,而通過在構(gòu)造函數(shù)中初始化原型(僅在必要的情況下),又保持了同時使用構(gòu)造函數(shù)和原型的優(yōu)點。換句話說,可以通過檢查某個應(yīng)該存在的方法是否有效,來決定是否需要初始化原型。來看一個例子。
function Person(name, age, job){ //屬性 this.name = name; this.age = age; this.job = job; //方法 --------------------------------------------- if (typeof this.sayName != "function"){ Person.prototype.sayName = function(){ alert(this.name); }; } -------------------------------------------- } var friend = new Person("Nicholas", 29, "Software Engineer"); friend.sayName();
7、寄生構(gòu)造函數(shù)模式
通常,在前述的幾種模式都不適用的情況下,可以使用寄生(parasitic)構(gòu)造函數(shù)模式。這種模式的基本思想是創(chuàng)建一個函數(shù),該函數(shù)的作用僅僅是封裝創(chuàng)建對象的代碼,然后再返回新創(chuàng)建的對象;但從表面上看,這個函數(shù)又很像是典型的構(gòu)造函數(shù)。下面是一個例子。
function Person(name, age, job){ var o = new Object(); o.name = name; o.age = age; o.job = job; o.sayName = function(){ alert(this.name); }; return o; } var friend = new Person("Nicholas", 29, "Software Engineer"); friend.sayName(); //"Nicholas"
在這個例子中,Person 函數(shù)創(chuàng)建了一個新對象,并以相應(yīng)的屬性和方法初始化該對象,然后又返回了這個對象。除了使用new 操作符并把使用的包裝函數(shù)叫做構(gòu)造函數(shù)之外,這個模式跟工廠模式其實是一模一樣的。構(gòu)造函數(shù)在不返回值的情況下,默認會返回新對象實例。
8、穩(wěn)妥構(gòu)造函數(shù)模式
所謂穩(wěn)妥對象,指的是沒有公共屬性,而且其方法也不引用this 的對象。穩(wěn)妥對象最適合在一些安全的環(huán)境中(這些環(huán)境中會禁止使用this 和new),或者在防止數(shù)據(jù)被其他應(yīng)用程序(如Mashup程序)改動時使用。穩(wěn)妥構(gòu)造函數(shù)遵循與寄生構(gòu)造函數(shù)類似的模式,但有兩點不同:一是新創(chuàng)建對象的實例方法不引用this;二是不使用new 操作符調(diào)用構(gòu)造函數(shù)。按照穩(wěn)妥構(gòu)造函數(shù)的要求,可以將前面的Person 構(gòu)造函數(shù)重寫如下。
function Person(name, age, job){ //創(chuàng)建要返回的對象 var o = new Object(); //可以在這里定義私有變量和函數(shù) //添加方法 o.sayName = function(){ alert(name); }; //返回對象 return o; }
以上javascript創(chuàng)建對象(類)的8種方法大家有沒有學(xué)會,希望對大家的學(xué)習(xí)有所幫助。
- JS創(chuàng)建類和對象的兩種不同方式
- JavaScript 創(chuàng)建對象和構(gòu)造類實現(xiàn)代碼
- JavaScript 常見對象類創(chuàng)建代碼與優(yōu)缺點分析
- JS OOP包機制,類創(chuàng)建的方法定義
- JavaScript創(chuàng)建類/對象的幾種方式概述及實例
- 創(chuàng)建js對象和js類的方法匯總
- Javascript創(chuàng)建類和對象詳解
- JavaScript構(gòu)造函數(shù)詳解
- JS面向?qū)ο蠡A(chǔ)講解(工廠模式、構(gòu)造函數(shù)模式、原型模式、混合模式、動態(tài)原型模式)
- JS中的構(gòu)造函數(shù)詳細解析
- 深入理解javascript構(gòu)造函數(shù)和原型對象
- JavaScript 面向?qū)ο蟪绦蛟O(shè)計詳解【類的創(chuàng)建、實例對象、構(gòu)造函數(shù)、原型等】
相關(guān)文章
JavaScript不使用prototype和new實現(xiàn)繼承機制
這篇文章主要介紹了JavaScript不使用prototype和new實現(xiàn)繼承機制的相關(guān)資料,需要的朋友可以參考下2014-12-12js實現(xiàn)的鼠標滾輪滾動切換頁面效果(類似360默認頁面滾動切換效果)
這篇文章主要介紹了js實現(xiàn)的鼠標滾輪滾動切換頁面效果,類似360默認頁面滾動切換效果.涉及JavaScript響應(yīng)鼠標事件動態(tài)變換頁面元素的相關(guān)技巧,需要的朋友可以參考下2016-01-01躲避這些會改變原數(shù)組JavaScript數(shù)組方法讓開發(fā)流暢無阻
JavaScript中有些數(shù)組的操作方法并不符合我們預(yù)期,容易導(dǎo)致想象不到的結(jié)果,因此,為避免這種情況的發(fā)生,本文將介紹哪些原生數(shù)組方法能改變原數(shù)組以及我對于如何更好地使用數(shù)組方法的建議2023-05-05networkInformation.downlink測用戶網(wǎng)速方法詳解
這篇文章主要為大家介紹了networkInformation.downlink測用戶網(wǎng)速方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-05-05