|
封裝:Javascript中創(chuàng)建對(duì)象的模式中,個(gè)人認(rèn)為通過(guò)閉包才算的上是真正意義上的封裝,所以首先我們先來(lái)簡(jiǎn)單介紹一下閉包,看下面這個(gè)例子:
復(fù)制代碼 代碼如下:
<script type="text/Javascript">// <![CDATA[
function myInfo(){
var name ="老魚(yú)",age =27;
var myInfo = "my name is" + name + "i am" + age +"years old";
function showInfo(){
alert(myInfo);
}
return showInfo;
}
var oldFish = myInfo();
oldFish();
// ]]></script>
是不是很眼熟呢?沒(méi)錯(cuò)了,這其實(shí)就是一個(gè)簡(jiǎn)單的閉包應(yīng)用了。簡(jiǎn)單解釋一下:上面的函數(shù)myInfo中定義的變量,在它的內(nèi)嵌函數(shù)showInfo中是可訪問(wèn)的(這個(gè)很好理解),但是當(dāng)我們把這個(gè)內(nèi)嵌函數(shù)的返回引用賦值給一個(gè)變量oldFish,這個(gè)時(shí)候函數(shù)showInfo是在myInfo函數(shù)體外被調(diào)用,但是同樣可以訪問(wèn)到定義在函數(shù)體內(nèi)的變量。oh yeah!
總結(jié)一下閉包的原理吧:函數(shù)是運(yùn)行在定義他們的作用域中而不是調(diào)用他們的作用域中。其實(shí)返回一個(gè)內(nèi)嵌函數(shù)也是創(chuàng)建閉包最常用的一種方法!
如果覺(jué)得上面的解釋太抽象的話(huà),那么我們一起重塑上面的函數(shù),看看這樣是否層次鮮明一些:
復(fù)制代碼 代碼如下:<script type="text/Javascript">// <![CDATA[
var ioldFish = function(name,age){
var name = name,age = age;
var myInfo = "my name is" + name + "i am" + age +"years old";
return{
showInfo:function(){
alert(myInfo);
}
}
}
ioldFish("老魚(yú)",27).showInfo();
// ]]></script>
上例中的編碼風(fēng)格是ext yui中比較常見(jiàn)的,公私分明,一目了然。通過(guò)閉包,我們可以很方便的把一些不希望被外部直接訪問(wèn)到的東西隱藏起來(lái),你要訪問(wèn)函數(shù)內(nèi)定義的變量,只能通過(guò)特定的方法才可以訪問(wèn)的到,直接從外部訪問(wèn)是訪問(wèn)不到的,寫(xiě)的挺累,饒了一圈終于轉(zhuǎn)回來(lái)了,封裝嘛,不就是把不希望被別人看到的東西隱藏起來(lái)嘛!哈哈……
上例如果轉(zhuǎn)換成JQ的風(fēng)格的話(huà),應(yīng)該如下例所寫(xiě), 這樣的封裝模式屬于門(mén)戶(hù)大開(kāi)型模式,里面定義的變量是可以被外部訪問(wèn)到的(下面的例子如果你先實(shí)例化一個(gè)對(duì)象,然后在函數(shù)外部訪問(wèn)對(duì)象的name或者age屬性都是可以讀取到的)當(dāng)然這種模式下我們可以設(shè)置一些”潛規(guī)則”,讓團(tuán)隊(duì)開(kāi)發(fā)成員明白哪些變量是私用的,通常我們?nèi)藶榈脑谒接凶兞亢头椒ㄇ凹酉聞澗€”_”,標(biāo)識(shí)警戒訊號(hào)!從而實(shí)現(xiàn)”封裝”!
復(fù)制代碼 代碼如下:<script type="text/Javascript">// <![CDATA[
var ioldFish = function(name,age){
return ioldFish.func.init(name,age);
};
ioldFish.func = ioldFish.prototype ={
init:function(name,age){
this.name = name;
this.age = age;
return this;
},
showInfo:function(){
var info = "my name is" + this.name +"i am " +this.age+"years old";
alert(info);
}
};
ioldFish.func.init.prototype = ioldFish.func;
ioldFish(" 老 魚(yú)",27).showInfo();
//var oldFish = new ioldFish("老魚(yú)",27);
//alert(oldFish.name);
// ]]></script>
可能有人會(huì)問(wèn),哪種模式好呢?這個(gè)怎么說(shuō)呢??jī)煞N方式都有優(yōu)缺點(diǎn),結(jié)合著用唄!總之一個(gè)原則,一定一定不能直接被外部對(duì)象訪問(wèn)的東西,就用閉包封裝吧?!币欢ㄒ欢ā彼膫€(gè)字很深?yuàn)W,不斷實(shí)踐中才能體會(huì)真諦!
繼承:提到這個(gè)的時(shí)候,要順便再補(bǔ)充一句:閉包封裝中的一個(gè)缺點(diǎn),不利于子類(lèi)的派生,所以閉包有風(fēng)險(xiǎn),封裝需謹(jǐn)慎!直觀起見(jiàn),下面例子中創(chuàng)建對(duì)象的方式,采用”門(mén)戶(hù)大開(kāi)型”模式。
在Javascript中繼承一般分為三種方式:”類(lèi)式繼承”,”原型繼承”,”摻元類(lèi)”。下面簡(jiǎn)單的介紹一下三類(lèi)繼承方式的原理。
A.類(lèi)式繼承:這個(gè)是現(xiàn)在主流框架中常用的繼承方式,看下例:
復(fù)制代碼 代碼如下:<script type="text/Javascript">// <![CDATA[
var Name = function(name){
this.name = name;
};
Name.prototype.getName = function(){
alert(this.name);
};
var Fish = function(name,age){
Name.call(this,name);
this.age = age;
};
Fish.prototype = new Name();
Fish.prototype.constructor = Fish;
Fish.prototype.showInfo = function(){
alert(this.age);
}
var ioldFish = new Fish("老魚(yú)",27);
ioldFish.getName();
// ]]></script>
上述子類(lèi)Fish中并沒(méi)定義getName方法,但是子類(lèi)Fish的實(shí)例對(duì)象ioldFish依然調(diào)用到了該方法,這是因?yàn)樽宇?lèi)Fish繼承了超類(lèi)Name中定義的getName方法。解釋一下,這里子類(lèi)Fish的prototype指到了超類(lèi)的一個(gè)實(shí)例,在子類(lèi)Fish中雖然沒(méi)有申明getName方法,但是根據(jù)原型鏈原理,會(huì)向prototype指向的上一級(jí)對(duì)象中去查找是否有該方法,如果沒(méi)找到該方法,會(huì)一直搜索到最初的原型對(duì)象。這其實(shí)也就是繼承的原理了。這里特別說(shuō)明一下,F(xiàn)ish.prototype.constructor = Fish;這句,由于默認(rèn)子類(lèi)的prototype應(yīng)該是指向本身的,但是之前把prototype指向到了超類(lèi)的實(shí)例對(duì)象,所以在這里要把它設(shè)置回來(lái)。當(dāng)然這里可以把相關(guān)代碼通過(guò)一個(gè)函數(shù)來(lái)組織起來(lái),起到偽裝extend的作用,這里不再闡述,可以關(guān)注本人下篇博文……
B.原型繼承,從內(nèi)存性能上看優(yōu)于類(lèi)式繼承。
復(fù)制代碼 代碼如下:<script type="text/Javascript">// <![CDATA[
function clone(object){
var F = function(){};
F.prototype = object;
return new F();
};
var Name = {
name:"who's name",
showInfo:function(){
alert(this.name);
}
};
var Fish = clone(Name);
//Fish.name = "老魚(yú)";
Fish.showInfo();
// ]]></script>
很明顯,原型繼承核心就是這個(gè)clone函數(shù),同樣是原型鏈的原理,不同的是它直接克隆超類(lèi),這樣的話(huà)子類(lèi)就繼承了超類(lèi)的所有屬性和方法.特別說(shuō)一下,這類(lèi)繼承并不需要?jiǎng)?chuàng)建構(gòu)造函數(shù),只需要?jiǎng)?chuàng)建一個(gè)對(duì)象字變量,定義相應(yīng)的屬性和方法,然后在子類(lèi)中只需要通過(guò)圓點(diǎn)”.”符號(hào)來(lái)引用屬性和方法就可以了.
C.摻元類(lèi):把一些常用通用性比較大的方法統(tǒng)一封裝在一個(gè)函數(shù)中,然后通過(guò)下面這個(gè)函數(shù)分派給要用到這些方法的類(lèi).還可以針對(duì)不同的類(lèi),選擇性的傳遞需要的方法。
復(fù)制代碼 代碼如下:<script type="text/Javascript">// <![CDATA[
function agument(receveClass,giveClass){
if(arguments[2]){
var len = arguments.length;
for(i=2;i<len;i++){
receveClass.prototype[arguments[i]] = giveClass.prototype[arguments[i]];
}
}
else{
for(method in giveClass.prototype){
if(!receveClass.prototype[method]){
receveClass.prototype[method] = giveClass.prototype[method];
}
}
}
};
var Name = function(){};
Name.prototype ={
sayLike:function(){
alert("i like oldfish");
},
sayLove:function(){
alert("i love oldfish");
}
}
var Fish = function(){};
var ioldFish = new Fish();
agument(Fish,Name,"sayLove");
ioldFish.sayLove();
ioldFish.sayLike();
// ]]></script>
多態(tài):個(gè)人覺(jué)得這個(gè)比較抽象,很難言傳,所以下面就從重載和覆蓋兩個(gè)方面來(lái)簡(jiǎn)單闡述一下。
重載:上面這個(gè)例子中agument函數(shù)初始帶了兩個(gè)參數(shù),但是在后面的調(diào)用中,agument(Fish,Name,”sayLove”)同樣可以帶入任意多個(gè)參數(shù),Javascript的重載,是在函數(shù)中由用戶(hù)自己通過(guò)操作arguments這個(gè)屬性來(lái)實(shí)現(xiàn)的。
覆蓋:這個(gè)很簡(jiǎn)單,就是子類(lèi)中定義的方法如果與從超類(lèi)中繼承過(guò)來(lái)的的方法同名,就覆蓋這個(gè)方法(這里并不是覆蓋超類(lèi)中的方法,注意一下),這里就不累贅了!
最后重點(diǎn)著墨說(shuō)一下this和執(zhí)行上下文,在前面舉的封裝例子中,this都是表示this所在的類(lèi)的實(shí)例化對(duì)象本身,但是并不是千篇一律的,打個(gè)比方,通過(guò)HTML屬性定義的事件處理代碼,見(jiàn)如下代碼:
復(fù)制代碼 代碼如下:<script type="text/Javascript">// <![CDATA[
var Name = function(name) {
this.name = name;
this.getName = function () {
alert(this.name);
}
};
var ioldFish = new Name("老魚(yú)"),
btn = document.getElementById('btn');
btn.onclick = ioldFish.getName;
//btn.onclick = function(){ioldFish.getName.call(ioldFish)};
// ]]></script>
上例中點(diǎn)了按鈕以后彈出框里并沒(méi)有顯示出實(shí)例對(duì)象的屬性,這是因?yàn)閠his的執(zhí)行上下文已經(jīng)改變了,他現(xiàn)在所在的上下文應(yīng)該是input這個(gè)HTML標(biāo)簽,但是該標(biāo)簽又不存在getName這個(gè)屬性,所以自然無(wú)法輸出這個(gè)屬性的屬性值了!從這個(gè)例子我們不難看出:執(zhí)行上下文是在執(zhí)行時(shí)才確定的,它隨時(shí)可以變。
當(dāng)然你可以去掉上面我注釋掉的那段代碼,通過(guò)call改變this的執(zhí)行上下文,從而獲取getName方法。apply方法同樣可以實(shí)現(xiàn)改變執(zhí)行上下文的功能,不過(guò)在prototype框架中發(fā)現(xiàn)了一個(gè)更為優(yōu)美的實(shí)現(xiàn)方法bind??匆幌逻@個(gè)方法的實(shí)現(xiàn)吧,不得不感嘆先人的偉大……
復(fù)制代碼 代碼如下:
Function.prototype.bind = function(obj) {
var method = this,
temp = function() {
return method.apply(obj, arguments);
};
}
相信如果能看明白的話(huà),您已經(jīng)可以靠這些知識(shí)點(diǎn),去寫(xiě)一個(gè)簡(jiǎn)單的腳本框架了,多多實(shí)踐,相信不久的將來(lái)就能高手進(jìn)級(jí)了!如果沒(méi)看明白,也不用著急,面向?qū)ο蟊緛?lái)就有些抽象,多練習(xí)練習(xí),應(yīng)該OK的了,加油……
這篇先寫(xiě)到這吧,下篇文章可以和大家一起探討一下,Javascript的設(shè)計(jì)模式,敬請(qǐng)期待。
JavaScript技術(shù):老魚(yú) 淺談javascript面向?qū)ο缶幊?/a>,轉(zhuǎn)載需保留來(lái)源!
鄭重聲明:本文版權(quán)歸原作者所有,轉(zhuǎn)載文章僅為傳播更多信息之目的,如作者信息標(biāo)記有誤,請(qǐng)第一時(shí)間聯(lián)系我們修改或刪除,多謝。