這是《JavaScript編程精解》簡明讀書心得 的下半部分。這本書的下半節有兩章客戶端JavaScript內容。由於這部分內容比較繁雜,而概念比較簡單,而且很多工作都可以用JQuery來完成,所以我讀這本書的時候,這部分就直接瀏覽了一遍。因此這下半部分只有兩節內容,模塊化和正則表達式。
模塊化的JavaScript
比起C#中可以輕松地在類中聲明私有字段、繼承類、封裝接口、生成命名空間,JavaScript簡直太可憐了——它不提供任何模塊化的原生支持。事實上,我們只能通過一些JavaScript的特性來“模擬”出模塊的特點。模塊的特點是“封裝”,即模塊的使用者可以對模塊內的細節一無所知,其目的就是希望當其他模塊發生變化時,自身可以不變,增強了代碼的復用性。在JavaScript中,這是通過以下兩個特性來完成的:
- 1、函數內部的局部變量對外不可見(閉包)
- 2、對象的字段只能通過對象調用(這好像又是廢話)
用函數封裝變量,使得不需要出現在全局作用域的變量不出現在全局作用域。比如我希望獲得一個計算圓柱體和圓錐體體積的函數,也許你會這樣做:
var PI = 3.1415926; var circleArea = function(theCircle) { return theCircle.radius*theCircle.radius*PI; } var cylinderVolume = function(theCylinder) { return circleArea(theCylinder.circle)*theCylinder.height; } var taperedVolume = function(theTapered) { return circleArea(theTapered.circle)*theTapered.height/3; }
瀏覽器解釋完這4條語句之后,你獲得了想要的兩個函數cylinderVolume和taperedVolume,但是請注意,你也獲得了PI和circleArea,這並不是你想做的,換句話說,全局作用域被PI和circleArea“污染”了。也許前面一個腳本文件里已經定義了另一個PI=1.414,或者只有你掌握了計算圓面積的方法circleArea,你不想其他人也能方便地使用(話說回來既然用了JavaScript而不是什么COM技術,不管如何扭曲代碼你的秘密總會被解析出來的,因為源碼就是公開的嘛),你都應該將這兩個家伙封裝起來。下面的實現可能會比較好:
function createFunction(){ var PI = 3.1415926; var circleArea = function(theCircle) { …… } var cylinderVolume = function(theCylinder) { …… } var taperedVolume = function(theTapered) { …… } window.cylinderVolume = cylinderVolume; window.taperedVolume = taperedVolume; } createFunction();
window是瀏覽器窗口對象,所有的“全局變量”都是window的屬性。這個函數運行之后,window對象只獲得了兩個計算圓柱體和圓錐體體積的函數,在后面代碼里可以方便地調用這兩個函數。你看,PI和circleArea並沒有隨着createFunction定義的結束而被釋放,它們被保存到一個由瀏覽器維護的地方,只要調用taperedVolume函數,就會使用到它們,這就是“閉包”的特性。
如果仔細考慮,上面的實現還有問題,那就是污染了變量createFunction,那么也許使用匿名函數會更好。下面是最好的實現:
function(){ var PI = 3.1415926; var circleArea = function(theCircle) { …… } var cylinderVolume = function(theCylinder) { …… } var taperedVolume = function(theTapered) { …… } window.cylinderVolume = cylinderVolume; window.taperedVolume = taperedVolume; }();
這樣,利用函數體內局部變量是不可達的這一特點,就完成了對模塊的封裝,cylinderVolume和taperedVolume就是對外交流的接口。 也許你會說,直接把PI值和計算圓面積的邏輯寫到計算體積的函數里不就行了?但是,當函數的規模足夠大的時候,你一定不會想那么做的。
至於利用對象的屬性封裝字段則很簡單了,因為對象(名值對)本身就是用來模擬面向對象語言中的“對象”的,通過對象名來訪問對象內部的屬性,這是理所當然的。
正則表達式
正則表達式提供了非常靈活的字符串匹配方法,可想而知在表單驗證之類的領域有着很強的應用。之前看犀牛書的時候一帶而過,這次看書雖然不指望完全掌握,但我總想還是稍微整理一下吧。
下面是正則表達式的一個簡單的應用,返回1表示能夠在索引(從0開始)為1的地方找到第一個匹配el。
var text = “hello”; text.search(/el/);//->1
正則表達式以一個斜杠開頭,以一個斜杠結尾,中間的內容為匹配的內容。正則表達式可以通過通配符匹配字符串:
“1a 2 3d”.search(/\s\d\s/); //->3 匹配到a后面的“空格2空格”
還有一些常用通配符:
通配符 | 釋意 |
\d | 任意數字 |
\w | 任意單詞 |
\s | 任意空字符:制表符、換行符、空格符 |
\b | 單詞邊界:標點符號,空格,字符串開頭和結束 |
. | 任意不是換行符的字符 |
正則表達式中,中括號"["和"]"之間的部分表示符合任意字符的匹配,星號和加號分別表示可以重復任意次數和重復至少一次:
“abccxa”.search(/[xyz]/); //->4 “abccxa”.search(/c*xa/); //->4
字符串除了search方法,還有replace方法用以替換掉被匹配的字符串,還有match方法檢測是否找到匹配。
寫在最后的話
花了兩天看完這本小書,有一些收獲都基本寫到了這兩篇博文里面。也許有人會問:既然JavaScript有這么多缺點,OOP是模擬出來的,事件機制也是模擬出來的,甚至調用函數的時候連參數都不檢查,那為什么還要學習它呢?我有這樣的答案:首先,JavaScript的本質是簡單的,在這種簡單的基礎之上構造抽象問題的思想卻是值得學習的(當突然理解“閉包”的時候,我覺得設計者的思路真是妙絕了);其次,JavaScript作為一種“立竿見影”的語言,能幫助我做想做的事情:開發游戲——WebGL官方demo里面有個賽車游戲,簡直太COOL了。