国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費(fèi)電子書(shū)等14項(xiàng)超值服

開(kāi)通VIP
全面理解面向?qū)ο蟮?JavaScript

在 IBM Bluemix 云平臺(tái)上開(kāi)發(fā)并部署您的下一個(gè)應(yīng)用。

開(kāi)始您的試用

前言

當(dāng)今 JavaScript 大行其道,各種應(yīng)用對(duì)其依賴(lài)日深。web 程序員已逐漸習(xí)慣使用各種優(yōu)秀的 JavaScript 框架快速開(kāi)發(fā) Web 應(yīng)用,從而忽略了對(duì)原生 JavaScript 的學(xué)習(xí)和深入理解。所以,經(jīng)常出現(xiàn)的情況是,很多做了多年 JS 開(kāi)發(fā)的程序員對(duì)閉包、函數(shù)式編程、原型總是說(shuō)不清道不明,即使使用了框架,其代碼組織也非常糟糕。這都是對(duì)原生 JavaScript 語(yǔ)言特性理解不夠的表現(xiàn)。要掌握好 JavaScript,首先一點(diǎn)是必須摒棄一些其他高級(jí)語(yǔ)言如 Java、C# 等類(lèi)式面向?qū)ο笏季S的干擾,全面地從函數(shù)式語(yǔ)言的角度理解 JavaScript 原型式面向?qū)ο蟮奶攸c(diǎn)。把握好這一點(diǎn)之后,才有可能進(jìn)一步使用好這門(mén)語(yǔ)言。本文適合群體:使用過(guò) JS 框架但對(duì) JS 語(yǔ)言本質(zhì)缺乏理解的程序員,具有 Java、C++ 等語(yǔ)言開(kāi)發(fā)經(jīng)驗(yàn),準(zhǔn)備學(xué)習(xí)并使用 JavaScript 的程序員,以及一直對(duì) JavaScript 是否面向?qū)ο竽@鈨煽桑M勒嫦嗟?JS 愛(ài)好者。

回頁(yè)首

重新認(rèn)識(shí)面向?qū)ο?/h2>

為了說(shuō)明 JavaScript 是一門(mén)徹底的面向?qū)ο蟮恼Z(yǔ)言,首先有必要從面向?qū)ο蟮母拍钪?, 探討一下面向?qū)ο笾械膸讉€(gè)概念:

  • 一切事物皆對(duì)象
  • 對(duì)象具有封裝和繼承特性
  • 對(duì)象與對(duì)象之間使用消息通信,各自存在信息隱藏

以這三點(diǎn)做為依據(jù),C++ 是半面向?qū)ο蟀朊嫦蜻^(guò)程語(yǔ)言,因?yàn)椋m然他實(shí)現(xiàn)了類(lèi)的封裝、繼承和多態(tài),但存在非對(duì)象性質(zhì)的全局函數(shù)和變量。Java、C# 是完全的面向?qū)ο笳Z(yǔ)言,它們通過(guò)類(lèi)的形式組織函數(shù)和變量,使之不能脫離對(duì)象存在。但這里函數(shù)本身是一個(gè)過(guò)程,只是依附在某個(gè)類(lèi)上。

然而,面向?qū)ο髢H僅是一個(gè)概念或者編程思想而已,它不應(yīng)該依賴(lài)于某個(gè)語(yǔ)言存在。比如 Java 采用面向?qū)ο笏枷霕?gòu)造其語(yǔ)言,它實(shí)現(xiàn)了類(lèi)、繼承、派生、多態(tài)、接口等機(jī)制。但是這些機(jī)制,只是實(shí)現(xiàn)面向?qū)ο缶幊痰囊环N手段,而非必須。換言之,一門(mén)語(yǔ)言可以根據(jù)其自身特性選擇合適的方式來(lái)實(shí)現(xiàn)面向?qū)ο蟆K?,由于大多?shù)程序員首先學(xué)習(xí)或者使用的是類(lèi)似 Java、C++ 等高級(jí)編譯型語(yǔ)言(Java 雖然是半編譯半解釋?zhuān)话阕鰹榫幾g型來(lái)講解),因而先入為主地接受了“類(lèi)”這個(gè)面向?qū)ο髮?shí)現(xiàn)方式,從而在學(xué)習(xí)腳本語(yǔ)言的時(shí)候,習(xí)慣性地用類(lèi)式面向?qū)ο笳Z(yǔ)言中的概念來(lái)判斷該語(yǔ)言是否是面向?qū)ο笳Z(yǔ)言,或者是否具備面向?qū)ο筇匦?。這也是阻礙程序員深入學(xué)習(xí)并掌握 JavaScript 的重要原因之一。

實(shí)際上,JavaScript 語(yǔ)言是通過(guò)一種叫做 原型(prototype的方式來(lái)實(shí)現(xiàn)面向?qū)ο缶幊痰?。下面就?lái)討論 基于類(lèi)的(class-based)面向?qū)ο?/strong>和 基于原型的 (prototype-based) 面向?qū)ο?/strong>這兩種方式在構(gòu)造客觀世界的方式上的差別。

回頁(yè)首

基于類(lèi)的面向?qū)ο蠛突谠偷拿嫦驅(qū)ο蠓绞奖容^

在基于類(lèi)的面向?qū)ο蠓绞街校?strong>對(duì)象(object依靠 類(lèi)(class來(lái)產(chǎn)生。而在基于原型的面向?qū)ο蠓绞街校?strong>對(duì)象(object則是依靠 構(gòu)造器(constructor利用 原型(prototype構(gòu)造出來(lái)的。舉個(gè)客觀世界的例子來(lái)說(shuō)明二種方式認(rèn)知的差異。例如工廠造一輛車(chē),一方面,工人必須參照一張工程圖紙,設(shè)計(jì)規(guī)定這輛車(chē)應(yīng)該如何制造。這里的工程圖紙就好比是語(yǔ)言中的 類(lèi) (class),而車(chē)就是按照這個(gè) 類(lèi)(class制造出來(lái)的;另一方面,工人和機(jī)器 ( 相當(dāng)于 constructor) 利用各種零部件如發(fā)動(dòng)機(jī),輪胎,方向盤(pán) ( 相當(dāng)于 prototype 的各個(gè)屬性 ) 將汽車(chē)構(gòu)造出來(lái)。

事實(shí)上關(guān)于這兩種方式誰(shuí)更為徹底地表達(dá)了面向?qū)ο蟮乃枷?,目前尚有?zhēng)論。但筆者認(rèn)為原型式面向?qū)ο笫且环N更為徹底的面向?qū)ο蠓绞?,理由如下?/p>

首先,客觀世界中的對(duì)象的產(chǎn)生都是其它實(shí)物對(duì)象構(gòu)造的結(jié)果,而抽象的“圖紙”是不能產(chǎn)生“汽車(chē)”的,也就是說(shuō),類(lèi)是一個(gè)抽象概念而并非實(shí)體,而對(duì)象的產(chǎn)生是一個(gè)實(shí)體的產(chǎn)生;

其次,按照一切事物皆對(duì)象這個(gè)最基本的面向?qū)ο蟮姆▌t來(lái)看,類(lèi) (class) 本身并不是一個(gè)對(duì)象,然而原型方式中的構(gòu)造器 (constructor) 和原型 (prototype) 本身也是其他對(duì)象通過(guò)原型方式構(gòu)造出來(lái)的對(duì)象。

再次,在類(lèi)式面向?qū)ο笳Z(yǔ)言中,對(duì)象的狀態(tài) (state) 由對(duì)象實(shí)例 (instance) 所持有,對(duì)象的行為方法 (method) 則由聲明該對(duì)象的類(lèi)所持有,并且只有對(duì)象的結(jié)構(gòu)和方法能夠被繼承;而在原型式面向?qū)ο笳Z(yǔ)言中,對(duì)象的行為、狀態(tài)都屬于對(duì)象本身,并且能夠一起被繼承(參考資源),這也更貼近客觀實(shí)際。

最后,類(lèi)式面向?qū)ο笳Z(yǔ)言比如 Java,為了彌補(bǔ)無(wú)法使用面向過(guò)程語(yǔ)言中全局函數(shù)和變量的不便,允許在類(lèi)中聲明靜態(tài) (static) 屬性和靜態(tài)方法。而實(shí)際上,客觀世界不存在所謂靜態(tài)概念,因?yàn)橐磺惺挛锝詫?duì)象!而在原型式面向?qū)ο笳Z(yǔ)言中,除內(nèi)建對(duì)象 (build-in object) 外,不允許全局對(duì)象、方法或者屬性的存在,也沒(méi)有靜態(tài)概念。所有語(yǔ)言元素 (primitive) 必須依賴(lài)對(duì)象存在。但由于函數(shù)式語(yǔ)言的特點(diǎn),語(yǔ)言元素所依賴(lài)的對(duì)象是隨著運(yùn)行時(shí) (runtime) 上下文 (context) 變化而變化的,具體體現(xiàn)在 this 指針的變化。正是這種特點(diǎn)更貼近 “萬(wàn)物皆有所屬,宇宙乃萬(wàn)物生存之根本”的自然觀點(diǎn)。在 程序清單 1window 便類(lèi)似與宇宙的概念。

清單 1. 對(duì)象的上下文依賴(lài)
 <script>  var str = "我是一個(gè) String 對(duì)象 , 我聲明在這里 , 但我不是獨(dú)立存在的!" var obj = { des: "我是一個(gè) Object 對(duì)象 , 我聲明在這里,我也不是獨(dú)立存在的。" };  var fun = function() {     console.log( "我是一個(gè) Function 對(duì)象!誰(shuí)調(diào)用我,我屬于誰(shuí):", this );  };  obj.fun = fun;  console.log( this === window );     // 打印 true  console.log( window.str === str );  // 打印 true  console.log( window.obj === obj );  // 打印 true  console.log( window.fun === fun );  // 打印 true  fun();                              // 打印 我是一個(gè) Function 對(duì)象!誰(shuí)調(diào)用我,我屬于誰(shuí):window  obj.fun();                          // 打印 我是一個(gè) Function 對(duì)象!誰(shuí)調(diào)用我,我屬于誰(shuí):obj  fun.apply(str);                   // 打印 我是一個(gè) Function 對(duì)象!誰(shuí)調(diào)用我,我屬于誰(shuí):str  </script>

在接受了面向?qū)ο蟠嬖谝环N叫做基于原型實(shí)現(xiàn)的方式的事實(shí)之后,下面我們就可以來(lái)深入探討 ECMAScript 是如何依據(jù)這一方式構(gòu)造自己的語(yǔ)言的。

回頁(yè)首

最基本的面向?qū)ο?/h2>

ECMAScript 是一門(mén)徹底的面向?qū)ο蟮木幊陶Z(yǔ)言(參考資源),JavaScript 是其中的一個(gè)變種 (variant)。它提供了 6 種基本數(shù)據(jù)類(lèi)型,即 Boolean、Number、String、Null、Undefined、Object。為了實(shí)現(xiàn)面向?qū)ο螅?em>ECMAScript設(shè)計(jì)出了一種非常成功的數(shù)據(jù)結(jié)構(gòu) - JSON(JavaScript Object Notation), 這一經(jīng)典結(jié)構(gòu)已經(jīng)可以脫離語(yǔ)言而成為一種廣泛應(yīng)用的數(shù)據(jù)交互格式 (參考資源)。

應(yīng)該說(shuō),具有基本數(shù)據(jù)類(lèi)型和 JSON 構(gòu)造語(yǔ)法的 ECMAScript 已經(jīng)基本可以實(shí)現(xiàn)面向?qū)ο蟮木幊塘?。開(kāi)發(fā)者可以隨意地用 字面式聲明(literal notation方式來(lái)構(gòu)造一個(gè)對(duì)象,并對(duì)其不存在的屬性直接賦值,或者用 delete 將屬性刪除 ( 注:JS 中的 delete 關(guān)鍵字用于刪除對(duì)象屬性,經(jīng)常被誤作為 C++ 中的 delete,而后者是用于釋放不再使用的對(duì)象 ),如 程序清單 2。

清單 2. 字面式 (literal notation) 對(duì)象聲明
 var person = {     name: “張三”,     age: 26,     gender: “男”,     eat: function( stuff ) {         alert( “我在吃” + stuff );     }  };  person.height = 176;  delete person[ “age” ];

在實(shí)際開(kāi)發(fā)過(guò)程中,大部分初學(xué)者或者對(duì) JS 應(yīng)用沒(méi)有太高要求的開(kāi)發(fā)者也基本上只用到 ECMAScript 定義的這一部分內(nèi)容,就能滿(mǎn)足基本的開(kāi)發(fā)需求。然而,這樣的代碼復(fù)用性非常弱,與其他實(shí)現(xiàn)了繼承、派生、多態(tài)等等的類(lèi)式面向?qū)ο蟮膹?qiáng)類(lèi)型語(yǔ)言比較起來(lái)顯得有些干癟,不能滿(mǎn)足復(fù)雜的 JS 應(yīng)用開(kāi)發(fā)。所以 ECMAScript 引入原型來(lái)解決對(duì)象繼承問(wèn)題。

回頁(yè)首

使用函數(shù)構(gòu)造器構(gòu)造對(duì)象

除了 字面式聲明(literal notation方式之外,ECMAScript 允許通過(guò) 構(gòu)造器(constructor)創(chuàng)建對(duì)象。每個(gè)構(gòu)造器實(shí)際上是一個(gè) 函數(shù)(function) 對(duì)象, 該函數(shù)對(duì)象含有一個(gè)“prototype”屬性用于實(shí)現(xiàn) 基于原型的繼承prototype-based inheritance共享屬性(shared properties。對(duì)象可以由“new 關(guān)鍵字 + 構(gòu)造器調(diào)用”的方式來(lái)創(chuàng)建,如 程序清單 3

清單 3. 使用構(gòu)造器 (constructor) 創(chuàng)建對(duì)象
 // 構(gòu)造器 Person 本身是一個(gè)函數(shù)對(duì)象 function Person() { 	 // 此處可做一些初始化工作 }  // 它有一個(gè)名叫 prototype 的屬性 Person.prototype = {     name: “張三”,     age: 26,     gender: “男”,     eat: function( stuff ) {         alert( “我在吃” + stuff );     }  }  // 使用 new 關(guān)鍵字構(gòu)造對(duì)象 var p = new Person();

由于早期 JavaScript 的發(fā)明者為了使這門(mén)語(yǔ)言與大名鼎鼎的 Java 拉上關(guān)系 ( 雖然現(xiàn)在大家知道二者是雷鋒和雷鋒塔的關(guān)系 ),使用了 new 關(guān)鍵字來(lái)限定構(gòu)造器調(diào)用并創(chuàng)建對(duì)象,以使其在語(yǔ)法上跟 Java 創(chuàng)建對(duì)象的方式看上去類(lèi)似。但需要指出的是,這兩門(mén)語(yǔ)言的 new含義毫無(wú)關(guān)系,因?yàn)槠鋵?duì)象構(gòu)造的機(jī)理完全不同。也正是因?yàn)檫@里語(yǔ)法上的類(lèi)似,眾多習(xí)慣了類(lèi)式面向?qū)ο笳Z(yǔ)言中對(duì)象創(chuàng)建方式的程序員,難以透徹理解 JS 對(duì)象原型構(gòu)造的方式,因?yàn)樗麄兛偸遣幻靼自?JS 語(yǔ)言中,為什么“函數(shù)名可以作為類(lèi)名”的現(xiàn)象。而實(shí)質(zhì)上,JS 這里僅僅是借用了關(guān)鍵字 new,僅此而已;換句話(huà)說(shuō),ECMAScript 完全可以用其它 new 表達(dá)式來(lái)用調(diào)用構(gòu)造器創(chuàng)建對(duì)象。

回頁(yè)首

徹底理解原型鏈 (prototype chain)

在 ECMAScript 中,每個(gè)由構(gòu)造器創(chuàng)建的對(duì)象擁有一個(gè)指向構(gòu)造器 prototype 屬性值的 隱式引用(implicit reference,這個(gè)引用稱(chēng)之為 原型(prototype。進(jìn)一步,每個(gè)原型可以擁有指向自己原型的 隱式引用(即該原型的原型),如此下去,這就是所謂的 原型鏈(prototype chain參考資源)。在具體的語(yǔ)言實(shí)現(xiàn)中,每個(gè)對(duì)象都有一個(gè) __proto__ 屬性來(lái)實(shí)現(xiàn)對(duì)原型的 隱式引用。程序清單 4說(shuō)明了這一點(diǎn)。

清單 4. 對(duì)象的 __proto__ 屬性和隱式引用
 function Person( name ) {     this.name = name;  }  var p = new Person();  // 對(duì)象的隱式引用指向了構(gòu)造器的 prototype 屬性,所以此處打印 true  console.log( p.__proto__ === Person.prototype );  // 原型本身是一個(gè) Object 對(duì)象,所以他的隱式引用指向了 // Object 構(gòu)造器的 prototype 屬性 , 故而打印 true  console.log( Person.prototype.__proto__ === Object.prototype );  // 構(gòu)造器 Person 本身是一個(gè)函數(shù)對(duì)象,所以此處打印 true  console.log( Person.__proto__ === Function.prototype );

有了 原型鏈,便可以定義一種所謂的 屬性隱藏機(jī)制,并通過(guò)這種機(jī)制實(shí)現(xiàn)繼承。ECMAScript 規(guī)定,當(dāng)要給某個(gè)對(duì)象的屬性賦值時(shí),解釋器會(huì)查找該對(duì)象原型鏈中第一個(gè)含有該屬性的對(duì)象(注:原型本身就是一個(gè)對(duì)象,那么原型鏈即為一組對(duì)象的鏈。對(duì)象的原型鏈中的第一個(gè)對(duì)象是該對(duì)象本身)進(jìn)行賦值。反之,如果要獲取某個(gè)對(duì)象屬性的值,解釋器自然是返回該對(duì)象原型鏈中首先具有該屬性的對(duì)象屬性值。圖 1說(shuō)名了這中隱藏機(jī)制:

圖 1. 原型鏈中的屬性隱藏機(jī)制

在圖 1 中,object1->prototype1->prototype2 構(gòu)成了 對(duì)象 object1 的原型鏈,根據(jù)上述屬性隱藏機(jī)制,可以清楚地看到 prototype1 對(duì)象中的 property4 屬性和 prototype2 對(duì)象中的 property3 屬性皆被隱藏。理解了原型鏈,那么將非常容易理解 JS 中基于原型的繼承實(shí)現(xiàn)原理,程序清單 5 是利用原型鏈實(shí)現(xiàn)繼承的簡(jiǎn)單例子。

清單 5. 利用原型鏈 Horse->Mammal->Animal 實(shí)現(xiàn)繼承
 // 聲明 Animal 對(duì)象構(gòu)造器 function Animal() {  }  // 將 Animal 的 prototype 屬性指向一個(gè)對(duì)象, // 亦可直接理解為指定 Animal 對(duì)象的原型 Animal.prototype = {     name: animal",     weight: 0,     eat: function() {         alert( "Animal is eating!" );     }  }  // 聲明 Mammal 對(duì)象構(gòu)造器 function Mammal() {     this.name = "mammal";  }  // 指定 Mammal 對(duì)象的原型為一個(gè) Animal 對(duì)象。 // 實(shí)際上此處便是在創(chuàng)建 Mammal 對(duì)象和 Animal 對(duì)象之間的原型鏈 Mammal.prototype = new Animal();  // 聲明 Horse 對(duì)象構(gòu)造器 function Horse( height, weight ) {     this.name = "horse";     this.height = height;     this.weight = weight;  }  // 將 Horse 對(duì)象的原型指定為一個(gè) Mamal 對(duì)象,繼續(xù)構(gòu)建 Horse 與 Mammal 之間的原型鏈 Horse.prototype = new Mammal();  // 重新指定 eat 方法 , 此方法將覆蓋從 Animal 原型繼承過(guò)來(lái)的 eat 方法 Horse.prototype.eat = function() {     alert( "Horse is eating grass!" );  }  // 驗(yàn)證并理解原型鏈 var horse = new Horse( 100, 300 );  console.log( horse.__proto__ === Horse.prototype );  console.log( Horse.prototype.__proto__ === Mammal.prototype );  console.log( Mammal.prototype.__proto__ === Animal.prototype );

理解清單 5 中對(duì)象原型繼承邏輯實(shí)現(xiàn)的關(guān)鍵在于 Horse.prototype = new Mammal() 和 Mammal.prototype = new Animal() 這兩句代碼。首先,等式右邊的結(jié)果是構(gòu)造出一個(gè)臨時(shí)對(duì)象,然后將這個(gè)對(duì)象賦值給等式左邊對(duì)象的 prototype 屬性。也就是說(shuō)將右邊新建的對(duì)象作為左邊對(duì)象的原型。讀者可以將這兩個(gè)等式替換到相應(yīng)的程序清單 5 代碼最后兩行的等式中自行領(lǐng)悟。

回頁(yè)首

JavaScript 類(lèi)式繼承的實(shí)現(xiàn)方法

從代碼清單 5 可以看出,基于原型的繼承方式,雖然實(shí)現(xiàn)了代碼復(fù)用,但其行文松散且不夠流暢,可閱讀性差,不利于實(shí)現(xiàn)擴(kuò)展和對(duì)源代碼進(jìn)行有效地組織管理。不得不承認(rèn),類(lèi)式繼承方式在語(yǔ)言實(shí)現(xiàn)上更具健壯性,且在構(gòu)建可復(fù)用代碼和組織架構(gòu)程序方面具有明顯的優(yōu)勢(shì)。這使得程序員們希望尋找到一種能夠在 JavaScript 中以類(lèi)式繼承風(fēng)格進(jìn)行編碼的方法途徑。從抽象的角度來(lái)講,既然類(lèi)式繼承和原型繼承都是為實(shí)現(xiàn)面向?qū)ο蠖O(shè)計(jì)的,并且他們各自實(shí)現(xiàn)的載體語(yǔ)言在計(jì)算能力上是等價(jià)的 ( 因?yàn)閳D靈機(jī)的計(jì)算能力與 Lambda 演算的計(jì)算能力是等價(jià)的 ),那么能不能找到一種變換,使得原型式繼承語(yǔ)言通過(guò)該變換實(shí)現(xiàn)具有類(lèi)式繼承編碼的風(fēng)格呢?

目前一些主流的 JS 框架都提供了這種轉(zhuǎn)換機(jī)制,也即類(lèi)式聲明方法,比如 Dojo.declare()、Ext.entend() 等等。用戶(hù)使用這些框架,可以輕易而友好地組織自己的 JS 代碼。其實(shí),在眾多框架出現(xiàn)之前,JavaScript 大師 Douglas Crockford 最早利用三個(gè)函數(shù)對(duì) Function 對(duì)象進(jìn)行擴(kuò)展,實(shí)現(xiàn)了這種變換,關(guān)于它的實(shí)現(xiàn)細(xì)節(jié)可以(參考資源)。此外還有由 Dean Edwards實(shí)現(xiàn)的著名的 Base.js(參考資源)。值得一提的是,jQuery 之父 John Resig 在搏眾家之長(zhǎng)之后,用不到 30 行代碼便實(shí)現(xiàn)了自己的 Simple Inheritance。使用其提供的 extend 方法聲明類(lèi)非常簡(jiǎn)單。程序清單 6是使用了 Simple Inheritance庫(kù)實(shí)現(xiàn)類(lèi)的聲明的例子。其中最后一句打印輸出語(yǔ)句是對(duì) Simple Inheritance實(shí)現(xiàn)類(lèi)式繼承的最好說(shuō)明。

清單 6. 使用 Simple Inheritance 實(shí)現(xiàn)類(lèi)式繼承
 // 聲明 Person 類(lèi) var Person = Class.extend( {     _issleeping: true,     init: function( name ) {         this._name = name;     },     isSleeping: function() {         return this._issleeping;     }  } );  // 聲明 Programmer 類(lèi),并繼承 Person  var Programmer = Person.extend( {     init: function( name, issleeping ) {         // 調(diào)用父類(lèi)構(gòu)造函數(shù)        this._super( name );         // 設(shè)置自己的狀態(tài)        this._issleeping = issleeping;     }  } );  var person = new Person( "張三" );  var diors = new Programmer( "張江男", false );  // 打印 true  console.log( person.isSleeping() );  // 打印 false  console.log( diors.isSleeping() );  // 此處全為 true,故打印 true  console.log( person instanceof Person && person instanceof Class     && diors instanceof Programmer &&     diors instanceof Person && diors instanceof Class );

如果您已對(duì)原型、函數(shù)構(gòu)造器、閉包和基于上下文的 this 有了充分的理解,那么理解 Simple Inheritance 的實(shí)現(xiàn)原理也并非相當(dāng)困難。從本質(zhì)上講,var Person = Class.extend(...)該語(yǔ)句中,左邊的 Person 實(shí)際上是獲得了由 Class 調(diào)用 extend 方法返回的一個(gè)構(gòu)造器,也即一個(gè) function 對(duì)象的引用。順著這個(gè)思路,我們繼續(xù)介紹 Simple Inheritance 是如何做到這一點(diǎn),進(jìn)而實(shí)現(xiàn)了由原型繼承方式到類(lèi)式繼承方式的轉(zhuǎn)換的。圖 2 是 Simple Inheritance 的源碼及其附帶注釋。為了方便理解,用中文對(duì)代碼逐行補(bǔ)充說(shuō)明。

圖 2.Simple Inheritance 源碼解析

拋開(kāi)代碼第二部分,整體連貫地考察第一和第三部分會(huì)發(fā)現(xiàn),extend 函數(shù)的根本目的就是要構(gòu)造一個(gè)具有新原型屬性的新構(gòu)造器。我們不禁感嘆 John Resig的大師手筆及其對(duì) JS 語(yǔ)言本質(zhì)把握的細(xì)膩程度。至于 John Resig是如何想到這樣精妙的實(shí)現(xiàn)方法,感興趣的讀者可以閱讀本文 (參考資源),其中有詳細(xì)介紹關(guān)于最初設(shè)計(jì) Simple Inheritance 的思維過(guò)程。

回頁(yè)首

JavaScript 私有成員實(shí)現(xiàn)

到此為止,如果您任然對(duì) JavaScript 面向?qū)ο蟪謶岩蓱B(tài)度,那么這個(gè)懷疑一定是,JavaScript 沒(méi)有實(shí)現(xiàn)面向?qū)ο笾械男畔㈦[藏,即私有和公有。與其他類(lèi)式面向?qū)ο竽菢语@式地聲明私有公有成員的方式不同,JavaScript 的信息隱藏就是靠閉包實(shí)現(xiàn)的。見(jiàn) 程序清單 7:

清單 7. 使用閉包實(shí)現(xiàn)信息隱藏
 // 聲明 User 構(gòu)造器 function User( pwd ) {     // 定義私有屬性    var password = pwd;     // 定義私有方法     function getPassword() {         // 返回了閉包中的 password         return password;     }     // 特權(quán)函數(shù)聲明,用于該對(duì)象其他公有方法能通過(guò)該特權(quán)方法訪(fǎng)問(wèn)到私有成員    this.passwordService = function() {         return getPassword();     }  }  // 公有成員聲明 User.prototype.checkPassword = function( pwd ) {     return this.passwordService() === pwd;  };  // 驗(yàn)證隱藏性 var u = new User( "123456" );  // 打印 true  console.log( u.checkPassword( "123456" ) );  // 打印 undefined  console.log( u.password );  // 打印 true  console.log( typeof u.gePassword === "undefined" );

JavaScript 必須依賴(lài)閉包實(shí)現(xiàn)信息隱藏,是由其函數(shù)式語(yǔ)言特性所決定的。本文不會(huì)對(duì)函數(shù)式語(yǔ)言和閉包這兩個(gè)話(huà)題展開(kāi)討論,正如上文默認(rèn)您理解 JavaScript 中基于上下文的 this 一樣。關(guān)于 JavaScript 中實(shí)現(xiàn)信息隱藏,Douglas Crockford在《 Private members in JavaScript 》(參考資源)一文中有更權(quán)威和詳細(xì)的介紹。

回頁(yè)首

結(jié)束語(yǔ)

JavaScript 被認(rèn)為是世界上最受誤解的編程語(yǔ)言,因?yàn)樗砼?c 語(yǔ)言家族的外衣,表現(xiàn)的卻是 LISP 風(fēng)格的函數(shù)式語(yǔ)言特性;沒(méi)有類(lèi),卻實(shí)也徹底實(shí)現(xiàn)了面向?qū)ο?。要?duì)這門(mén)語(yǔ)言有透徹的理解,就必須扒開(kāi)其 c 語(yǔ)言的外衣,從新回到函數(shù)式編程的角度,同時(shí)摒棄原有類(lèi)的面向?qū)ο蟾拍钊W(xué)習(xí)領(lǐng)悟它。隨著近些年來(lái) Web 應(yīng)用的普及和 JS 語(yǔ)言自身的長(zhǎng)足發(fā)展,特別是后臺(tái) JS 引擎的出現(xiàn) ( 如基于 V8 的 NodeJS 等 ),可以預(yù)見(jiàn),原來(lái)只是作為玩具編寫(xiě)頁(yè)面效果的 JS 將獲得更廣闊發(fā)展天地。這樣的發(fā)展趨勢(shì),也對(duì) JS 程序員提出了更高要求。只有徹底領(lǐng)悟了這門(mén)語(yǔ)言,才有可能在大型的 JS 項(xiàng)目中發(fā)揮她的威力。

本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶(hù)發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
JavaScript面向?qū)ο蠛诵闹R(shí)歸納
分享 收藏 導(dǎo)長(zhǎng)圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服