DIV CSS 佈局教程網

 DIV+CSS佈局教程網 >> 網頁腳本 >> JavaScript入門知識 >> 關於JavaScript >> 在JavaScript中實現類的方式探討
在JavaScript中實現類的方式探討
編輯:關於JavaScript     
在 javascript 中有很多方式來創建對象,所以創建對象的方式使用起來非常靈活。那麼,到底哪一種方式是最恰當的對象創建方式呢?構造模式,原型模式還是對象原意模式(Object literal)呢?

但這些模式具體是怎麼回事呢?

在開始講解之前,讓我們先清楚地介紹一下關於 javascript 基本知識。

有沒有可能在 javascript 中實現面向對象編程的方式呢?

答案是可能的,javascript 是可以創建對象的!這種對象可以包含數據及能夠操作數據的方法,甚至可以包含其他對象。它沒有類但擁有構造函數;它沒有類繼承機制,但是可以通過原型(prototype)實現繼承。

現在看起來,我們已經了解了在 javascript 中創建對象及實現基於對象編程時所必須的組成部分。

我們都知道 javascript 擁有私有變量。一個通過“var”關鍵字定義的變量,只能在函數體中被訪問,而不能在函數外被訪問。那麼,如果我們不通過使用“var”關鍵字來定義變量會怎樣呢?我們現在不對這個問題進行深入探討,可能是通過“this”進行訪問的,我會在另外的時間來詳細講述這個問題。

現在回到之前的問題。到底哪一種方式是最恰當的對象創建方式呢?
讓我們用已經知曉的知識,通過創建Person的對象是來試驗一下。
復制代碼 代碼如下:
var Person = {
firstName : 'John',
lastName : 'Cody',
fullName : '',
message : '',

createFullName : function () {
fullName = this.firstName + ' ' + this.lastName;
},

changeMessage : function (msg) {
this.message = msg;
},

getMessage : function () {
this.createFullName();
return this.message + ' ' + fullName;
}
}

Person.firstName = 'Eli';
Person.lastName = 'Flowers'
Person.changeMessage('welcome');
var message = Person.getMessage(); // welcome Eli Flowers
alert(message);

這是對象原意模式(literal pattern)。這非常接近我們常創建對象的方式。如果你不需要關心私有/包裝的成員,並且你知道不將創建這個對象的實例。那麼,這種方式將會很適合你。公有的成員可以做所有私有成員的事情,不是嗎?但是,這不是一個類,而是一個對象而已,不能被創建實例並且不能被繼承。

讓我們嘗試下其他的方面:
復制代碼 代碼如下:
var Person = {
firstName : 'John',
lastName : 'Cody',
fullName : '',
message : '',

createFullName : function () {
fullName = this.firstName + ' ' + this.lastName;
},

changeMessage : function (msg) {
this.message = msg;
},

getMessage : function () {
this.createFullName();
return this.message + ' ' + fullName;
}
}

Person.firstName = 'Eli';
Person.lastName = 'Flowers'
Person.changeMessage('welcome');
var message = Person.getMessage(); // welcome Eli Flowers
alert(message);

這是一種構造模式的實例(Constructor Pattern)。那麼,這是類還是對象呢?應該 兩種都算是吧。我們能夠在當請求時把它當做對象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”這兩個入口變量很容易造成給人們帶來疑惑。尤其對於那些一直堅持要求純粹私有的人來說,更容易迷惑。

讓我們再稍微修改下試試吧。
復制代碼 代碼如下:
var Person = function () {

//private
var firstName = 'John';
var lastName = 'Cody';
var fullName = '';
var message = '';


var createFullName = function () {
fullName = firstName + ' ' + lastName;
}

//public setters
var setMessage = function (msg) {
message = msg;
}

var setFirstName = function (fName) {
firstName = fName;
}

var setLastName = function (lName) {
lastName = lName;
}

var getMessage = function () {
createFullName();
return message + ' ' + fullName;
}

//functions exposed public
return {
setFirstName: setFirstName,
setLastName: setLastName,
setMessage: setMessage,
getMessage: getMessage
};

};

var person1 = new Person();
person1.setFirstName('Eli');
person1.setLastName('Flowers');
person1.setMessage('welcome');
var message = person1.getMessage(); // welcome Eli Flowers
alert(message);

這是一個顯示模式(Revealing Pattern)。非常感謝 Christian Heilmann。使用這種模式的方式就是把請求的"getters" 和 "setters" 當作屬性使用。我們很多都是從傳統的Java編程中找到這樣的身影並且很明顯地知道實現它其實並不復雜。這同樣是一種類似於當類繼承自一個接口的情況。

這種模式大部分方面都實現得很好,僅僅只有一個很微小的問題。每一次當一個類的實例被創建時。這個新創建的對象獲得了一份變量和函數的拷貝。現在,拷貝變量是沒有問題的,我們希望對於每一個對象的數據都是屬於對象自身的,那麼,成員函數呢?他們僅僅是操作數據而已。那麼,為什麼需要拷貝他們呢?

這正是原型模式(Prototype)的優勢所在。在所有實例中,所有東西都是被創建成一個原型,並且能夠相互分享。我們僅僅需要做的就是依據原型創建共有函數。
復制代碼 代碼如下:
var Person = function () {

//private
var welcomeMessage = 'welcome';
var fullName = '';
var firstName = '';
var lastName = "";
var createFullName = function () {
Person.prototype.setFirstName('asdsad');
fullName = firstName + ' ' + lastName;
};

//constructor
var Person = function () { }; //will be created evrytime

//public
Person.prototype = {
getFullName: function () {
createFullName();
return welcomeMessage + ' ' + fullName;
},
setFirstName: function (fName) {
firstName = fName;
},
setLastName: function (lName) {
lastName = lName;
},
ChangeMessage: function (mesg) {
welcomeMessage = mesg;
}
}

return new Person(); // Person; //new Person();
};


var person1 = new Person();
person1.setFirstName ('Eli');
person1.setLastName('Flowers');
person1.ChangeMessage('welcome');
var message = person1.getFullName(); // welcome asdsad Flowers
alert(message);

原型模式存在的一個問題是它不能訪問私有變量及私有函數,正因為這個問題,我們才會介紹閉包以及始終組織好創建類中存在的代碼以使得它在全局范圍內不會變得很混亂。所有都是屬於 Person 類的作用范圍內。

另外一個問題是每一次實例被創建時,全部的代碼都被執行一遍,包括原型的綁定。對於我們中的一部分人來說,這僅僅只是一個效率問題。處理好這個問題的一種方式是僅僅在期望共有函數不可用的情況下綁定這個原型。

這樣將會使得綁定原型操作只會在第一個實例被創建時執行,並且在那之後所有其他的實例都將只會進行檢查操作。不幸的是,這樣還是不能解決我們在上面例子中提到的問題,因為我們只有重新再來一次創建的函數用於生成一個閉包來達到這個類的效果。這樣的話,至少我們減少了一部分內存的使用。

等等,還有另外一個問題是私有函數不能直接訪問原型函數。

為什麼你們一定得需要私有函數和私有變量呢?我知道你一定是想實現類的封裝性,想確保類中的屬性或者內部的數據不會被突然地修改了或者被內部的其他程序所修改,或者任何其他的操作……

你應該記住你是不能將 javascript 代碼編譯成二進制的,對於這種情況,你在一定程度上很惱火吧,這樣代碼始終都是可用的。所以,如果任何人想攪亂代碼的話,不管你真正實現私有或者沒有實現私有,不管你將代碼給團隊中的其他成員或者賣出去,他們都可以攪亂代碼。實現私有化可能有那麼一點點幫助吧。

另一個其他編程者使用的技術是使用約定命名,使用下劃線 “_”給所有你想設成私有任何的東西加上前綴以規定它成為私有。
復制代碼 代碼如下:
(function () {
var Person = function () {
this._fullName = '';
this.welcomeMessage = '';
this.firstName = '';
this.lastName = "";
_that = this;

this._createFullName = function () {
this.ChangeMessage('Namaste');
this._fullName = this.firstName + ' ' + this.lastName;
};
}

//Shared Functions for Code optimization
Person.prototype = {
constructor: Person,
getFullName: function () {
this._createFullName();
return this.welcomeMessage + ' ' + this._fullName;
},
ChangeMessage: function (mesg) {
this.welcomeMessage = mesg;
}
}

this.Person = Person;
})();

var person1 = new Person();
person1.firstName = 'Eli';
person1.lastName = 'Flowers';
person1.ChangeMessage('Welcome');
var message = person1.getFullName(); // Namaste Eli Flowers
alert(message);

我不是說你不應該考慮 “private” 或者類似的知識。你是代碼的設計者,所以你將知道怎麼來管理並且知道怎麼做才是最好的。根據你的需求,你可以使用任何一種設計模式或者多個設計模式組合一起使用。

無論你決定采用哪種設計模式,始終記住做盡量少的事情,不要在全局作用范圍內實現閉包,盡量減少內存洩露,以及優化代碼,並且組織好代碼。所以,盡量多了解些作用域,閉包以及 “this” 的表現行為。

最後,祝編程愉快!

譯後感

經常使用 javascript,對於它的印象一直都是直接拷貝過來就可以用的。最近使用 extjs,它的類框架非常好用。從這樣文章也明白在 javascript 中實現類的各種方式,以及在文章最後討論了類中私有成員的實現情況。
XML學習教程| jQuery入門知識| AJAX入門| Dreamweaver教程| Fireworks入門知識| SEO技巧| SEO優化集錦|
Copyright © DIV+CSS佈局教程網 All Rights Reserved