一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

jQuery中的編程范式

 newpuresteel 2012-01-03
該帖已經(jīng)被評(píng)為精華帖
作者 正文
  • canonical
  • 等級(jí): 五星會(huì)員
  • 性別:
  • 文章: 167
  • 積分: 546
  • 來(lái)自: 北京
   發(fā)表時(shí)間:2011-12-25   最后修改:2011-12-27

     瀏覽器前端編程的面貌自2005年以來(lái)已經(jīng)發(fā)生了深刻的變化,這并不簡(jiǎn)單的意味著出現(xiàn)了大量功能豐富的基礎(chǔ)庫(kù),使得我們可以更加方便的編寫(xiě)業(yè)務(wù)代碼,更重要的是我們看待前端技術(shù)的觀念發(fā)生了重大轉(zhuǎn)變,明確意識(shí)到了如何以前端特有的方式釋放程序員的生產(chǎn)力。本文將結(jié)合jQuery源碼的實(shí)現(xiàn)原理,對(duì)javascript中涌現(xiàn)出的編程范式和常用技巧作一簡(jiǎn)單介紹。
 
1. AJAX: 狀態(tài)駐留,異步更新
    首先來(lái)看一點(diǎn)歷史。
A. 1995年Netscape公司的Brendan Eich開(kāi)發(fā)了javacript語(yǔ)言,這是一種動(dòng)態(tài)(dynamic)、弱類(lèi)型(weakly typed)、基于原型(prototype-based)的腳本語(yǔ)言。
B. 1999年微軟IE5發(fā)布,其中包含了XMLHTTP ActiveX控件。
C. 2001年微軟IE6發(fā)布,部分支持DOM level 1和CSS 2標(biāo)準(zhǔn)。
D. 2002年Douglas Crockford發(fā)明JSON格式。
至此,可以說(shuō)Web2.0所依賴(lài)的技術(shù)元素已經(jīng)基本成形,但是并沒(méi)有立刻在整個(gè)業(yè)界產(chǎn)生重大的影響。盡管一些“頁(yè)面異步局部刷新”的技巧在程序員中間秘密的流傳,甚至催生了bindows這樣龐大臃腫的類(lèi)庫(kù),但總的來(lái)說(shuō),前端被看作是貧瘠而又骯臟的沼澤地,只有后臺(tái)技術(shù)才是王道。到底還缺少些什么呢?
     當(dāng)我們站在今天的角度去回顧2005年之前的js代碼,包括那些當(dāng)時(shí)的牛人所寫(xiě)的代碼,可以明顯的感受到它們?cè)诔绦蚩刂屏ι系腻钊?。并不是說(shuō)2005年之前的js技術(shù)本身存在問(wèn)題,只是它們?cè)诟拍顚用嫔鲜且槐P(pán)散沙,缺乏統(tǒng)一的觀念,或者說(shuō)缺少自己獨(dú)特的風(fēng)格, 自己的靈魂。當(dāng)時(shí)大多數(shù)的人,大多數(shù)的技術(shù)都試圖在模擬傳統(tǒng)的面向?qū)ο笳Z(yǔ)言,利用傳統(tǒng)的面向?qū)ο蠹夹g(shù),去實(shí)現(xiàn)傳統(tǒng)的GUI模型的仿制品。
     2005年是變革的一年,也是創(chuàng)造概念的一年。伴隨著Google一系列讓人耳目一新的交互式應(yīng)用的發(fā)布,Jesse James Garrett的一篇文章《Ajax: A New Approach to Web Applications》被廣為傳播。Ajax這一前端特有的概念迅速將眾多分散的實(shí)踐統(tǒng)一在同一口號(hào)之下,引發(fā)了Web編程范式的轉(zhuǎn)換。所謂名不正則言不順,這下無(wú)名群眾可找到組織了。在未有Ajax之前,人們?cè)缫颜J(rèn)識(shí)到了B/S架構(gòu)的本質(zhì)特征在于瀏覽器和服務(wù)器的狀態(tài)空間是分離的,但是一般的解決方案都是隱藏這一區(qū)分,將前臺(tái)狀態(tài)同步到后臺(tái),由后臺(tái)統(tǒng)一進(jìn)行邏輯處理,例如ASP.NET。因?yàn)槿狈Τ墒斓脑O(shè)計(jì)模式支持前臺(tái)狀態(tài)駐留,在換頁(yè)的時(shí)候,已經(jīng)裝載的js對(duì)象將被迫被丟棄,這樣誰(shuí)還能指望它去完成什么復(fù)雜的工作嗎?
     Ajax明確提出界面是局部刷新的,前臺(tái)駐留了狀態(tài),這就促成了一種需要:需要js對(duì)象在前臺(tái)存在更長(zhǎng)的時(shí)間。這也就意味著需要將這些對(duì)象和功能有效的管理起來(lái),意味著更復(fù)雜的代碼組織技術(shù),意味著對(duì)模塊化,對(duì)公共代碼基的渴求。
     jQuery現(xiàn)有的代碼中真正與Ajax相關(guān)(使用XMLHTTP控件異步訪問(wèn)后臺(tái)返回?cái)?shù)據(jù))的部分其實(shí)很少,但是如果沒(méi)有Ajax, jQuery作為公共代碼基也就缺乏存在的理由。

2. 模塊化:管理名字空間
     當(dāng)大量的代碼產(chǎn)生出來(lái)以后,我們所需要的最基礎(chǔ)的概念就是模塊化,也就是對(duì)工作進(jìn)行分解和復(fù)用。工作得以分解的關(guān)鍵在于各人獨(dú)立工作的成果可以集成在一起。這意味著各個(gè)模塊必須基于一致的底層概念,可以實(shí)現(xiàn)交互,也就是說(shuō)應(yīng)該基于一套公共代碼基,屏蔽底層瀏覽器的不一致性,并實(shí)現(xiàn)統(tǒng)一的抽象層,例如統(tǒng)一的事件管理機(jī)制等。比統(tǒng)一代碼基更重要的是,各個(gè)模塊之間必須沒(méi)有名字沖突。否則,即使兩個(gè)模塊之間沒(méi)有任何交互,也無(wú)法共同工作。
     jQuery目前鼓吹的主要賣(mài)點(diǎn)之一就是對(duì)名字空間的良好控制。這甚至比提供更多更完善的功能點(diǎn)都重要的多。良好的模塊化允許我們復(fù)用任何來(lái)源的代碼,所有人的工作得以積累疊加。而功能實(shí)現(xiàn)僅僅是一時(shí)的工作量的問(wèn)題。jQuery使用module pattern的一個(gè)變種來(lái)減少對(duì)全局名字空間的影響,僅僅在window對(duì)象上增加了一個(gè)jQuery對(duì)象(也就是$函數(shù))。
     所謂的module pattern代碼如下,它的關(guān)鍵是利用匿名函數(shù)限制臨時(shí)變量的作用域。

Java代碼 復(fù)制代碼 收藏代碼
  1. var feature =(function() {   
  2.   
  3. // 私有變量和函數(shù)   
  4. var privateThing = 'secret',   
  5.     publicThing = 'not secret',   
  6.   
  7.     changePrivateThing = function() {   
  8.         privateThing = 'super secret';   
  9.     },   
  10.   
  11.     sayPrivateThing = function() {   
  12.         console.log(privateThing);   
  13.         changePrivateThing();   
  14.     };   
  15.   
  16. // 返回對(duì)外公開(kāi)的API   
  17. return {   
  18.     publicThing : publicThing,   
  19.     sayPrivateThing :  sayPrivateThing   
  20. }   
  21. })();  
 

  js本身缺乏包結(jié)構(gòu),不過(guò)經(jīng)過(guò)多年的嘗試之后業(yè)內(nèi)已經(jīng)逐漸統(tǒng)一了對(duì)包加載的認(rèn)識(shí),形成了RequireJs庫(kù)這樣得到一定共識(shí)的解決方案。jQuery可以與RequireJS庫(kù)良好的集成在一起, 實(shí)現(xiàn)更完善的模塊依賴(lài)管理。http:///docs/jquery.html

Java代碼 復(fù)制代碼 收藏代碼
  1. require(["jquery""jquery.my"], function() {   
  2.     //當(dāng)jquery.js和jquery.my.js都成功裝載之后執(zhí)行   
  3.     $(function(){   
  4.       $('#my').myFunc();   
  5.     });   
  6.   });  

 

  通過(guò)以下函數(shù)調(diào)用來(lái)定義模塊my/shirt, 它依賴(lài)于my/cart和my/inventory模塊,

Java代碼 復(fù)制代碼 收藏代碼
  1. require.def("my/shirt",   
  2.   ["my/cart""my/inventory"],   
  3.   function(cart, inventory) {   
  4.       // 這里使用module pattern來(lái)返回my/shirt模塊對(duì)外暴露的API   
  5.       return {   
  6.           color: "blue",   
  7.           size: "large"  
  8.           addToCart: function() {   
  9.               // decrement是my/inventory對(duì)外暴露的API   
  10.               inventory.decrement(this);   
  11.               cart.add(this);   
  12.           }   
  13.       }   
  14.   }   
  15. );  


3. 神奇的$:對(duì)象提升
      當(dāng)你第一眼看到$函數(shù)的時(shí)候,你想到了什么?傳統(tǒng)的編程理論總是告訴我們函數(shù)命名應(yīng)該準(zhǔn)確,應(yīng)該清晰無(wú)誤的表達(dá)作者的意圖,甚至聲稱(chēng)長(zhǎng)名字要優(yōu)于短名字,因?yàn)闇p少了出現(xiàn)歧義的可能性。但是,$是什么?亂碼?它所傳遞的信息實(shí)在是太隱晦,太曖昧了。$是由prototype.js庫(kù)發(fā)明的,它真的是一個(gè)神奇的函數(shù),因?yàn)樗梢詫⒁粋€(gè)原始的DOM節(jié)點(diǎn)提升(enhance)為一個(gè)具有復(fù)雜行為的對(duì)象。在prototype.js最初的實(shí)現(xiàn)中,$函數(shù)的定義為

Java代碼 復(fù)制代碼 收藏代碼
  1. var $ = function (id) {   
  2.   return "string" == typeof id ? document.getElementById(id) : id;   
  3. };  

  這基本對(duì)應(yīng)于如下公式

Java代碼 復(fù)制代碼 收藏代碼
  1. e = $(id)   

  這絕不僅僅是提供了一個(gè)聰明的函數(shù)名稱(chēng)縮寫(xiě),更重要的是在概念層面上建立了文本id與DOM element之間的一一對(duì)應(yīng)。在未有$之前,id與對(duì)應(yīng)的element之間的距離十分遙遠(yuǎn),一般要將element緩存到變量中,例如

Java代碼 復(fù)制代碼 收藏代碼
  1. var ea = docuement.getElementById('a');   
  2. var eb = docuement.getElementById('b');   
  3. ea.style....  

但是使用$之后,卻隨處可見(jiàn)如下的寫(xiě)法

Java代碼 復(fù)制代碼 收藏代碼
  1. $('header_'+id).style...   
  2. $('body_'+id)....  

id與element之間的距離似乎被消除了,可以非常緊密的交織在一起。
  prototype.js后來(lái)擴(kuò)展了$的含義,

Java代碼 復(fù)制代碼 收藏代碼
  1. function $() {   
  2.   var elements = new Array();   
  3.     
  4.   for (var i = 0; i < arguments.length; i++) {   
  5.       var element = arguments[i];   
  6.       if (typeof element == 'string')   
  7.         element = document.getElementById(element);   
  8.     
  9.       if (arguments.length == 1)   
  10.         return element;   
  11.     
  12.       elements.push(element);   
  13.   }   
  14.     
  15.   return elements;   
  16. }  

  這對(duì)應(yīng)于公式

Java代碼 復(fù)制代碼 收藏代碼
  1. [e,e] = $(id,id)  

  很遺憾,這一步prototype.js走偏了,這一做法很少有實(shí)用的價(jià)值。
  真正將$發(fā)揚(yáng)光大的是jQuery, 它的$對(duì)應(yīng)于公式

Java代碼 復(fù)制代碼 收藏代碼
  1. [o] = $(selector)  

  這里有三個(gè)增強(qiáng)
  A. selector不再是單一的節(jié)點(diǎn)定位符,而是復(fù)雜的集合選擇符
  B. 返回的元素不是原始的DOM節(jié)點(diǎn),而是經(jīng)過(guò)jQuery進(jìn)一步增強(qiáng)的具有豐富行為的對(duì)象,可以啟動(dòng)復(fù)雜的函數(shù)調(diào)用鏈。
  C. $返回的包裝對(duì)象被造型為數(shù)組形式,將集合操作自然的整合到調(diào)用鏈中。

  當(dāng)然,以上僅僅是對(duì)神奇的$的一個(gè)過(guò)分簡(jiǎn)化的描述,它的實(shí)際功能要復(fù)雜得多. 特別是有一個(gè)非常常用的直接構(gòu)造功能.

Java代碼 復(fù)制代碼 收藏代碼
  1. $("<table><tbody><tr><td>...</td></tr></tbody></table>")....  

  jQuery將根據(jù)傳入的html文本直接構(gòu)造出一系列的DOM節(jié)點(diǎn),并將其包裝為jQuery對(duì)象. 這在某種程度上可以看作是對(duì)selector的擴(kuò)展: html內(nèi)容描述本身就是一種唯一指定.
     $(function{})這一功能就實(shí)在是讓人有些無(wú)語(yǔ)了, 它表示當(dāng)document.ready的時(shí)候調(diào)用此回調(diào)函數(shù)。真的,$是一個(gè)神奇的函數(shù), 有任何問(wèn)題,請(qǐng)$一下。
     總結(jié)起來(lái), $是從普通的DOM和文本描述世界到具有豐富對(duì)象行為的jQuery世界的躍遷通道??邕^(guò)了這道門(mén),就來(lái)到了理想國(guó)。
  
4. 無(wú)定形的參數(shù):專(zhuān)注表達(dá)而不是約束
     弱類(lèi)型語(yǔ)言既然頭上頂著個(gè)"弱"字, 總難免讓人有些先天不足的感覺(jué). 在程序中缺乏類(lèi)型約束, 是否真的是一種重大的缺憾? 在傳統(tǒng)的強(qiáng)類(lèi)型語(yǔ)言中, 函數(shù)參數(shù)的類(lèi)型,個(gè)數(shù)等都是由編譯器負(fù)責(zé)檢查的約束條件, 但這些約束仍然是遠(yuǎn)遠(yuǎn)不夠的. 一般應(yīng)用程序中為了加強(qiáng)約束, 總會(huì)增加大量防御性代碼, 例如在C++中我們常用ASSERT, 而在java中也經(jīng)常需要判斷參數(shù)值的范圍

Java代碼 復(fù)制代碼 收藏代碼
  1. if (index < 0 || index >= size)   
  2.     throw new IndexOutOfBoundsException(   
  3.         "Index: "+index+", Size: "+size);              


     很顯然, 這些代碼將導(dǎo)致程序中存在大量無(wú)功能的執(zhí)行路徑, 即我們做了大量判斷, 代碼執(zhí)行到某個(gè)點(diǎn), 系統(tǒng)拋出異常, 大喊此路不通. 如果我們換一個(gè)思路, 既然已經(jīng)做了某種判斷,能否利用這些判斷的結(jié)果來(lái)做些什么呢? javascript是一種弱類(lèi)型的語(yǔ)言,它是無(wú)法自動(dòng)約束參數(shù)類(lèi)型的, 那如果順勢(shì)而行,進(jìn)一步弱化參數(shù)的形態(tài), 將"弱"推進(jìn)到一種極致, 在弱無(wú)可弱的時(shí)候, weak會(huì)不會(huì)成為標(biāo)志性的特點(diǎn)?
  看一下jQuery中的事件綁定函數(shù)bind,
   A. 一次綁定一個(gè)事件 $("#my").bind("mouseover", function(){});
   B. 一次綁定多個(gè)事件 $("#my").bind("mouseover mouseout",function(){})
   C. 換一個(gè)形式, 同樣綁定多個(gè)事件
      $("#my").bind({mouseover:function(){}, mouseout:function(){}});
   D. 想給事件監(jiān)聽(tīng)器傳點(diǎn)參數(shù)
      $('#my').bind('click', {foo: "xxxx"}, function(event) { event.data.foo..})
   E. 想給事件監(jiān)聽(tīng)器分個(gè)組
      $("#my").bind("click.myGroup″, function(){});
   F. 這個(gè)函數(shù)為什么還沒(méi)有瘋掉???
  
   就算是類(lèi)型不確定, 在固定位置上的參數(shù)的意義總要是確定的吧? 退一萬(wàn)步來(lái)說(shuō), 就算是參數(shù)位置不重要了,函數(shù)本身的意義應(yīng)該是確定的吧? 但這是什么?

Java代碼 復(fù)制代碼 收藏代碼
  1. 取值 value = o.val(), 設(shè)置值 o.val(3)        

   一個(gè)函數(shù)怎么可以這樣過(guò)分, 怎么能根據(jù)傳入?yún)?shù)的類(lèi)型和個(gè)數(shù)不同而行為不同呢? 看不順眼是不是? 可這就是俺們的價(jià)值觀. 既然不能防止, 那就故意允許. 雖然形式多變, 卻無(wú)一句廢話. 缺少約束, 不妨礙表達(dá)(我不是出來(lái)嚇人的).
  
5. 鏈?zhǔn)讲僮? 線性化的逐步細(xì)化
    jQuery早期最主要的賣(mài)點(diǎn)就是所謂的鏈?zhǔn)讲僮?chain).

Java代碼 復(fù)制代碼 收藏代碼
  1. $('#content'// 找到content元素   
  2.     .find('h3'// 選擇所有后代h3節(jié)點(diǎn)   
  3.     .eq(2)      // 過(guò)濾集合, 保留第三個(gè)元素   
  4.         .html('改變第三個(gè)h3的文本')   
  5.     .end()      // 返回上一級(jí)的h3集合   
  6.     .eq(0)   
  7.         .html('改變第一個(gè)h3的文本');   

在一般的命令式語(yǔ)言中, 我們總需要在重重嵌套循環(huán)中過(guò)濾數(shù)據(jù), 實(shí)際操作數(shù)據(jù)的代碼與定位數(shù)據(jù)的代碼糾纏在一起. 而jQuery采用先構(gòu)造集合然后再應(yīng)用函數(shù)于集合的方式實(shí)現(xiàn)兩種邏輯的解耦, 實(shí)現(xiàn)嵌套結(jié)構(gòu)的線性化. 實(shí)際上, 我們并不需要借助過(guò)程化的思想就可以很直觀的理解一個(gè)集合, 例如 $('div.my input:checked')可以看作是一種直接的描述,而不是對(duì)過(guò)程行為的跟蹤.
     循環(huán)意味著我們的思維處于一種反復(fù)回繞的狀態(tài), 而線性化之后則沿著一個(gè)方向直線前進(jìn), 極大減輕了思維負(fù)擔(dān), 提高了代碼的可組合性. 為了減少調(diào)用鏈的中斷, jQuery發(fā)明了一個(gè)絕妙的主意: jQuery包裝對(duì)象本身類(lèi)似數(shù)組(集合). 集合可以映射到新的集合, 集合可以限制到自己的子集合,調(diào)用的發(fā)起者是集合,返回結(jié)果也是集合,集合可以發(fā)生結(jié)構(gòu)上的某種變化但它還是集合, 集合是某種概念上的不動(dòng)點(diǎn),這是從函數(shù)式語(yǔ)言中吸取的設(shè)計(jì)思想。集合操作是太常見(jiàn)的操作, 在java中我們很容易發(fā)現(xiàn)大量所謂的封裝函數(shù)其實(shí)就是在封裝一些集合遍歷操作, 而在jQuery中集合操作因?yàn)樘卑锥恍枰庋b.
    鏈?zhǔn)秸{(diào)用意味著我們始終擁有一個(gè)“當(dāng)前”對(duì)象,所有的操作都是針對(duì)這一當(dāng)前對(duì)象進(jìn)行。這對(duì)應(yīng)于如下公式

Java代碼 復(fù)制代碼 收藏代碼
  1. x += dx   

調(diào)用鏈的每一步都是對(duì)當(dāng)前對(duì)象的增量描述,是針對(duì)最終目標(biāo)的逐步細(xì)化過(guò)程。Witrix平臺(tái)中對(duì)這一思想也有著廣泛的應(yīng)用。特別是為了實(shí)現(xiàn)平臺(tái)機(jī)制與業(yè)務(wù)代碼的融合,平臺(tái)會(huì)提供對(duì)象(容器)的缺省內(nèi)容,而業(yè)務(wù)代碼可以在此基礎(chǔ)上進(jìn)行逐步細(xì)化的修正,包括取消缺省的設(shè)置等。
    話說(shuō)回來(lái), 雖然表面上jQuery的鏈?zhǔn)秸{(diào)用很簡(jiǎn)單, 內(nèi)部實(shí)現(xiàn)的時(shí)候卻必須自己多寫(xiě)一層循環(huán), 因?yàn)榫幾g器并不知道"自動(dòng)應(yīng)用于集合中每個(gè)元素"這回事.

Java代碼 復(fù)制代碼 收藏代碼
  1. $.fn['someFunc'] = function(){   
  2.     return this.each(function(){   
  3.       jQuery.someFunc(this,...);   
  4.     }   
  5.   }   

 
6. data: 統(tǒng)一數(shù)據(jù)管理
     作為一個(gè)js庫(kù),它必須解決的一個(gè)大問(wèn)題就是js對(duì)象與DOM節(jié)點(diǎn)之間的狀態(tài)關(guān)聯(lián)與協(xié)同管理問(wèn)題。有些js庫(kù)選擇以js對(duì)象為主,在js對(duì)象的成員變量中保存DOM節(jié)點(diǎn)指針,訪問(wèn)時(shí)總是以js對(duì)象為入口點(diǎn),通過(guò)js函數(shù)間接操作DOM對(duì)象。在這種封裝下,DOM節(jié)點(diǎn)其實(shí)只是作為界面展現(xiàn)的一種底層“匯編”而已。jQuery的選擇與Witrix平臺(tái)類(lèi)似,都是以HTML自身結(jié)構(gòu)為基礎(chǔ),通過(guò)js增強(qiáng)(enhance)DOM節(jié)點(diǎn)的功能,將它提升為一個(gè)具有復(fù)雜行為的擴(kuò)展對(duì)象。這里的思想是非侵入式設(shè)計(jì)(non-intrusive)和優(yōu)雅退化機(jī)制(graceful degradation)。語(yǔ)義結(jié)構(gòu)在基礎(chǔ)的HTML層面是完整的,js的作用是增強(qiáng)了交互行為,控制了展現(xiàn)形式。
     如果每次我們都通過(guò)$('#my')的方式來(lái)訪問(wèn)相應(yīng)的包裝對(duì)象,那么一些需要長(zhǎng)期保持的狀態(tài)變量保存在什么地方呢?jQuery提供了一個(gè)統(tǒng)一的全局?jǐn)?shù)據(jù)管理機(jī)制。

Java代碼 復(fù)制代碼 收藏代碼
  1. 獲取數(shù)據(jù) $('#my').data('myAttr')   設(shè)置數(shù)據(jù) $('#my').data('myAttr',3);  

這一機(jī)制自然融合了對(duì)HTML5的data屬性的處理

Java代碼 復(fù)制代碼 收藏代碼
  1. <input id="my" data-my-attr="4" ... />   

 通過(guò) $('#my').data('myAttr')將可以讀取到HTML中設(shè)置的數(shù)據(jù)。
 
 第一次訪問(wèn)data時(shí),jQuery將為DOM節(jié)點(diǎn)分配一個(gè)唯一的uuid, 然后設(shè)置在DOM節(jié)點(diǎn)的一個(gè)特定的expando屬性上, jQuery保證這個(gè)uuid在本頁(yè)面中不重復(fù)。

Java代碼 復(fù)制代碼 收藏代碼
  1. elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ];  
 

 以上代碼可以同時(shí)處理DOM節(jié)點(diǎn)和純js對(duì)象的情況。如果是js對(duì)象,則data直接放置在js對(duì)象自身中,而如果是DOM節(jié)點(diǎn),則通過(guò)cache統(tǒng)一管理。
 因?yàn)樗械臄?shù)據(jù)都是通過(guò)data機(jī)制統(tǒng)一管理的,特別是包括所有事件監(jiān)聽(tīng)函數(shù)(data.events),因此jQuery可以安全的實(shí)現(xiàn)資源管理。在clone節(jié)點(diǎn)的時(shí)候,可以自動(dòng)clone其相關(guān)的事件監(jiān)聽(tīng)函數(shù)。而當(dāng)DOM節(jié)點(diǎn)的內(nèi)容被替換或者DOM節(jié)點(diǎn)被銷(xiāo)毀的時(shí)候,jQuery也可以自動(dòng)解除事件監(jiān)聽(tīng)函數(shù), 并安全的釋放相關(guān)的js數(shù)據(jù)。
 
7. event:統(tǒng)一事件模型
  "事件沿著對(duì)象樹(shù)傳播"這一圖景是面向?qū)ο蠼缑婢幊棠P偷木杷?。?duì)象的復(fù)合構(gòu)成對(duì)界面結(jié)構(gòu)的一個(gè)穩(wěn)定的描述,事件不斷在對(duì)象樹(shù)的某個(gè)節(jié)點(diǎn)發(fā)生,并通過(guò)冒泡機(jī)制向上傳播。對(duì)象樹(shù)很自然的成為一個(gè)控制結(jié)構(gòu),我們可以在父節(jié)點(diǎn)上監(jiān)聽(tīng)所有子節(jié)點(diǎn)上的事件,而不用明確與每一個(gè)子節(jié)點(diǎn)建立關(guān)聯(lián)。
  jQuery除了為不同瀏覽器的事件模型建立了統(tǒng)一抽象之外,主要做了如下增強(qiáng):
  A. 增加了自定制事件(custom)機(jī)制. 事件的傳播機(jī)制與事件內(nèi)容本身原則上是無(wú)關(guān)的, 因此自定制事件完全可以和瀏覽器內(nèi)置事件通過(guò)同一條處理路徑, 采用同樣的監(jiān)聽(tīng)方式. 使用自定制事件可以增強(qiáng)代碼的內(nèi)聚性, 減少代碼耦合. 例如如果沒(méi)有自定制事件, 關(guān)聯(lián)代碼往往需要直接操作相關(guān)的對(duì)象

Java代碼 復(fù)制代碼 收藏代碼
  1. $('.switch, .clapper').click(function() {   
  2.     var $light = $(this).parent().find('.lightbulb');   
  3.     if ($light.hasClass('on')) {   
  4.         $light.removeClass('on').addClass('off');   
  5.     } else {   
  6.         $light.removeClass('off').addClass('on');   
  7.     }   
  8.   });  
 

而如果使用自定制事件,則表達(dá)的語(yǔ)義更加內(nèi)斂明確,

Java代碼 復(fù)制代碼 收藏代碼
  1. $('.switch, .clapper').click(function() {   
  2.   $(this).parent().find('.lightbulb').trigger('changeState');   
  3. });  


  B. 增加了對(duì)動(dòng)態(tài)創(chuàng)建節(jié)點(diǎn)的事件監(jiān)聽(tīng). bind函數(shù)只能將監(jiān)聽(tīng)函數(shù)注冊(cè)到已經(jīng)存在的DOM節(jié)點(diǎn)上. 例如

Java代碼 復(fù)制代碼 收藏代碼
  1. $('li.trigger').bind('click',function(){}}  
 

  如果調(diào)用bind之后,新建了另一個(gè)li節(jié)點(diǎn),則該節(jié)點(diǎn)的click事件不會(huì)被監(jiān)聽(tīng).
  jQuery的delegate機(jī)制可以將監(jiān)聽(tīng)函數(shù)注冊(cè)到父節(jié)點(diǎn)上, 子節(jié)點(diǎn)上觸發(fā)的事件會(huì)根據(jù)selector被自動(dòng)派發(fā)到相應(yīng)的handlerFn上. 這樣一來(lái)現(xiàn)在注冊(cè)就可以監(jiān)聽(tīng)未來(lái)創(chuàng)建的節(jié)點(diǎn).

Java代碼 復(fù)制代碼 收藏代碼
  1. $('#myList').delegate('li.trigger''click', handlerFn);  
 

  最近jQuery1.7中統(tǒng)一了bind, live和delegate機(jī)制, 天下一統(tǒng), 只有on/off.

Java代碼 復(fù)制代碼 收藏代碼
  1. $('li.trigger’).on('click', handlerFn);  // 相當(dāng)于bind   
  2. $('#myList’).on('click', 'li.trigger', handlerFn);  // 相當(dāng)于delegate  

 

   
8. 動(dòng)畫(huà)隊(duì)列:全局時(shí)鐘協(xié)調(diào)
  拋開(kāi)jQuery的實(shí)現(xiàn)不談, 先考慮一下如果我們要實(shí)現(xiàn)界面上的動(dòng)畫(huà)效果, 到底需要做些什么? 比如我們希望將一個(gè)div的寬度在1秒鐘之內(nèi)從100px增加到200px. 很容易想見(jiàn), 在一段時(shí)間內(nèi)我們需要不時(shí)的去調(diào)整一下div的寬度, [同時(shí)]我們還需要執(zhí)行其他代碼. 與一般的函數(shù)調(diào)用不同的是, 發(fā)出動(dòng)畫(huà)指令之后, 我們不能期待立刻得到想要的結(jié)果, 而且我們不能原地等待結(jié)果的到來(lái). 動(dòng)畫(huà)的復(fù)雜性就在于:一次性表達(dá)之后要在一段時(shí)間內(nèi)執(zhí)行,而且有多條邏輯上的執(zhí)行路徑要同時(shí)展開(kāi), 如何協(xié)調(diào)?
  偉大的艾薩克.牛頓爵士在《自然哲學(xué)的數(shù)學(xué)原理》中寫(xiě)道:"絕對(duì)的、真正的和數(shù)學(xué)的時(shí)間自身在流逝著". 所有的事件可以在時(shí)間軸上對(duì)齊, 這就是它們內(nèi)在的協(xié)調(diào)性. 因此為了從步驟A1執(zhí)行到A5, 同時(shí)將步驟B1執(zhí)行到B5, 我們只需要在t1時(shí)刻執(zhí)行[A1, B1], 在t2時(shí)刻執(zhí)行[A2,B2], 依此類(lèi)推.

Java代碼 復(fù)制代碼 收藏代碼
  1. t1 | t2 | t3 | t4 | t5 ...   
  2. A1 | A2 | A3 | A4 | A5 ...   
  3. B1 | B2 | B3 | B4 | B5 ...  

  具體的一種實(shí)現(xiàn)形式可以是
  A. 對(duì)每個(gè)動(dòng)畫(huà), 將其分裝為一個(gè)Animation對(duì)象, 內(nèi)部分成多個(gè)步驟.

Java代碼 復(fù)制代碼 收藏代碼
  1. animation = new Animation(div,"width",100,200,1000,   
  2.             負(fù)責(zé)步驟切分的插值函數(shù),動(dòng)畫(huà)執(zhí)行完畢時(shí)的回調(diào)函數(shù));  

  B. 在全局管理器中注冊(cè)動(dòng)畫(huà)對(duì)象

Java代碼 復(fù)制代碼 收藏代碼
  1. timerFuncs.add(animation);  

  C. 在全局時(shí)鐘的每一個(gè)觸發(fā)時(shí)刻, 將每個(gè)注冊(cè)的執(zhí)行序列推進(jìn)一步, 如果已經(jīng)結(jié)束, 則從全局管理器中刪除.

Java代碼 復(fù)制代碼 收藏代碼
  1. for each animation in timerFuncs   
  2.    if(!animation.doOneStep())   
  3.       timerFuncs.remove(animation)  


  解決了原理問(wèn)題,再來(lái)看看表達(dá)問(wèn)題, 怎樣設(shè)計(jì)接口函數(shù)才能夠以最緊湊形式表達(dá)我們的意圖? 我們經(jīng)常需要面臨的實(shí)際問(wèn)題:
  A. 有多個(gè)元素要執(zhí)行類(lèi)似的動(dòng)畫(huà)
  B. 每個(gè)元素有多個(gè)屬性要同時(shí)變化
  C. 執(zhí)行完一個(gè)動(dòng)畫(huà)之后開(kāi)始另一個(gè)動(dòng)畫(huà)
jQuery對(duì)這些問(wèn)題的解答可以說(shuō)是榨盡了js語(yǔ)法表達(dá)力的最后一點(diǎn)剩余價(jià)值.

Java代碼 復(fù)制代碼 收藏代碼
  1. $('input')   
  2.   .animate({left:'+=200px',top:'300'},2000)   
  3.   .animate({left:'-=200px',top:20},1000)   
  4.   .queue(function(){   
  5.     // 這里dequeue將首先執(zhí)行隊(duì)列中的后一個(gè)函數(shù),因此alert("y")   
  6.     $(this).dequeue();   
  7.     alert('x');   
  8.    })   
  9.   .queue(function(){   
  10.      alert("y");   
  11.      // 如果不主動(dòng)dequeue, 隊(duì)列執(zhí)行就中斷了,不會(huì)自動(dòng)繼續(xù)下去.   
  12.      $(this).dequeue();   
  13.    });  


  A. 利用jQuery內(nèi)置的selector機(jī)制自然表達(dá)對(duì)一個(gè)集合的處理.
  B. 使用Map表達(dá)多個(gè)屬性變化
  C. 利用微格式表達(dá)領(lǐng)域特定的差量概念. '+=200px'表示在現(xiàn)有值的基礎(chǔ)上增加200px
  D. 利用函數(shù)調(diào)用的順序自動(dòng)定義animation執(zhí)行的順序: 在后面追加到執(zhí)行隊(duì)列中的動(dòng)畫(huà)自然要等前面的動(dòng)畫(huà)完全執(zhí)行完畢之后再啟動(dòng).
  
  jQuery動(dòng)畫(huà)隊(duì)列的實(shí)現(xiàn)細(xì)節(jié)大概如下所示,
   A. animate函數(shù)實(shí)際是調(diào)用queue(function(){執(zhí)行結(jié)束時(shí)需要調(diào)用dequeue,否則不會(huì)驅(qū)動(dòng)下一個(gè)方法})
      queue函數(shù)執(zhí)行時(shí), 如果是fx隊(duì)列, 并且當(dāng)前沒(méi)有正在運(yùn)行動(dòng)畫(huà)(如果連續(xù)調(diào)用兩次animate,第二次的執(zhí)行函數(shù)將在隊(duì)列中等待),則會(huì)自動(dòng)觸發(fā)dequeue操作, 驅(qū)動(dòng)隊(duì)列運(yùn)行.
      如果是fx隊(duì)列, dequeue的時(shí)候會(huì)自動(dòng)在隊(duì)列頂端加入"inprogress"字符串,表示將要執(zhí)行的是動(dòng)畫(huà).
   B. 針對(duì)每一個(gè)屬性,創(chuàng)建一個(gè)jQuery.fx對(duì)象。然后調(diào)用fx.custom函數(shù)(相當(dāng)于start)來(lái)啟動(dòng)動(dòng)畫(huà)。
   C. custom函數(shù)中將fx.step函數(shù)注冊(cè)到全局的timerFuncs中,然后試圖啟動(dòng)一個(gè)全局的timer.
       timerId = setInterval( fx.tick, fx.interval );
   D. 靜態(tài)的tick函數(shù)中將依次調(diào)用各個(gè)fx的step函數(shù)。step函數(shù)中通過(guò)easing計(jì)算屬性的當(dāng)前值,然后調(diào)用fx的update來(lái)更新屬性。
   E. fx的step函數(shù)中判斷如果所有屬性變化都已完成,則調(diào)用dequeue來(lái)驅(qū)動(dòng)下一個(gè)方法。

  很有意思的是, jQuery的實(shí)現(xiàn)代碼中明顯有很多是接力觸發(fā)代碼: 如果需要執(zhí)行下一個(gè)動(dòng)畫(huà)就取出執(zhí)行, 如果需要啟動(dòng)timer就啟動(dòng)timer等. 這是因?yàn)閖s程序是單線程的,真正的執(zhí)行路徑只有一條,為了保證執(zhí)行線索不中斷, 函數(shù)們不得不互相幫助一下. 可以想見(jiàn), 如果程序內(nèi)部具有多個(gè)執(zhí)行引擎, 甚至無(wú)限多的執(zhí)行引擎, 那么程序的面貌就會(huì)發(fā)生本質(zhì)性的改變. 而在這種情形下, 遞歸相對(duì)于循環(huán)而言會(huì)成為更自然的描述.
 
9. promise模式:因果關(guān)系的識(shí)別
  現(xiàn)實(shí)中,總有那么多時(shí)間線在獨(dú)立的演化著, 人與物在時(shí)空中交錯(cuò),卻沒(méi)有發(fā)生因果. 軟件中, 函數(shù)們?cè)谠创a中排著隊(duì), 難免會(huì)產(chǎn)生一些疑問(wèn), 憑什么排在前面的要先執(zhí)行? 難道沒(méi)有它就沒(méi)有我? 讓全宇宙喊著1,2,3齊步前進(jìn), 從上帝的角度看,大概是管理難度過(guò)大了, 于是便有了相對(duì)論. 如果相互之間沒(méi)有交換信息, 沒(méi)有產(chǎn)生相互依賴(lài), 那么在某個(gè)坐標(biāo)系中順序發(fā)生的事件, 在另外一個(gè)坐標(biāo)系中看來(lái), 就可能是顛倒順序的. 程序員依葫蘆畫(huà)瓢, 便發(fā)明了promise模式.
  promise與future模式基本上是一回事,我們先來(lái)看一下java中熟悉的future模式.

Java代碼 復(fù)制代碼 收藏代碼
  1. futureResult = doSomething();   
  2. ...   
  3. realResult = futureResult.get();  


  發(fā)出函數(shù)調(diào)用僅僅意味著一件事情發(fā)生過(guò), 并不必然意味著調(diào)用者需要了解事情最終的結(jié)果. 函數(shù)立刻返回的只是一個(gè)將在未來(lái)兌現(xiàn)的承諾(Future類(lèi)型), 實(shí)際上也就是某種句柄. 句柄被傳來(lái)傳去, 中間轉(zhuǎn)手的代碼對(duì)實(shí)際結(jié)果是什么,是否已經(jīng)返回漠不關(guān)心. 直到一段代碼需要依賴(lài)調(diào)用返回的結(jié)果, 因此它打開(kāi)future, 查看了一下. 如果實(shí)際結(jié)果已經(jīng)返回, 則future.get()立刻返回實(shí)際結(jié)果, 否則將會(huì)阻塞當(dāng)前的執(zhí)行路徑, 直到結(jié)果返回為止. 此后再調(diào)用future.get()總是立刻返回, 因?yàn)橐蚬P(guān)系已經(jīng)被建立, [結(jié)果返回]這一事件必然在此之前發(fā)生, 不會(huì)再發(fā)生變化.
  future模式一般是外部對(duì)象主動(dòng)查看future的返回值, 而promise模式則是由外部對(duì)象在promise上注冊(cè)回調(diào)函數(shù).

Java代碼 復(fù)制代碼 收藏代碼
  1. function getData(){   
  2.  return $.get('/foo/').done(function(){   
  3.     console.log('Fires after the AJAX request succeeds');   
  4.  }).fail(function(){   
  5.     console.log('Fires after the AJAX request fails');   
  6.  });   
  7. }   
  8.   
  9. function showDiv(){   
  10.   var dfd = $.Deferred();   
  11.   $('#foo').fadeIn( 1000, dfd.resolve );   
  12.   return dfd.promise();   
  13. }   
  14.   
  15. $.when( getData(), showDiv() )   
  16.   .then(function( ajaxResult, ignoreResultFromShowDiv ){   
  17.       console.log('Fires after BOTH showDiv() AND the AJAX request succeed!');   
  18.       // 'ajaxResult' is the server’s response   
  19.   });  


  jQuery引入Deferred結(jié)構(gòu), 根據(jù)promise模式對(duì)ajax, queue, document.ready等進(jìn)行了重構(gòu), 統(tǒng)一了異步執(zhí)行機(jī)制. then(onDone, onFail)將向promise中追加回調(diào)函數(shù), 如果調(diào)用成功完成(resolve), 則回調(diào)函數(shù)onDone將被執(zhí)行, 而如果調(diào)用失敗(reject), 則onFail將被執(zhí)行. when可以等待在多個(gè)promise對(duì)象上. promise巧妙的地方是異步執(zhí)行已經(jīng)開(kāi)始之后甚至已經(jīng)結(jié)束之后,仍然可以注冊(cè)回調(diào)函數(shù)
  someObj.done(callback).sendRequest() vs. someObj.sendRequest().done(callback)
 callback函數(shù)在發(fā)出異步調(diào)用之前注冊(cè)或者在發(fā)出異步調(diào)用之后注冊(cè)是完全等價(jià)的, 這揭示出程序表達(dá)永遠(yuǎn)不是完全精確的, 總存在著內(nèi)在的變化維度. 如果能有效利用這一內(nèi)在的可變性, 則可以極大提升并發(fā)程序的性能.
   promise模式的具體實(shí)現(xiàn)很簡(jiǎn)單. jQuery._Deferred定義了一個(gè)函數(shù)隊(duì)列,它的作用有以下幾點(diǎn):
   A. 保存回調(diào)函數(shù)。
   B. 在resolve或者reject的時(shí)刻把保存著的函數(shù)全部執(zhí)行掉。
   C. 已經(jīng)執(zhí)行之后, 再增加的函數(shù)會(huì)被立刻執(zhí)行。
 
   一些專(zhuān)門(mén)面向分布式計(jì)算或者并行計(jì)算的語(yǔ)言會(huì)在語(yǔ)言級(jí)別內(nèi)置promise模式, 比如E語(yǔ)言.

Java代碼 復(fù)制代碼 收藏代碼
  1. def carPromise := carMaker <- produce("Mercedes");   
  2. def temperaturePromise := carPromise <- getEngineTemperature()   
  3. ...   
  4. when (temperaturePromise) -> done(temperature) {   
  5.   println(`The temperature of the car engine is: $temperature`)   
  6. catch e {   
  7.   println(`Could not get engine temperature, error: $e`)   
  8. }  

  在E語(yǔ)言中, <-是eventually運(yùn)算符, 表示最終會(huì)執(zhí)行, 但不一定是現(xiàn)在. 而普通的car.moveTo(2,3)表示立刻執(zhí)行得到結(jié)果. 編譯器負(fù)責(zé)識(shí)別所有的promise依賴(lài), 并自動(dòng)實(shí)現(xiàn)調(diào)度.
 
10. extend: 繼承不是必須的
  js是基于原型的語(yǔ)言, 并沒(méi)有內(nèi)置的繼承機(jī)制, 這一直讓很多深受傳統(tǒng)面向?qū)ο蠼逃耐瑢W(xué)們耿耿于懷. 但繼承一定是必須的嗎? 它到底能夠給我們帶來(lái)什么? 最純樸的回答是: 代碼重用. 那么, 我們首先來(lái)分析一下繼承作為代碼重用手段的潛力.
  曾經(jīng)有個(gè)概念叫做"多重繼承", 它是繼承概念的超級(jí)賽亞人版, 很遺憾后來(lái)被診斷為存在著先天缺陷, 以致于出現(xiàn)了一種對(duì)于繼承概念的解讀: 繼承就是"is a"關(guān)系, 一個(gè)派生對(duì)象"is a"很多基類(lèi), 必然會(huì)出現(xiàn)精神分裂, 所以多重繼承是不好的.

Java代碼 復(fù)制代碼 收藏代碼
  1. class A{ publicvoid f(){ f in A } }   
  2. class B{ publicvoid f(){ f in B } }   
  3. class D: public A, B{}  

 如果D類(lèi)從A,B兩個(gè)基類(lèi)繼承, 而A和B類(lèi)中都實(shí)現(xiàn)了同一個(gè)函數(shù)f, 那么D類(lèi)中的f到底是A中的f還是B中的f, 抑或是A中的f+B中的f呢? 這一困境的出現(xiàn)實(shí)際上源于D的基類(lèi)A和B是并列關(guān)系, 它們滿(mǎn)足交換律和結(jié)合律, 畢竟,在概念層面上我們可能難以認(rèn)可兩個(gè)任意概念之間會(huì)出現(xiàn)從屬關(guān)系. 但如果我們放松一些概念層面的要求, 更多的從操作層面考慮一下代碼重用問(wèn)題, 可以簡(jiǎn)單的認(rèn)為B在A的基礎(chǔ)上進(jìn)行操作, 那么就可以得到一個(gè)線性化的結(jié)果. 也就是說(shuō), 放棄A和B之間的交換律只保留結(jié)合律, extends A, B 與 extends B,A 會(huì)是兩個(gè)不同的結(jié)果, 不再存在詮釋上的二義性. scala語(yǔ)言中的所謂trait(特性)機(jī)制實(shí)際上采用的就是這一策略.
  面向?qū)ο蠹夹g(shù)發(fā)明很久之后, 出現(xiàn)了所謂的面向方面編程(AOP), 它與OOP不同, 是代碼結(jié)構(gòu)空間中的定位與修改技術(shù). AOP的眼中只有類(lèi)與方法, 不知道什么叫做意義. AOP也提供了一種類(lèi)似多重繼承的代碼重用手段, 那就是mixin. 對(duì)象被看作是可以被打開(kāi),然后任意修改的Map, 一組成員變量與方法就被直接注射到對(duì)象體內(nèi), 直接改變了它的行為.
  prototype.js庫(kù)引入了extend函數(shù),

Java代碼 復(fù)制代碼 收藏代碼
  1. Object.extend = function(destination, source) {   
  2.   for (var property in source) {   
  3.     destination[property] = source[property];   
  4.   }   
  5.   return destination;   
  6. }  

 

  就是Map之間的一個(gè)覆蓋運(yùn)算, 但很管用, 在jQuery庫(kù)中也得到了延用. 這個(gè)操作類(lèi)似于mixin, 在jQuery中是代碼重用的主要技術(shù)手段---沒(méi)有繼承也沒(méi)什么大不了的.

11. 名稱(chēng)映射: 一切都是數(shù)據(jù)

  代碼好不好, 循環(huán)判斷必須少. 循環(huán)和判斷語(yǔ)句是程序的基本組成部分, 但是優(yōu)良的代碼庫(kù)中卻往往找不到它們的蹤影, 因?yàn)檫@些語(yǔ)句的交織會(huì)模糊系統(tǒng)的邏輯主線, 使我們的思想迷失在疲于奔命的代碼追蹤中. jQuery本身通過(guò)each, extend等函數(shù)已經(jīng)極大減少了對(duì)循環(huán)語(yǔ)句的需求, 對(duì)于判斷語(yǔ)句, 則主要是通過(guò)映射表來(lái)處理. 例如, jQuery的val()函數(shù)需要針對(duì)不同標(biāo)簽進(jìn)行不同的處理, 因此定義一個(gè)以tagName為key的函數(shù)映射表

Java代碼 復(fù)制代碼 收藏代碼
  1. valHooks: { option: {get:function(){}}}  

這樣在程序中就不需要到處寫(xiě)

Java代碼 復(fù)制代碼 收藏代碼
  1. if(elm.tagName == 'OPTION'){   
  2.   return ...;   
  3. }else if(elm.tagName == 'TEXTAREA'){   
  4.   return ...;   
  5. }   


可以統(tǒng)一處理

Java代碼 復(fù)制代碼 收藏代碼
  1. (valHooks[elm.tagName.toLowerCase()] || defaultHandler).get(elm);  

  
  映射表將函數(shù)作為普通數(shù)據(jù)來(lái)管理, 在動(dòng)態(tài)語(yǔ)言中有著廣泛的應(yīng)用. 特別是, 對(duì)象本身就是函數(shù)和變量的容器, 可以被看作是映射表. jQuery中大量使用的一個(gè)技巧就是利用名稱(chēng)映射來(lái)動(dòng)態(tài)生成代碼, 形成一種類(lèi)似模板的機(jī)制. 例如為了實(shí)現(xiàn)myWidth和myHeight兩個(gè)非常類(lèi)似的函數(shù), 我們不需要

Java代碼 復(fù)制代碼 收藏代碼
  1. jQuery.fn.myWidth = function(){   
  2.     return parseInt(this.style.width,10) + 10;   
  3.   }   
  4.     
  5.   jQuery.fn.myHeight = function(){   
  6.     return parseInt(this.style.height,10) + 10;   
  7.   }  

而可以選擇動(dòng)態(tài)生成

Java代碼 復(fù)制代碼 收藏代碼
  1. jQuery.each(['Width','Height'],function(name){   
  2.   jQuery.fn['my'+name] = function(){   
  3.     return parseInt(this.style[name.toLowerCase()],10) + 10;   
  4.   }   
  5. });   


12. 插件機(jī)制:其實(shí)我很簡(jiǎn)單  
  jQuery所謂的插件其實(shí)就是$.fn上增加的函數(shù), 那這個(gè)fn是什么東西?

Java代碼 復(fù)制代碼 收藏代碼
  1. (function(window,undefined){   
  2.   // 內(nèi)部又有一個(gè)包裝   
  3.   var jQuery = (function() {   
  4.     var jQuery = function( selector, context ) {   
  5.           return new jQuery.fn.init( selector, context, rootjQuery );   
  6.       }   
  7.      ....   
  8.     // fn實(shí)際就是prototype的簡(jiǎn)寫(xiě)   
  9.     jQuery.fn = jQuery.prototype = {   
  10.         constructor: jQuery,   
  11.         init: function( selector, context, rootjQuery ) {...  }   
  12.     }   
  13.     
  14.     // 調(diào)用jQuery()就是相當(dāng)于new init(), 而init的prototype就是jQuery的prototype   
  15.     jQuery.fn.init.prototype = jQuery.fn;   
  16.     
  17.     // 這里返回的jQuery對(duì)象只具備最基本的功能, 下面就是一系列的extend   
  18.     return jQuery;   
  19.   })();    
  20.   ...   
  21.    // 將jQuery暴露為全局對(duì)象   
  22.   window.jQuery = window.$ = jQuery;   
  23. })(window);   


  顯然, $.fn其實(shí)就是jQuery.prototype的簡(jiǎn)寫(xiě).
 
  無(wú)狀態(tài)的插件僅僅就是一個(gè)函數(shù), 非常簡(jiǎn)單.

Java代碼 復(fù)制代碼 收藏代碼
  1. // 定義插件   
  2. (function($){   
  3.     $.fn.hoverClass = function(c) {   
  4.         return this.hover(   
  5.             function() { $(this).toggleClass(c); }   
  6.         );   
  7.     };   
  8. })(jQuery);   
  9.   
  10. // 使用插件   
  11. $('li').hoverClass('hover');  


 
 對(duì)于比較復(fù)雜的插件開(kāi)發(fā), jQuery UI提供了一個(gè)widget工廠機(jī)制,

Java代碼 復(fù)制代碼 收藏代碼
  1. $.widget("ui.dialog", {   
  2.   options: {   
  3.        autoOpen: true,...   
  4.     },   
  5.     _create: function(){ ... },   
  6.     _init: function() {   
  7.        if ( this.options.autoOpen ) {   
  8.            this.open();   
  9.        }   
  10.     },   
  11.     _setOption: function(key, value){ ... }   
  12.     destroy: function(){ ... }   
  13. });  

 
 調(diào)用 $('#dlg').dialog(options)時(shí), 實(shí)際執(zhí)行的代碼基本如下所示:

Java代碼 復(fù)制代碼 收藏代碼
  1. this.each(function() {   
  2.       var instance = $.data( this"dialog" );   
  3.       if ( instance ) {   
  4.           instance.option( options || {} )._init();   
  5.       } else {   
  6.           $.data( this"dialog"new $.ui.dialog( options, this ) );   
  7.       }   
  8.   }  

 可以看出, 第一次調(diào)用$('#dlg').dialog()函數(shù)時(shí)會(huì)創(chuàng)建窗口對(duì)象實(shí)例,并保存在data中, 此時(shí)會(huì)調(diào)用_create()和_init()函數(shù), 而如果不是第一次調(diào)用, 則是在已經(jīng)存在的對(duì)象實(shí)例上調(diào)用_init()方法. 多次調(diào)用$('#dlg').dialog()并不會(huì)創(chuàng)建多個(gè)實(shí)例.

13. browser sniffer vs. feature detection
  瀏覽器嗅探(browser sniffer)曾經(jīng)是很流行的技術(shù), 比如早期的jQuery中

Java代碼 復(fù)制代碼 收藏代碼
  1. jQuery.browser = {   
  2.       version:(userAgent.match(/.+(?:rv|it|ra|ie)[/: ]([d.]+)/) || [0,'0'])[1],   
  3.       safari:/webkit/.test(userAgent),   
  4.       opera:/opera/.test(userAgent),   
  5.       msie:/msie/.test(userAgent) && !/opera/.test(userAgent),   
  6.       mozilla:/mozilla/.test(userAgent) && !/(compatible|webkit)/.test(userAgent)   
  7. };  

  在具體代碼中可以針對(duì)不同的瀏覽器作出不同的處理
  if($.browser.msie) {
      // do something
  } else if($.browser.opera) {
      // ...
  }

Java代碼 復(fù)制代碼 收藏代碼
  1. if($.browser.msie) {   
  2.     // do something   
  3. else if($.browser.opera) {   
  4.     // ...   
  5. }   

  但是隨著瀏覽器市場(chǎng)的競(jìng)爭(zhēng)升級(jí), 競(jìng)爭(zhēng)對(duì)手之間的互相模仿和偽裝導(dǎo)致userAgent一片混亂, 加上Chrome的誕生, Safari的崛起, IE也開(kāi)始加速向標(biāo)準(zhǔn)靠攏, sniffer已經(jīng)起不到積極的作用. 特性檢測(cè)(feature detection)作為更細(xì)粒度, 更具體的檢測(cè)手段, 逐漸成為處理瀏覽器兼容性的主流方式.

Java代碼 復(fù)制代碼 收藏代碼
  1. jQuery.support = {   
  2.       // IE strips leading whitespace when .innerHTML is used   
  3.       leadingWhitespace: ( div.firstChild.nodeType === 3 ),   
  4.       ...   
  5.   }  

    只基于實(shí)際看見(jiàn)的,而不是曾經(jīng)知道的, 這樣更容易做到兼容未來(lái).

14. Prototype vs. jQuery
  prototype.js是一個(gè)立意高遠(yuǎn)的庫(kù), 它的目標(biāo)是提供一種新的使用體驗(yàn),參照Ruby從語(yǔ)言級(jí)別對(duì)javascript進(jìn)行改造,并最終真的極大改變了js的面貌。$, extends, each, bind...這些耳熟能詳?shù)母拍疃际莗rototype.js引入到j(luò)s領(lǐng)域的. 它肆無(wú)忌憚的在window全局名字空間中增加各種概念, 大有誰(shuí)先占坑誰(shuí)有理, 舍我其誰(shuí)的氣勢(shì). 而jQuery則扣扣索索, 抱著比較實(shí)用化的理念, 目標(biāo)僅僅是write less, do more而已. 
  不過(guò)等待激進(jìn)的理想主義者的命運(yùn)往往都是壯志未酬身先死. 當(dāng)prototype.js標(biāo)志性的bind函數(shù)等被吸收到ECMAScript標(biāo)準(zhǔn)中時(shí), 便注定了它的沒(méi)落. 到處修改原生對(duì)象的prototype, 這是prototype.js的獨(dú)門(mén)秘技, 也是它的死穴. 特別是當(dāng)它試圖模仿jQuery, 通過(guò)Element.extend(element)返回增強(qiáng)對(duì)象的時(shí)候, 算是徹底被jQuery給帶到溝里去了. prototype.js與jQuery不同, 它總是直接修改原生對(duì)象的prototype, 而瀏覽器卻是充滿(mǎn)bug, 謊言, 歷史包袱并夾雜著商業(yè)陰謀的領(lǐng)域, 在原生對(duì)象層面解決問(wèn)題注定是一場(chǎng)悲劇. 性能問(wèn)題, 名字沖突, 兼容性問(wèn)題等等都是一個(gè)幫助庫(kù)的能力所無(wú)法解決的. Prototype.js的2.0版本據(jù)說(shuō)要做大的變革, 不知是要與歷史決裂, 放棄兼容性, 還是繼續(xù)掙扎, 在夾縫中求生.

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶(hù)發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶(hù) 評(píng)論公約

    類(lèi)似文章 更多

    隔壁的日本人妻中文字幕版| 亚洲中文字幕一区三区| 国产成人精品久久二区二区| 国产剧情欧美日韩中文在线| 亚洲精品一区二区三区免| 亚洲精品福利视频你懂的| 成人午夜爽爽爽免费视频| 国产内射一级二级三级| 这里只有九九热精品视频| 少妇人妻精品一区二区三区| 老司机精品国产在线视频| 婷婷激情五月天丁香社区| 亚洲欧美国产中文色妇| 久久热九九这里只有精品| 91免费精品国自产拍偷拍| 精品国产丝袜一区二区| 九九热视频网在线观看| 精品一区二区三区乱码中文| 亚洲熟妇熟女久久精品| 91久久国产福利自产拍| 国产主播精品福利午夜二区| 国产精品尹人香蕉综合网| 国产精品成人一区二区三区夜夜夜| 国产成人精品资源在线观看| 欧美日韩国产福利在线观看| 国产大屁股喷水在线观看视频 | 欧美国产日本高清在线| 大香蕉精品视频一区二区| 激情偷拍一区二区三区视频 | 国产精品一区二区成人在线| 婷婷激情五月天丁香社区| 国产精品一区二区日韩新区| 欧美日韩三区在线观看| 精品人妻一区二区四区| 欧美日韩精品综合一区| 久久精品国产在热亚洲| 欧美日韩校园春色激情偷拍 | 国产又爽又猛又粗又色对黄| 欧美日韩有码一二三区| 久久精品一区二区少妇| 亚洲精品中文字幕一二三|