優雅的封裝仍是執行的效率?這是一個悖論。
優雅封裝的程序看起來是那麼的美妙:每一個屬性被隱藏在對象以後,你所能看到的就是這個對象讓你看到的,至於它究竟是怎麼操做的,這個不須要你操心。
執行的效率就是另一回事。就像是C語言和麪向對象的C++之間的差異:C++很優雅,可是執行效率,不管是編譯後的二進制代碼仍是運行期的內存的佔用,都要比簡單的C語言多出一截來。
這個問題在腳本語言中顯得更加劇要,由於JavaScript根本就是一種解釋語言,解釋語言的執行效率要比編譯語言低不少。
1. 優雅的封裝
咱們先來看看變量封裝。這裏的變量不單單是屬性,也包括函數。
前面已經說過,JavaScript中並無類這個概念,是咱們利用變量做用域和閉包「巧妙的模擬」出來的,這是一種優雅的實現。仍是溫故一下之前的代碼:
function Person() {
var id;
var showId =
function() {
alert(
"My id is " + id);
}
this.getId =
function() {
return id;
}
this.setId =
function(newId) {
id = newId;
}
}
var p =
new Person();
p.setId(1000);
alert(p.id);
// undefined
// p.showId(); error: function not defined
var p2 =
new Person();
alert(p.getId == p2.getId);
// false
咱們很優雅的實現了私有變量——儘管是投機取巧的實現的。可是,這段代碼又有什麼問題呢?爲何兩個對象的函數是不一樣的呢?
想一下,咱們使用變量的做用域模擬出私有變量,用閉包模擬出公有變量,那麼,也就是說,實際上每一個建立的對象都會有一個相同的代碼的拷貝!不單單是那個id,就連那些showId、getId 等函數也會建立屢次。注意,考慮到JavaScript函數就是對象,就不會感到那麼奇怪了。可是毫無疑問,這是一種浪費:每一個變量所不一樣的只是本身的數據域,函數代碼都是相同的,由於咱們進行的是同一種操做。其餘語言通常不會遇到這種問題,由於那些語言的函數和對象的概念是不一樣的,像Java,每一個對象的方法其實指向了同一份代碼的拷貝,而不是每一個對象都會有本身的代碼拷貝。
2. 去看效率
那種封裝雖然優雅,可是很浪費。好在JavaScript是一種靈活的語言,因而,咱們立刻想到,把這些函數的指針指向另外的一個函數不就能夠了嗎?
function show() {
alert(
"I'm a person.");
}
function Person() {
this.show = show;
}
var p1 =
new Person();
var p2 =
new Person();
alert(p1.show == p2.show);
// true
這個辦法不錯,解決了咱們之前的那個問題:不一樣的對象共享了一份代碼。可是這種實現雖然有了效率,但是卻太不優雅了——若是我有不少類,那麼豈不是有不少全局函數?
好在JavaScript中還有一個機制:prototype。還記得這個prototype嗎?每一個對象都維護着一個prototype屬性,這些對象的prototype屬性是共享的。那麼,咱們就能夠把函數的定義放到prototype裏面,因而,不一樣的對象不就共享了一份代碼拷貝嗎?事實確實如此:
function Person() {
}
Person.prototype.show =
function() {
alert(
"I'm a person.");
}
var p1 =
new Person();
var p2 =
new Person();
alert(p1.show == p2.show);
// true
不過,這種分開定義看上去很彆扭,那麼好,爲何不把函數定義也寫到類定義裏面呢?
function Person() {
Person.prototype.show =
function() {
alert(
"I'm a person.");
}
}
var p1 =
new Person();
var p2 =
new Person();
alert(p1.show == p2.show);
// true
實際上這種寫法和上面一種沒有什麼不一樣:惟一的區別就是代碼位置不一樣。這只是一個「看上去很甜」的語法糖,並無實質性差異。
最初,微軟的.Net AJAX框架使用前面的機制模擬了私有變量和函數,這種寫法和C#很相像,十分的優雅。可是,處於效率的緣故,微軟後來把它改爲了這種原型的定義方式。雖然這種方式不那麼優雅,可是頗有效率。
在JavaScript中,這種封裝的優雅和執行的效率之間的矛盾一直存在。如今咱們最好的解決方案就是把數據定義在類裏面,函數定義在類的prototype屬性裏面。