在JavaScript中,建立一個具有自訂屬性和方法的物件有很多種模式
1、直接建立模式。這是最簡單也是最直接的模式,首先建立一個引用類型的對象,然後為其新增自訂屬性和方法。範例程式碼如下:
var person = new Object(); person.name = "Sam"; person.age = 16; person.speak = function(){ alert(this.name + "is " + this.age + "years old"); } person.speak();
可以看到,上面建立了一個Object類型的對象,然後為其新增了name和age屬性以及一個speak方法。直接建立模式雖然簡單,但其缺點是顯而易見的:當我們需要建立許多相同的物件時,每次都要重複編寫程式碼。為了解決這個問題,我們可以將創建物件的過程進行封裝,於是便有了下面的工廠模式。
2、工廠模式。工廠模式是程式設計中常用的設計模式,它主要是將建立物件的過程進行了封裝,範例程式碼如下:
function createPerson(name, age){ var person = new Object(); person.name = name; person.age = age; person.speak = function(){ alert(this.name + "is " + this.age + "years old"); } return person; } var person1 = createPerson("Sam", 16); var person2 = createPerson("Jack", 18);
使用工廠模式後,建立相同類型的物件變得簡單了。但工廠模式沒有解決物件辨識的問題,也就是我們無法確定創建的物件的具體類型。有過物件導向程式設計經驗的開發人員都知道,物件的創建應當基於類,有了具體的自訂類,再來創建該類別的物件。幸好,在JavaScript中,我們可以透過建構函數模式來模擬一個類別。
3、建構函式模式。構造函數和普通函數沒有任何區別。任何普通函數都可以當作建構函數,只要使用new運算元即可;任何建構函數也都可以當作普通函數來呼叫。只不過在JavaScript中,有一個約定,就是用來作為建構函數的函數名稱需要一個首字母大寫。範例程式碼如下:
function Person(name, age){ this.name = name; this.age = age; this.speak = function(){ alert(this.name + "is " + this.age + "years old"); } } var person1 = new Person("Sam", 16); var person2 = new Person("Jack", 18);
可以看到,在建構函式內部,我們使用了this來新增屬性和方法,那麼,這個this是指什麼呢?當我們建立了一個Person的物件時,this也就是指這個被建立的物件。現在,我們可以辨識出物件person1和person2的具體類型了。使用alert(person1 instanceOf Person)後可以發現,輸出的值為true。但建構函式模式也有自己的缺點,就是建構函式內宣告的方法在每次建立新物件時都會重新建立(在JavaScript中,函數也是物件)。也就是說,建構函式內的方法是與物件綁定的,而不是與類別綁定的。下面程式碼的輸出可以驗證我們的推斷。
alert(person1.speak == person2.speak); // false 解決這個缺點的一個比較簡單的方法就是將函數的宣告放到建構子的外面,即:
function Person(name, age){ this.name = name; this.age = age; this.speak = speak; } function speak(){ alert(this.name + "is " + this.age + "years old"); } var person1 = new Person("Sam", 16); var person2 = new Person("Jack", 18); alert(person1.speak == person2.speak); // true
問題解決了,但這種方法又帶來了新的問題。首先,函數speak是在全域作用域中宣告的,但它卻只能被用於Person建構函數,放在全域作用域中有被誤用的風險;其次,如果一個自訂型別有很多的方法,則需要宣告很多的全域函數,這不僅會導致全域作用域的污染,也不利於程式碼的封裝。那麼,有沒有辦法能讓自訂類型的方法成為與類別綁定的,又不污染全域作用域呢?答案是使用原型模式。
4、原型模式。在我們宣告一個新的函數後,該函數(在JavaScript中,函數也是物件)就會擁有一個prototype的屬性。 prototype是一個對象,表示會被該函數所建立的所有物件擁有的公共屬性和方法。範例程式碼如下:
function Person(){} Person.prototype.name="Sam"; Person.prototype.age=16; Person.prototype.speak = function(){ alert(this.name + "is " + this.age + "years old"); } var person1 = new Person(); person1.speak(); var person2 = new Person(); alert(person1.speak == person2.speak); // true
可以看到,雖然建構函式內沒有宣告speak方法,但我們建立的物件person1還是能呼叫speak方法,這是因為JavaScript有一個搜尋規則,先搜尋實例屬性和方法,找到則回傳;如果沒找到,則再到prototype中去搜尋。原型模式使得方法是與類別相關的,並且沒有污染全域作用域,但其也有自身的缺點:一是所有屬性也都與類別相關,這意味著所有物件共享一份屬性,這顯然是不合理的;二是沒有辦法向構造函數傳入初始化資料了。解決的方法很簡單,就是混合使用建構函式模式和原型模式。
5、組合模式。範例程式碼如下:
function Person(name, age){ this.name = name; this.age = age; } Person.prototype.speak = function(){ alert(this.name + "is " + this.age + "years old"); } var person1 = new Person(); person1.speak(); var person2 = new Person(); alert(person1.speak == person2.speak); // true
不難發現,組合模式實現了我們的所有需求,這也是目前應用得比較廣泛的一種模式。有物件導向程式設計經驗的開發人員可能會覺得將prototype的聲明放在建構函式外面有點彆扭,那麼能否將其放到建構函式裡去呢?答案是肯定的,使用動態組合模式即可。
6、動態組合模式。其原理就是先判斷原型中的某個屬性或方法是不是已經聲明過,如果沒有聲明,則聲明整個原型;否則,什麼也不用做。範例程式碼如下:
function Person(name, age){ this.name = name; this.age = age; if (Person.prototype.speak == "undefined"){ Person.prototype.speak = function(){ alert(this.name + "is " + this.age + "years old"); } } }
以上是JavaScript自訂屬性和方法的物件模型程式碼詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!