關於JavaScript定義類和對象的幾種方式

來源:互聯網
上載者:User

可以看看這個例子: 複製代碼 代碼如下:var a = 'global';
(function () {
alert(a);
var a = 'local';
})();

  大家第一眼看到這個例子覺得輸出結果是什嗎?‘global'?還是‘local'?其實都不是,輸出的是undefined,不用迷惑,我的題外話就是為了講這個東西的。
其實很簡單,看一看JavaScript運行機制就會明白。我們可以把這種現象看做“預聲明”。但是如果稍微深究一下,會明白得更透徹。
  這裡其實涉及到對象屬性綁定機制。因為所有JavaScript函數都是一個對象。在函數裡聲明的變數可以看做這個對象的“類似屬性”。對象屬性的綁定在語言裡是有分“早綁定”和“晚綁定”之分的。

  【早綁定】
是指在執行個體化對象之前定義其屬性和方法。解析程式時可以提前轉換為機器代碼。通常的強型別語言如C++,java等,都是早綁定機制的。而JavaScript不是強型別語言。它使用的是“晚綁定”機制。
  【晚綁定】
是指在程式運行前,無需檢查物件類型,只要檢查對象是否支援特性和方法即可。可以在綁定前對對象執行大量操作而不受任何懲罰。
上面代碼出現的“預聲明”現象,我們大可用“晚綁定”機制來解釋。在函數的範圍中,所有變數都是“晚綁定”的。 即聲明是頂級的。所以上面的代碼和下面的一致: 複製代碼 代碼如下:var a = 'global';
(function () {
var a;
alert(a);
a = 'local';
})();

在alert(a)之前只對a作了聲明而沒有賦值。所以結果可想而知。

<!-- 題外話到此結束 -->
  RT:本文要說的是,在JavaScript裡,我所知道的幾種定義類和對象的方式:<! -- 聲明:以下內容大部分來自《JavaScript進階程式設計》,只是個人敘述方式不同而已 -- >
  【直接量方式】
  使用直接量構建對象是最基礎的方式,但也有很多弊端。 複製代碼 代碼如下:var Obj = new Object;
Obj.name = 'sun';
Obj.showName = function() {
alert('this.name');
}

我們構建了一個對象Obj,它有一個屬性name,一個方法showName。但是如果我們要再構建一個類似的對象呢?難道還要再重複一遍?
NO!,我們可以用一個返回特定類型對象的工廠函數來實現。就像工廠一樣,流水線的輸出我們要的特定類型結果。
  【工廠方式】 複製代碼 代碼如下:function createObj(name) {
var tempObj = new Object;
tempObj.name = name;
tempObj.showName = function () {
alert(this.name);
};
return tempObj;
}
var obj1 = createObj('obj_one');
var obj2 = createObj('obj_two');

這種工廠函數很多人是不把他當做構建對象的一種形式的。一部分原因是語義:即它並不像使用了運算子new來構建的那麼正規。還有一個更大的原因,是因為這個工廠每次產出一個對象都會建立一個新函數showName(),即每個對象擁有不同的版本,但實際上他們共用的是同一個函數。
有些人把showName在工廠函數外定義,然後通過屬性指向該方法,可以避開這個問題: 複製代碼 代碼如下:function showName () {
alert(this.name);
}
function createObj(name) {
var tempObj = new Object;
tempObj.name = name;
tempObj.showName = showName;
return tempObj;
}
var obj1 = createObj('obj_one');
var obj2 = createObj('obj_two');

可惜的是,這種方式讓showName()這個函數看起來不像對象的一個方法。
  【建構函式方式】
這種方式是為瞭解決上面工廠函數的第一個問題,即沒有new運算子的問題。可是第二個問題它依然不能解決。我們來看看。 複製代碼 代碼如下:function Obj(name) {
this.name = name;
this.showName = function () {
alert(this.name);
}
}
var obj1 = new Obj('obj_one');
var obj2 = new Obj('obj_two');

它的好處是不用在建構函式內建立一個對象了,因為new運算子執行的時候會自動建立一個對象,並且只有通過this才能訪問這個對象。所以我們可以直接通過this來對這個對象進行賦值。而且不用再return,因為this指向預設為建構函式的傳回值。
同時,用了new關鍵字來建立我們想要的對象是不是感覺更“正式”了。
可惜,它仍然不能解決會重複產生方法函數的問題,這個情況和工廠函數一樣。

  【原型方式】
這種方式對比以上方式,有個很大的優勢,就是它解決了方法函數會被產生多次的問題。它利用了對象的prototype屬性。我們依賴原型可以重寫對象執行個體。 複製代碼 代碼如下:var Obj = function () {}
Obj.prototype.name = 'me';
Obj.prototype.showName = function () {
alert(this.name);
}
var obj1 = new Obj();
var obj2 = new Obj();

我們依賴原型對建構函式進行重寫,無論是屬性還是方法都是通過原型引用的方式給建立的對象,因此都只會被建立一次。可惜的是,這種方式存在兩個致命的問題:
1。沒辦法在構建對象的時候就寫入想要的屬性,因為原型在建構函式範圍外邊,沒辦法通過傳遞參數的方式在對象建立的時候就寫入屬性值。只能在對象建立完畢後對值進行重寫。
2。致命問題在於當屬性指向對象時,這個對象會被多個執行個體所共用。考慮下面的代碼: 複製代碼 代碼如下:var Obj = function () {}
Obj.prototype.name = 'me';
Obj.prototype.flag = new Array('A', 'B');
Obj.prototype.showName = function () {
alert(this.name);
}
var obj1 = new Obj();
var obj2 = new Obj();
obj1.flag.push('C');
alert(obj1.flag); // A,B,C
alert(obj2.flag); //A,B,C

是的,當flag屬性指向對象時,那麼執行個體obj1和obj2都共用它,哪怕我們僅僅改變了obj1的flag屬性,但是它的改變在執行個體obj2中任然可見。
面對這個問題,讓我們不得不想是否應該把【建構函式方式】和【原型方式】結合起來,讓他們互補。。。

  【建構函式和原型混合方式】
我們讓屬性用建構函式方式建立,方法用原型方式建立即可: 複製代碼 代碼如下:var Obj = function (name) {
this.name = name;
this.flag = new Array('A', 'B');
}
Obj.prototype = {
showName : function () {
alert(this.name);
}
}
var obj1 = new Obj();
var obj2 = new Obj();
obj1.flag.push('C');
alert(obj1.flag); // A,B,C
alert(obj2.flag); //A,B

這種方式有效地結合了原型和建構函式的優勢,是目前用的最多,也是副作用最少的方式。
不過,有些追求完美的傢伙還不滿足,因為在視覺上還沒達到他們的要求,因為通過原型來建立方法的過程在視覺上還是會讓人覺得它不太像執行個體的方法(尤其對於傳統OOP語言的開發人員來說。)
所以,我們可以讓原型活動起來,讓他也加入到建構函式裡面去,好讓這個建構函式在視覺上更為統一。而這一系列的過程只需用一個判斷即可完成。 複製代碼 代碼如下:var Obj = function (name) {
this.name = name;
this.flag = new Array('A', 'B');
if (typeof Obj._init == 'undefined') {
Obj.prototype = {
showName : function () {
alert(this.name);
}
};
Obj._init = true;
}
}

如上,用_init作為一個標誌來判斷是否已經給原型建立了方法。如果是那麼就不再執行。這樣其實在本質上是沒有任何變化的,方法仍是通過原型建立,唯一的區別在於這個建構函式看起來“江山統一”了。
但是這種動態原型的方式是有問題的,《JavaScript進階程式設計》裡並沒有深究。建立第一個對象的時候會因為prototype在對象執行個體化之前沒來的及建起來,是根本無法訪問的。所以第一個對象是無法訪問原型方法的。同時這種方式在子類繼承中也會有問題。
關於解決方案,我會在下一文中說明。

其實就使用方便來說的話,個人覺得是沒必要做這個判斷的。。。呵呵 ^_^

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.