探討在 JavaScript 實作類別的方式
在 javascript 中有很多方式來創建對象,所以創建對象的方式使用起來非常靈活。那麼,到底哪一種方式是最恰當的物件創建方式呢?建構
模式,原型模式還是物件原意模式(Object literal)呢?
但這些模式具體又是怎麼回事呢?
在開始講解之前,讓我們先清楚地介紹一下關於 javascript 基本知識。
有沒有可能在 javascript 中實現物件導向程式設計的方式呢?
答案是可能的,javascript 是可以創建物件的!這種物件可以包含資料及能夠操作資料的方法,甚至可以包含其他物件。它沒有類別但擁有構
造函數;它沒有類別繼承機制,但是可以透過原型(prototype)實現繼承。
現在看起來,我們已經了解了在 javascript 中創建物件及實現基於物件程式設計時所必須的組成部分。
我們都知道 javascript 擁有私有變數。一個透過「var」關鍵字定義的變量,只能在函數體中被訪問,而不能在函數外被訪問。那麼,如果
我們不透過使用「var」關鍵字來定義變數會怎麼樣呢?我們現在不對這個問題進行深入探討,可能是透過「this」進行訪問的,我會在另外的
時間來詳細講述這個問題。
現在回到之前的問題。到底哪一種方式是最恰當的物件創建方式呢?
讓我們用已經知道的知識,透過創建Person的物件是來試驗一下。
[javascript]
var Person = {
firstName : 'John',
message : '',
createFullName : function () {
fullName = this.firstName + ' ' + this.lastName;
},
this.message = msg; }, getMessage : function () { this.createFullName(); return this.message + ' ' + fullName; return this.message + ' ' + fullName; Person.firstName = 'Eli'; Person.lastName = ' Flowers' Person.changeMessage('welcome'); var message = Person.getMessage(); // welcome Eli Flowers alert(message); welcome Eli Flowers alert(message);這非常接近我們經常創建物件的方式。如果你不需要關心私人/包裝的成員,並且你知道不將創建這個對 象的實例。那麼,這種方式將會很適合你。公有的成員可以做所有私有成員的事情,不是嗎?但是,這不是一個類,而是一個物件而已,不能 被創建實例並且不能被繼承。 讓我們嘗試下其他的方面: [javascript] var Person = function() { this. ;
var fullName = '';
this.message = '';
var _that = this;
ate🜠.fullName = _that.firstName + ' ' + _that.lastName;
}
this.changeMessage = function (msg) {
this.getMessage = function () {
createFullName();
return return this. message + ' ' + fullName;
}
}
var person1 = new Person(); person1.lastName = 'Flowers'
person1. changeMessage('welcome');
var message = person1.getMessage(); // welcome Eli Flowers
alert(message);
那麼,這是類別還是物件呢?應該 兩種都算是吧。我們能夠在當請求時把它當做對象Person
來使用。它畢竟也只是一個函數而已。然而,它可以透過使用“new”關鍵字來實現創建新的實例功能。
在使用這種方式時,我們需要時刻記住以下要點:
1. 無論什麼時候這個函數被調用時,它擁有一個特別的變量叫做“this”並且可以在全局範圍內使用。全域範圍依賴這個函數本身的作用
範圍。
2. 無論何時透過「new」關鍵字建立這個函數的實例,「this」變數指向這個函數本身,而這個「new」運算將會影響函數體內的代名詞
碼被執行。這也正是建構模式。
3. 任何附加到「this」變數下的變數都會成為公有屬性並且任何透過「var」關鍵字定義的變數都將是屬於私有屬性。
4. 一個附加到「this」下的函數叫做特權函數,它可以存取所有的私有變數以及附加到「this」下的函數及變數。
5. 私有函數可以存取到其他私有變數及私有函數。
6. 私有函數不能直接存取被附加到「this」變數和函數。我們可以透過創建一個私有變數「_that」並且將它賦值為「this」的方式來實現。
7. 任何私有變數及函數對於其他私有函數及其他被附加到「this」的函數是可用的。這完全是可能的再javascript的作用範圍下。
8. 一個變數:不是透過「var」關鍵字,也不是附加到「this」變數上以獲得全域作用範圍的。例如,對於一個自訂函數的作用範圍。需要
再一次地了解作用域及群聚的知識。
這已經實現了我們想要的大部分要求了,但是,有時「this」和「that」這兩個入口變數很容易造成給人帶來疑惑。尤其對於一直堅
持要求純粹私有的人來說,更容易迷惑。
讓我們再稍微修改下試試看。
[javascript]
var Person = function () {
lastName = 'Cody';
var fullName = '';
var message = '';
var createFullName = function () {. }
//public setters
var setMessage = function (msg ) {
message = msg;
}
¢ ;
}
var setLastName = function (lName) {
lastName = Name
}
var getMessage = function () {
}
//functions exposed public
return { 集setFirstName,
setLastName: setLastName,
setMessage: setMess };
};
var person1 = new setPerson();
var person1 = new setPerson();
Eli');
person1.setLastName('Flowers');
person1.setMessage('welcome');
var message = person1.getMessage();
var message = person1.getMessage();
這是一個顯示模式(Revealing Pattern)。非常感謝 Christian Heilmann。使用這種模式的方式就是把請求的"getters" 和 "setters" 當 作屬性使用。我們很多都是從傳統的Java程式設計中找到這樣的身影並且很明顯地知道實現它其實並不復雜。這同樣是類似當類別繼承自一個 介面的情況。 這種模式大部分方面都實現得很好,只是只有一個很微小的問題。每一次當一個類別的實例被創建時。這個新建立的物件獲得了一個變數和函數的拷貝。現在,拷貝變數是沒有問題的,我們希望每個物件的資料都是屬於物件自身的,那麼,成員函數呢?他們只是操作資料而已
。那麼,為什麼需要拷貝他們呢?
這正是原型模式(Prototype)的優點。在所有實例中,所有東西都是被創建成一個原型,並且能夠相互分享。我們只需要做的就是依照
據原型建立共有函數。
[javascript]
var Person = function () {
var fullName = '';
var firstName = '';
var lastName = "";
var createFullName = function () {
Person.prototype.setFirstName('asdsad'); le
};
//constructor
var Person = function () { }; //will be created evrytime
//public
名詞unction () { createFullName(); return welcomeMessage + ' ' + fullName; }, setFirstName: function (fName) { }, setLastName: function (lName) { lastName = lName; eMessage : function (mesg) { welcomeMessage = mesg; Person(); // Person; //new Person(); }; var person1 = new Person(); person1.setFirstName ('Eli'); person1.setLastName('Flowers'); person1.ChangeMessage('wegeMessage); ; // welcome asdsad Flowers alert(message); 原型模式存在的一個問題是它不能訪問私有變量及私有函數,正因為這個問題,我們才會介紹閉包中存在的程式碼以使得 它在全域範圍內不會變得很混亂。所有都是屬於 Person 類別的作用範圍內。 另一個問題是每個實例被創建時,全部的程式碼都被執行一遍,包括原型的綁定。對我們中的一部分人來說,這僅僅只是一個效率問題。 處理好這個問題的一種方式是僅僅在期望共有函數不可用的情況下綁定這個原型。 這樣將會使得綁定原型操作只會在第一個實例被創建時執行,並且在那之後所有其他的實例都將只會進行檢查操作。不幸的是,這樣還是不能 解決我們在上面例子中提到的問題,因為我們只有重新再來一次創建的函數用於產生一個閉包來達到這個類別的效果。這樣的話,至少我們減少 了一部分記憶體的使用。 等等,還有另一個問題是私有函數不能直接存取原型函數。 為什麼你們一定要需要私有函數和私有變數?我知道你一定是想實現類別的封裝性,想確保類別中的屬性或內部的資料不會被突然地修改了或 者被內部的其他程式所修改,或任何其他的操作… 你應該記住你是不能將javascript 程式碼編譯成二進位的,對於這種情況,你在某種程度上很惱火吧,這樣程式碼總是可用的。所以,如果 任何人想攪亂程式碼的話,不管你真正實現私有或沒有實現私有,不管你將程式碼給團隊中的其他成員或賣出去,他們都可以攪亂程式碼。實現 私有化可能有那麼一點點幫助吧。 另一個其他程式設計者使用的技巧是使用約定命名,使用底線 “_”給所有你想設為私有任何的東西加上前綴以規定它成為私有。 [javascript] (function () {var Person = function () {
this._fullName = '';
this.welcomeMessage = '';
this.firstName = '';
this.lastName = "";
_that = 這個;
_that = 這個; this._createFullName = function () { 含 this._fullName = this.firstName + ' ' + this.lastName; }; } //程式最佳化的共享函數 Person.prototype = { getFullName: function () { this._createFullName(); return this.welcomeMessage + ' ' + this._fullName; }, ChangeMesage: function (mesg) { sage } } this.Person = Person; })(); var person1 = new Person(); person1.firstName = 'Eli'; person1.lastName = '花'; person1.ChangeMesage('歡迎'); var message = person1.getFullName(); // Namaste Eli 花 alert(message); 我不是說你不應該考慮「樹」或類似的知識。 ,你可以使用任何一種設計模式多種設計模式組合在一起使用。洩露,以及優化代碼,並且組織好 代碼。
經常使用javascript,對於它的印像一直都是直接複製過來就可以用的。在文章最後討論了這類中型蠟燭成員的實際情況。