感覺挺經(jīng)典的js文章,傳個附件。
“ JavaScript 中沒有‘類’,類已化于無形,與對象融為一體。正是由于放下了‘類’這個概念,JavaScript的對象才有了其他編程語言所沒有的活力。 ”這句看了很有感悟,也許這就是Javascript強(qiáng)大之所在吧。Javascript因此具有了動態(tài)增刪對象功能的能力。
function myfunc(){ alert("hello"); } myfunc();//這里調(diào)用myfunc,輸出 yeah 而不是 hello function myfunc(){ alert("yeah"); } myfunc();//這里調(diào)用myfunc,當(dāng)然輸出yeah
JavaScript 執(zhí)行引擎并非一行一行地分析和執(zhí)行程序,而是 一段一段地分析執(zhí)行 的。而且,在同一段程序的分析執(zhí)行中,定義式的函數(shù)語句會被提取出來優(yōu)先執(zhí)行。函數(shù)定義執(zhí)行完之后,才會按順序執(zhí)行其他語句代碼。也就是說,在第一次調(diào)用myfunc之前,第一個函數(shù)語句定義的代碼邏輯,已被第二個函數(shù)定義語句覆蓋了。所以,兩次都調(diào)用都是執(zhí)行最后一個函數(shù)邏輯了。
如果把這個 JavaScript 代碼分成兩段,例如將它們寫在一個 html 中,并用<script/>標(biāo)簽將其分成這樣的兩塊:
<script> function myfunc () { alert("hello"); }; myfunc(); //這里調(diào)用myfunc,輸出 hello </script> <script> function myfunc () { alert("yeah"); }; myfunc(); //這里調(diào)用myfunc,輸出 yeah </script>
這時,輸出才是各自按順序來的, 也證明了JavaScript的確是一段段地執(zhí)行的 。
一段代碼中的定義式函數(shù)語句會優(yōu)先執(zhí)行,這似乎有點(diǎn)象靜態(tài)語言的編譯概念。所以,這一特征也被有些人稱為:JavaScript的“預(yù)編譯”。
大多數(shù)情況下,我們也沒有必要去糾纏這些細(xì)節(jié)問題。只要你記住一點(diǎn): JavaScript 里的代碼也是一種數(shù)據(jù),同樣可以被任意賦值和修改的,而它的值就是代碼的邏輯 。只是,與一般數(shù)據(jù)不同的是, 函數(shù)是可以被調(diào)用執(zhí)行的 。
JavaScript 函數(shù)的神奇之處還體現(xiàn)在另外兩個方面:一是 函數(shù) function類型本身也具有對象化的能力 ,二是 函數(shù)function與對象 object超然的結(jié)合能力 。
在JavaScript函數(shù)中,你只能把this看成當(dāng)前要服務(wù)的“這個”對象。this是一個特殊的內(nèi)置參數(shù),根據(jù)this參數(shù),您可以訪問到“這個”對象的屬性和方法,但卻不能給this參數(shù)賦值。在一般對象語言中,方法體代碼中的this可以省略的,成員默認(rèn)都首先是“自己”的。但JavaScript卻不同,由于不存在“自我”,當(dāng)訪問“這個”對象時,this不可省略!
我們已經(jīng)知道,用 var anObject = new aFunction() 形式創(chuàng)建對象的過程實(shí)際上可以分為三步:
第一步是建立一個新對象 ; 第二步將該對象內(nèi)置的原型對象設(shè)置為構(gòu)造函數(shù)prototype引用的那個原型對
象 ; 第三步就是將該對象作為this參數(shù)調(diào)用構(gòu)造函數(shù),完成成員設(shè)置等初始化工作 。對象建立之后,對象
上的任何訪問和操作都只與對象自身及其原型鏈上的那串對象有關(guān),與構(gòu)造函數(shù)再扯不上關(guān)系了。換句話
說, 構(gòu)造函數(shù)只是在創(chuàng)建對象時起到介紹原型對象和初始化對象兩個作用 。
那么,我們能否自己定義一個對象來當(dāng)作原型,并在這個原型上描述類,然后將這個原型設(shè)置給新創(chuàng)建
的對象,將其當(dāng)作對象的類呢?我們又能否將這個原型中的一個方法當(dāng)作構(gòu)造函數(shù),去初始化新建的對象
呢?例如,我們定義這樣一個原型對象:
var Person = //定義一個對象來作為原型類 { Create: function(name, age) //這個當(dāng)構(gòu)造函數(shù) { 28 this.name = name; this.age = age; }, SayHello: function() //定義方法 { alert("Hello, I'm " + this.name); }, HowOld: function() //定義方法 { alert(this.name + " is " + this.age + " years old."); } };
這個JSON形式的寫法多么象一個C#的類??!既有構(gòu)造函數(shù),又有各種方法。如果可以用某種形式來
創(chuàng)建對象,并將對象的內(nèi)置的原型設(shè)置為上面這個“類”對象,不就相當(dāng)于創(chuàng)建該類的對象了嗎?
但遺憾的是,我們幾乎不能訪問到對象內(nèi)置的原型屬性!盡管有些瀏覽器可以訪問到對象的內(nèi)置原型,
但這樣做的話就只能限定了用戶必須使用那種瀏覽器。這也幾乎不可行。
那么,我們可不可以通過一個函數(shù)對象來做媒介,利用該函數(shù)對象的prototype 屬性來中轉(zhuǎn)這個原型,
并用new操作符傳遞給新建的對象呢?
其實(shí),象這樣的代碼就可以實(shí)現(xiàn)這一目標(biāo):
function anyfunc(){}; //定義一個函數(shù)軀殼 anyfunc.prototype = Person; //將原型對象放到中轉(zhuǎn)站 prototype var BillGates = new anyfunc(); //新建對象的內(nèi)置原型將是我們期望的原型對象
不過,這個 anyfunc 函數(shù)只是一個軀殼,在使用過這個軀殼之后它就成了多余的東西了,而且這和直
接使用構(gòu)造函數(shù)來創(chuàng)建對象也沒啥不同,有點(diǎn)不爽。
可是,如果我們將這些代碼寫成一個通用函數(shù),而那個函數(shù)軀殼也就成了函數(shù)內(nèi)的函數(shù),這個內(nèi)部函數(shù)
不就可以在外層函數(shù)退出作用域后自動消亡嗎?而且,我們可以將原型對象作為通用函數(shù)的參數(shù),讓通用
函數(shù)返回創(chuàng)建的對象。我們需要的就是下面這個形式:
function New(aClass, aParams) //通用創(chuàng)建函數(shù) { function new_() //定義臨時的中轉(zhuǎn)函數(shù)殼 { aClass.Create.apply(this, aParams); //調(diào)用原型中定義的的構(gòu)造函數(shù),中轉(zhuǎn)構(gòu)造邏輯及構(gòu)造參數(shù) }; new_.prototype = aClass; //準(zhǔn)備中轉(zhuǎn)原型對象 return new new_(); //返回建立最終建立的對象 }; var Person = //定義的類 { Create: function(name, age) { this.name = name; this.age = age; }, SayHello: function() { alert("Hello, I'm " + this.name); }, HowOld: function() { alert(this.name + " is " + this.age + " years old."); } }; var BillGates = New(Person, ["Bill Gates", 53]); //調(diào)用通用函數(shù)創(chuàng)建對象,并以數(shù)組形式傳遞構(gòu)造參數(shù) BillGates.SayHello(); BillGates.HowOld(); alert(BillGates.constructor == Object); //輸出:true
這里的通用函數(shù) New()就是一個“語法甘露”!這個語法甘露不但中轉(zhuǎn)了原型對象,還中轉(zhuǎn)了構(gòu)造函數(shù)
邏輯及構(gòu)造參數(shù)。
有趣的是,每次創(chuàng)建完對象退出New函數(shù)作用域時,臨時的new_函數(shù)對象會被自動釋放。由于new
_的 prototype 屬性被設(shè)置為新的原型對象,其原來的原型對象和 new_之間就已解開了引用鏈,臨時函
數(shù)及其原來的原型對象都會被正確回收了。上面代碼的最后一句證明,新創(chuàng)建的對象的constructor屬性
返回的是 Object 函數(shù)。其實(shí)新建的對象自己及其原型里沒有 constructor 屬性,那返回的只是最頂層原
型對象的構(gòu)造函數(shù),即Object。
當(dāng)然,這個代碼僅僅展示了“語法甘露”的概念。我們還需要多一些的語法甘露,才能實(shí)現(xiàn)用簡潔而優(yōu)雅
的代碼書寫類層次及其繼承關(guān)系。好了,我們再來看一個更豐富的示例吧:
//語法甘露: var object = //定義小寫的 object 基本類,用于實(shí)現(xiàn)最基礎(chǔ)的方法等 { isA: function(aType) //一個判斷類與類之間以及對象與類之間關(guān)系的基礎(chǔ)方法 { var self = this; while(self) { if (self == aType) return true; self = self.Type; }; return false; } }; function Class(aBaseClass, aClassDefine) //創(chuàng)建類的函數(shù),用于聲明類及繼承關(guān)系 { function class_() //創(chuàng)建類的臨時函數(shù)殼 { this.Type = aBaseClass; //我們給每一個類約定一個 Type屬性,引用其繼承的類 for(var member in aClassDefine) this[member] = aClassDefine[member]; //復(fù)制類的全部定義到當(dāng)前創(chuàng)建的類 }; class_.prototype = aBaseClass; return new class_(); }; function New(aClass, aParams) //創(chuàng)建對象的函數(shù),用于任意類的對象創(chuàng)建 { function new_() //創(chuàng)建對象的臨時函數(shù)殼 { this.Type = aClass; //我們也給每一個對象約定一個 Type 屬性,據(jù)此可以訪問到對象所屬的類 if (aClass.Create) aClass.Create.apply(this, aParams); //我們約定所有類的構(gòu)造函數(shù)都叫Create,這和DELPHI比較相似 }; new_.prototype = aClass; return new new_(); }; //語法甘露的應(yīng)用效果: var Person = Class(object, //派生至 object 基本類 { Create: function(name, age) { this.name = name; this.age = age; }, SayHello: function() { alert("Hello, I'm " + this.name + ", " + this.age + " years old."); } }); var Employee = Class(Person, //派生至 Person 類,是不是和一般對象語言很相似? { Create: function(name, age, salary) { Person.Create.call(this, name, age); //調(diào)用基類的構(gòu)造函數(shù) this.salary = salary; }, ShowMeTheMoney: function() { alert(this.name + " $" + this.salary); } }); var BillGates = New(Person, ["Bill Gates", 53]); var SteveJobs = New(Employee, ["Steve Jobs", 53, 1234]); BillGates.SayHello(); SteveJobs.SayHello(); SteveJobs.ShowMeTheMoney(); var LittleBill = New(BillGates.Type, ["Little Bill", 6]); //根據(jù) BillGate 的類型創(chuàng)建LittleBill LittleBill.SayHello(); alert(BillGates.isA(Person)); //true alert(BillGates.isA(Employee)); //false alert(SteveJobs.isA(Person)); //true alert(Person.isA(Employee)); //false alert(Employee.isA(Person)); //true
“語法甘露”不用太多,只要那么一點(diǎn)點(diǎn),就能改觀整個代碼的易讀性和流暢性,從而讓代碼顯得更優(yōu)雅。
有了這些語法甘露,JavaScript就很像一般對象語言了,寫起代碼了感覺也就爽多了!
令人高興的是,受這些甘露滋養(yǎng)的 JavaScript 程序效率會更高。因為其原型對象里既沒有了毫無用處
的那些對象級的成員,而且還不存在constructor屬性體,少了與構(gòu)造函數(shù)間的牽連,但依舊保持了方法
的共享性。這讓JavaScript 在追溯原型鏈和搜索屬性及方法時,少費(fèi)許多工夫啊。
我們就把這種形式稱為“甘露模型”吧!其實(shí),這種“甘露模型”的原型用法才是符合prototype概念的本
意,才是的JavaScript原型的真諦!
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯(lián)系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長非常感激您!手機(jī)微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
