不用建構函式(Constructor)new關鍵字也能實現JavaScript的物件導向

來源:互聯網
上載者:User

JavaScript中的物件模型(object model)並不廣為人知。我曾寫過一篇關於他們的部落格。之所以不被人所熟知,原因之一就是JavaScript是這些被人廣泛使用的語言中唯一一個通過原型(prototype)來實現繼承的。但是,我認為另一個原因就是這種物件模型非常複雜,難於解釋。它為什麼這麼複雜並且又令人困惑呢?那是因為JavaScript試圖去隱藏它傳統的物件導向的特性——最終導致了它的雙重人格(譯者註:作者意思是JavaScript既有面向過程的特徵,又有物件導向的特徵)。

我認為正是由於JavaScript物件模型的難以理解和使用,才出現了一些像CoffeeScript,Dart和TypeScript這些通過編譯可以產生JS代碼的語言。

JavaScript的前輩們和那些頑固派相信JavaScript有更好的物件模型,並且為其將被大家所遺忘感到惋惜。即使JavaScript的專家Nicholas Zakas也歡迎在ECMAScript 6中加入的新的class文法——只不過是對原型風格(prototypal style)的文法做了一些修飾。換句話說,傳統的OOP贏了。

一個大膽的設想
但是,讓我們以玩笑的方式做一個設想:我們假想穿越到過去,那時候傳統的物件導向的程式設計還沒有像現在這樣被大家廣泛的接受,相反的,基於原型的繼承模型得到了大家的廣泛接受。這樣的話會發生什嗎?我們最終又會得到些什麼樣的設計模式呢?

我們再設想:假設JavaScript沒有建構函式或者沒有new關鍵字會怎樣?事情又會變成什麼樣的呢?讓我們推到以前的重來。:)

首先,第一件事情,在JavaScript中,我們可以使用對象字面量的來建立一個新對象。如下所示: 複製代碼 代碼如下:var felix = {
name: 'Felix',
greet: function(){
console.log('Hello, I am ' + this.name + '.');
}
};

接下來,假設我們想要將greet函數一般化,將其提取出來,放到一個一般的位置,這樣一來,我們就可以建立多個對象來共用同一個greet方法。該怎麼實現呢?
我們有好幾種選擇,先以mixin開始吧。

1、混入(對象擴張)Mixin(Augmentation)
在JavaScript語言中,混入屬性非常簡單。你只需要將混入對象的屬性複製到要混入的對象中去即可。我們將使用一個“augment”函數來實現它,看代碼就明白了: 複製代碼 代碼如下:var Dude = {
greet: function(){
console.log('Hello, I am ' + this.name + '.')
}
};
var felix = { name: 'Felix' };
augment(felix, Dude);//將Dude中的屬性複製一份到felix中,即混入(mixin)

在上面的代碼中,augment函數將Dude對象的屬性混入到了felix當中。在很多的JS庫中,augment函數被叫做extend。我不喜歡用extend,因為一些語言用extend表示繼承,以至於是我很困惑。我更喜歡用“augment”表示,因為實際上這種做法並不是繼承,並且文法augment(felix, Dude)已經很清楚的表明你是用Dude中的屬性對felix進行了擴充,而不是繼承。

也許你早就猜到了augment的代碼實現了,沒錯,非常簡單。如下所示: 複製代碼 代碼如下:function augment(obj, properties){
for (var key in properties){
obj[key] = properties[key];
}
}

2、對象複製(Cloning)
mixin的一個替代的辦法就是先複製Dude對象,然後再給複製的對象設定name屬性。如下所示: 複製代碼 代碼如下:var Dude = {
greet: function(){
console.log('Hello, I am ' + this.name + '.');
}
}
var felix = clone(Dude);//複製Dude對象
felix.name = 'Felix';

這兩種方法之間的唯一不同就是添加屬性的順序。如果你想覆寫複製的對象中的某些方法,你可以考慮使用這種手法。 複製代碼 代碼如下:var felix = clone(Dude);
felix.name = 'Felix';
felix.greet = function(){
console.log('Yo dawg!');
};//覆寫greet方法

如果想要調用父類的方法也很簡單——使用apply函數即可,如下所示 複製代碼 代碼如下:felix.greet = function(){
Dude.greet.apply(this);
this.greetingCount++;
}

這比原型風格的代碼要好很多,因為你不必去使用建構函式的.prototype屬性——我們不會使用任何建構函式。
以下是clone函數的實現: 複製代碼 代碼如下:function clone(obj){
var retval = {};//建立一個Null 物件
augment(retval, obj);//複製屬性
return retval;
}

3、繼承(Inheritance)
最後,就是繼承了。在我看來,繼承被高估了,但是繼承在“執行個體對象”之間共用屬性方面確實要比對象擴張有一些優勢。讓我們編寫一個inherit函數,這個函數接收一個對象作為參數,並且返回一個繼承自該對象的新對象。 複製代碼 代碼如下:var felix = inherit(Dude);
felix.name = 'Felix';

使用繼承,你可以建立多個繼承自同一個對象的子物件,這些子物件可以即時的繼承父物件的屬性。如下面的代碼所示, 複製代碼 代碼如下:var garfield = inherit(Dude);//garfield繼承自Dude
Dude.walk = function(){//給Dude添加新的方法walk
console.log('Step, step');
};
garfield.walk(); // prints "Step, step"
felix.walk(); // also prints "Step, step"

在inherit函數中使用了基於原型對象的繼承 複製代碼 代碼如下:function inherit(proto){
if (Object.create){
// 使用ES5中的Object.create方法
return Object.create(proto);
}else if({}.__proto__){
//使用非標準屬性__proto__
var ret = {};
ret.__proto__ = proto;
return ret;
}else{
//如果兩種都不支援,使用建構函式繼承
var f = function(){};
f.prototype = proto;
return new f();
}
}

上面的代碼看起來不怎麼好,那是因為我們使用了特性監測來判斷到底使用3種方式中的哪一種。

但是,怎麼使用構造方法呢(也就是,初始化方法)?你該怎麼在執行個體對象之間共用初始化代碼呢?在一些情況下,如果你只需要為對象設定一些屬性,這時候,初始化函數不是必須的,就像我們上面的例子中那樣。但是如果你有更多的初始化代碼呢,你也許會制定一個約定,例如:使用一個叫initialize的初始化方法。我們假設在Dude中定義了一個叫initialize的方法,如下 複製代碼 代碼如下:var Dude = {
initialize: function(){
this.greetingCount = 0;
},
greet: function(){
console.log('Hello, I am ' + this.name + '.');
this.greetingCount++;
}
}

然後,你可以這樣來初始化對象 複製代碼 代碼如下:var felix = clone(Dude);
felix.name = 'Felix';
felix.initialize();或者也可以
var felix = { name: 'Felix' };
felix.name = 'Felix';
augment(felix, Dude);
felix.initialize();還可以
var felix = inherit(Dude);
felix.name = 'Felix';
felix.initialize();結語

我表示通過上面定義的三個函數——augment,clone和inherit,你可以對JavaScript中的對象做任何你想做的事,而不必使用建構函式和new關鍵字。我認為這三個函數所體現的語義更簡單並且更接近於JavaScript底層的對象系統。(完)^_^

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.