1. AJAX: 狀態駐留,異步更新
首先來看一點歷史。
A. 1995年Netscape公司的Brendan Eich開發了javacript語言,這是一種動態(dynamic)、弱類型(weakly typed)、基於原型(prototype-based)的腳本語言。
B. 1999年微軟IE5發佈,其中包含了XMLHTTP ActiveX控件。
C. 2001年微軟IE6發佈,部分支持DOM level 1和CSS 2標準。
D. 2002年Douglas Crockford發明JSON格式。
至此,可以說Web2.0所依賴的技術元素已經基本成形,但是並沒有立刻在整個業界產生重大的影響。儘管一些“頁面異步局部刷新”的技巧在程序員中間祕密的流傳,甚至催生了bindows這樣龐大臃腫的類庫,但總的來說,前端被看作是貧瘠而又骯髒的沼澤地,只有後臺技術纔是王道。到底還缺少些什麼呢?
當我們站在今天的角度去回顧2005年之前的js代碼,包括那些當時的牛人所寫的代碼,可以明顯的感受到它們在程序控制力上的孱弱。並不是說2005年之前的js技術本身存在問題,只是它們在概念層面上是一盤散沙,缺乏統一的觀念,或者說缺少自己獨特的風格, 自己的靈魂。當時大多數的人,大多數的技術都試圖在模擬傳統的面嚮對象語言,利用傳統的面向對象技術,去實現傳統的GUI模型的仿製品。
Ajax這一前端特有的概念迅速將衆多分散的實踐統一在同一口號之下,引發了Web編程範式的轉換。所謂名不正則言不順,這下無名羣衆可找到組織了。在未有Ajax之前,人們早已認識到了B/S架構的本質特徵在於瀏覽器和服務器的狀態空間是分離的,但是一般的解決方案都是隱藏這一區分,將前臺狀態同步到後臺,由後臺統一進行邏輯處理,例如ASP.NET。因爲缺乏成熟的設計模式支持前臺狀態駐留,在換頁的時候,已經裝載的js對象將被迫被丟棄,這樣誰還能指望它去完成什麼複雜的工作嗎?
Ajax明確提出界面是局部刷新的,前臺駐留了狀態,這就促成了一種需要:需要js對象在前臺存在更長的時間。這也就意味着需要將這些對象和功能有效的管理起來,意味着更復雜的代碼組織技術,意味着對模塊化,對公共代碼基的渴求。
jQuery現有的代碼中真正與Ajax相關(使用XMLHTTP控件異步訪問後臺返回數據)的部分其實很少,但是如果沒有Ajax, jQuery作爲公共代碼基也就缺乏存在的理由。
2. 模塊化:管理名字空間
當大量的代碼產生出來以後,我們所需要的最基礎的概念就是模塊化,也就是對工作進行分解和複用。工作得以分解的關鍵在於各人獨立工作的成果可以集成在一起。這意味着各個模塊必須基於一致的底層概念,可以實現交互,也就是說應該基於一套公共代碼基,屏蔽底層瀏覽器的不一致性,並實現統一的抽象層,例如統一的事件管理機制等。比統一代碼基更重要的是,各個模塊之間必須沒有名字衝突。否則,即使兩個模塊之間沒有任何交互,也無法共同工作。
jQuery目前鼓吹的主要賣點之一就是對名字空間的良好控制。這甚至比提供更多更完善的功能點都重要的多。良好的模塊化允許我們複用任何來源的代碼,所有人的工作得以積累疊加。而功能實現僅僅是一時的工作量的問題。jQuery使用module pattern的一個變種來減少對全局名字空間的影響,僅僅在window對象上增加了一個jQuery對象(也就是$函數)。
所謂的module pattern代碼如下,它的關鍵是利用匿名函數限制臨時變量的作用域。
js本身缺乏包結構,不過經過多年的嘗試之後業內已經逐漸統一了對包加載的認識,形成了RequireJs庫這樣得到一定共識的解決方案。jQuery可以與RequireJS庫良好的集成在一起, 實現更完善的模塊依賴管理。http://requirejs.org/docs/jquery.html
通過以下函數調用來定義模塊my/shirt, 它依賴於my/cart和my/inventory模塊,
3. 神奇的$:對象提升
當你第一眼看到$函數的時候,你想到了什麼?傳統的編程理論總是告訴我們函數命名應該準確,應該清晰無誤的表達作者的意圖,甚至聲稱長名字要優於短名字,因爲減少了出現歧義的可能性。但是,$是什麼?亂碼?它所傳遞的信息實在是太隱晦,太曖昧了。$是由prototype.js庫發明的,它真的是一個神奇的函數,因爲它可以將一個原始的DOM節點提升(enhance)爲一個具有複雜行爲的對象。在prototype.js最初的實現中,$函數的定義爲
這基本對應於如下公式
這絕不僅僅是提供了一個聰明的函數名稱縮寫,更重要的是在概念層面上建立了文本id與DOM element之間的一一對應。在未有$之前,id與對應的element之間的距離十分遙遠,一般要將element緩存到變量中,例如
但是使用$之後,卻隨處可見如下的寫法
id與element之間的距離似乎被消除了,可以非常緊密的交織在一起。
prototype.js後來擴展了$的含義,
這對應於公式
很遺憾,這一步prototype.js走偏了,這一做法很少有實用的價值。
真正將$發揚光大的是jQuery, 它的$對應於公式
這裏有三個增強
A. selector不再是單一的節點定位符,而是複雜的集合選擇符
B. 返回的元素不是原始的DOM節點,而是經過jQuery進一步增強的具有豐富行爲的對象,可以啓動複雜的函數調用鏈。
C. $返回的包裝對象被造型爲數組形式,將集合操作自然的整合到調用鏈中。
當然,以上僅僅是對神奇的$的一個過分簡化的描述,它的實際功能要複雜得多. 特別是有一個非常常用的直接構造功能
jQuery將根據傳入的html文本直接構造出一系列的DOM節點,並將其包裝爲jQuery對象. 這在某種程度上可以看作是對selector的擴展: html內容描述本身就是一種唯一指定。
$(function{})這一功能就實在是讓人有些無語了, 它表示當document.ready的時候調用此回調函數。真的,$是一個神奇的函數, 有任何問題,請$一下。
總結起來, $是從普通的DOM和文本描述世界到具有豐富對象行爲的jQuery世界的躍遷通道。跨過了這道門,就來到了理想國。
4. 無定形的參數:專注表達而不是約束
弱類型語言既然頭上頂着個”弱”字, 總難免讓人有些先天不足的感覺. 在程序中缺乏類型約束, 是否真的是一種重大的缺憾? 在傳統的強類型語言中, 函數參數的類型,個數等都是由編譯器負責檢查的約束條件, 但這些約束仍然是遠遠不夠的。一般應用程序中爲了加強約束, 總會增加大量防禦性代碼, 例如在C++中我們常用ASSERT, 而在java中也經常需要判斷參數值的範圍:
很顯然, 這些代碼將導致程序中存在大量無功能的執行路徑, 即我們做了大量判斷, 代碼執行到某個點, 系統拋出異常, 大喊此路不通. 如果我們換一個思路, 既然已經做了某種判斷,能否利用這些判斷的結果來做些什麼呢? javascript是一種弱類型的語言,它是無法自動約束參數類型的, 那如果順勢而行,進一步弱化參數的形態, 將”弱”推進到一種極致, 在弱無可弱的時候, weak會不會成爲標誌性的特點?
看一下jQuery中的事件綁定函數bind,
A. 一次綁定一個事件 $(“#my”).bind(“mouseover”, function(){});
B. 一次綁定多個事件 $(“#my”).bind(“mouseover mouseout”,function(){})
C. 換一個形式, 同樣綁定多個事件 $(“#my”).bind({mouseover:function(){}, mouseout:function(){});
D. 想給事件監聽器傳點參數 $(‘#my’).bind(‘click’, {foo: “xxxx”}, function(event) { event.data.foo..})
E. 想給事件監聽器分個組 $(“#my”).bind(“click.myGroup″, function(){});
F. 這個函數爲什麼還沒有瘋掉???
就算是類型不確定, 在固定位置上的參數的意義總要是確定的吧? 退一萬步來說, 就算是參數位置不重要了,函數本身的意義應該是確定的吧? 但這是什麼
一個函數怎麼可以這樣過分, 怎麼能根據傳入參數的類型和個數不同而行爲不同呢? 看不順眼是不是? 可這就是俺們的價值觀. 既然不能防止, 那就故意允許. 雖然形式多變, 卻無一句廢話. 缺少約束, 不妨礙表達(我不是出來嚇人的).