我認(rèn)為正是由于JavaScript對(duì)象模型的難以理解和使用,才出現(xiàn)了一些像CoffeeScript,Dart和TypeScript這些通過編譯可以生成JS代碼的語言。
JavaScript的前輩們和那些頑固派相信JavaScript有更好的對(duì)象模型,并且為其將被大家所遺忘感到惋惜。即使JavaScript的專家Nicholas Zakas也歡迎在ECMAScript 6中加入的新的class語法——只不過是對(duì)原型風(fēng)格(prototypal style)的語法做了一些修飾。換句話說,傳統(tǒng)的OOP贏了。
一個(gè)大膽的設(shè)想
但是,讓我們以玩笑的方式做一個(gè)設(shè)想:我們假想穿越到過去,那時(shí)候傳統(tǒng)的面向?qū)ο蟮某绦蛟O(shè)計(jì)還沒有像現(xiàn)在這樣被大家廣泛的接受,相反的,基于原型的繼承模型得到了大家的廣泛接受。這樣的話會(huì)發(fā)生什么?我們最終又會(huì)得到些什么樣的設(shè)計(jì)模式呢?
我們?cè)僭O(shè)想:假設(shè)JavaScript沒有構(gòu)造函數(shù)或者沒有new關(guān)鍵字會(huì)怎樣?事情又會(huì)變成什么樣的呢?讓我們推到以前的重來。:)
首先,第一件事情,在JavaScript中,我們可以使用對(duì)象字面量的來創(chuàng)建一個(gè)新對(duì)象。如下所示:
代碼如下:
var felix = {
name: 'Felix',
greet: function(){
console.log('Hello, I am ' + this.name + '.');
}
};
接下來,假設(shè)我們想要將greet函數(shù)一般化,將其提取出來,放到一個(gè)一般的位置,這樣一來,我們就可以創(chuàng)建多個(gè)對(duì)象來共享同一個(gè)greet方法。該怎么實(shí)現(xiàn)呢?
我們有好幾種選擇,先以mixin開始吧。
1、混入(對(duì)象擴(kuò)張)Mixin(Augmentation)
在JavaScript語言中,混入屬性非常簡(jiǎn)單。你只需要將混入對(duì)象的屬性復(fù)制到要混入的對(duì)象中去即可。我們將使用一個(gè)“augment”函數(shù)來實(shí)現(xiàn)它,看代碼就明白了:
代碼如下:
var Dude = {
greet: function(){
console.log('Hello, I am ' + this.name + '.')
}
};
var felix = { name: 'Felix' };
augment(felix, Dude);//將Dude中的屬性復(fù)制一份到felix中,即混入(mixin)
在上面的代碼中,augment函數(shù)將Dude對(duì)象的屬性混入到了felix當(dāng)中。在很多的JS庫(kù)中,augment函數(shù)被叫做extend。我不喜歡用extend,因?yàn)橐恍┱Z言用extend表示繼承,以至于是我很困惑。我更喜歡用“augment”表示,因?yàn)閷?shí)際上這種做法并不是繼承,并且語法augment(felix, Dude)已經(jīng)很清楚的表明你是用Dude中的屬性對(duì)felix進(jìn)行了擴(kuò)充,而不是繼承。
也許你早就猜到了augment的代碼實(shí)現(xiàn)了,沒錯(cuò),非常簡(jiǎn)單。如下所示:
代碼如下:
function augment(obj, properties){
for (var key in properties){
obj[key] = properties[key];
}
}
2、對(duì)象克?。–loning)
mixin的一個(gè)替代的辦法就是先克隆Dude對(duì)象,然后再給克隆的對(duì)象設(shè)置name屬性。如下所示:
代碼如下:
var Dude = {
greet: function(){
console.log('Hello, I am ' + this.name + '.');
}
}
var felix = clone(Dude);//克隆Dude對(duì)象
felix.name = 'Felix';
這兩種方法之間的唯一不同就是添加屬性的順序。如果你想覆寫克隆的對(duì)象中的某些方法,你可以考慮使用這種手法。
代碼如下:
var felix = clone(Dude);
felix.name = 'Felix';
felix.greet = function(){
console.log('Yo dawg!');
};//覆寫greet方法
如果想要調(diào)用父類的方法也很簡(jiǎn)單——使用apply函數(shù)即可,如下所示
代碼如下:
felix.greet = function(){
Dude.greet.apply(this);
this.greetingCount++;
}
這比原型風(fēng)格的代碼要好很多,因?yàn)槟悴槐厝ナ褂脴?gòu)造函數(shù)的.prototype屬性——我們不會(huì)使用任何構(gòu)造函數(shù)。
以下是clone函數(shù)的實(shí)現(xiàn):
代碼如下:
function clone(obj){
var retval = {};//創(chuàng)建一個(gè)空對(duì)象
augment(retval, obj);//復(fù)制屬性
return retval;
}
3、繼承(Inheritance)
最后,就是繼承了。在我看來,繼承被高估了,但是繼承在“實(shí)例對(duì)象”之間共享屬性方面確實(shí)要比對(duì)象擴(kuò)張有一些優(yōu)勢(shì)。讓我們編寫一個(gè)inherit函數(shù),這個(gè)函數(shù)接收一個(gè)對(duì)象作為參數(shù),并且返回一個(gè)繼承自該對(duì)象的新對(duì)象。
代碼如下:
var felix = inherit(Dude);
felix.name = 'Felix';
使用繼承,你可以創(chuàng)建多個(gè)繼承自同一個(gè)對(duì)象的子對(duì)象,這些子對(duì)象可以實(shí)時(shí)的繼承父對(duì)象的屬性。如下面的代碼所示,
代碼如下:
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函數(shù)中使用了基于原型對(duì)象的繼承
代碼如下:
function inherit(proto){
if (Object.create){
// 使用ES5中的Object.create方法
return Object.create(proto);
}else if({}.__proto__){
//使用非標(biāo)準(zhǔn)屬性__proto__
var ret = {};
ret.__proto__ = proto;
return ret;
}else{
//如果兩種都不支持,使用構(gòu)造函數(shù)繼承
var f = function(){};
f.prototype = proto;
return new f();
}
}
上面的代碼看起來不怎么好,那是因?yàn)槲覀兪褂昧颂匦员O(jiān)測(cè)來判斷到底使用3種方式中的哪一種。
但是,怎么使用構(gòu)造方法呢(也就是,初始化方法)?你該怎么在實(shí)例對(duì)象之間共享初始化代碼呢?在一些情況下,如果你只需要為對(duì)象設(shè)置一些屬性,這時(shí)候,初始化函數(shù)不是必須的,就像我們上面的例子中那樣。但是如果你有更多的初始化代碼呢,你也許會(huì)制定一個(gè)約定,例如:使用一個(gè)叫initialize的初始化方法。我們假設(shè)在Dude中定義了一個(gè)叫initialize的方法,如下
代碼如下:
var Dude = {
initialize: function(){
this.greetingCount = 0;
},
greet: function(){
console.log('Hello, I am ' + this.name + '.');
this.greetingCount++;
}
}
然后,你可以這樣來初始化對(duì)象
代碼如下:
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();結(jié)語
我表示通過上面定義的三個(gè)函數(shù)——augment,clone和inherit,你可以對(duì)JavaScript中的對(duì)象做任何你想做的事,而不必使用構(gòu)造函數(shù)和new關(guān)鍵字。我認(rèn)為這三個(gè)函數(shù)所體現(xiàn)的語義更簡(jiǎn)單并且更接近于JavaScript底層的對(duì)象系統(tǒng)。(完)^_^
聲明:本網(wǎng)頁(yè)內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問題請(qǐng)及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com