jQuery實戰(第2版)

jQuery實戰(第2版)

《圖靈程式設計叢書:jQuery實戰(第2版)》全面介紹jQuery知識,展示如何遍歷HTML文檔、處理事件、執行動畫、給網頁添加Ajax以及jQueryUI。書中緊緊地圍繞“用實際的示例來解釋每一個新概念”這一宗旨,生動描述了jQuery如何與其他工具和框架互動以及如何生成jQuery外掛程式。

基本介紹

  • 書名:jQuery實戰(第2版)
  • 作者:(美)Bear Bibeault  Yehuda Katz 
  • 原版名稱: jQuery in Action, Second Edition
  • 譯者: 三生石上
出版信息,目錄,前言,

出版信息

原書名: jQuery in Action, Second Edition
原出版社: Manning Publications
作者: (美)Bear Bibeault Yehuda Katz [作譯者介紹]
譯者: 三生石上
叢書名: 圖靈程式設計叢書
出版社:人民郵電出版社
ISBN:9787115274571
上架時間:2012-3-27
出版日期:2012 年3月
開本:16開
頁碼:394
版次:2-1
內容簡介
《javascript語言精髓與編程實踐(第2版)》主要的著述目的是基於一種形式上簡單的語言來講述“語言的本質及其套用”。本書詳細講述了通過框架執行過程來構造一個javascript 擴展框架的方法,並完整地講述了框架擴展中各種設計取捨,因此可以作為研究電腦程式設計語言時的參考,用以展示現實系統如何實現經典理論中的各種編程范型。

目錄

第1部分 語言基礎
第1章 十年javascript 3
1.1 網頁中的代碼 3
1.1.1 新鮮的玩意兒 3
1.1.2 第一段在網頁中的代碼 4
1.1.3 最初的價值 5
1.2 用javascript來寫瀏覽器上的套用 7
1.2.1 我要做一個聊天室 7
1.2.2 flash的一席之地 9
1.2.3 rwc與ria之爭 10
1.3 沒有框架與庫的語言能怎樣發展呢 12
1.3.1 做一個框架 12
1.3.2 重寫框架的語言層 15
1.3.3 富瀏覽器端開發與ajax 16
1.4 語言的進化 18
1.4.1 qomo的重生 18
1.4.2 qobean是對語言的重新組織 18
1.4.3 javascript作為一門語言的進化 19
1.5 為javascript正名 22
.1.5.1 javascript 22
1.5.2 core javascript 23
1.5.3 spidermonkey javascript 24
1.5.4 ecmascript 24
1.5.5 jscript 25
1.5.6 總述 25
1.6 javascript的套用環境 26
1.6.1 宿主環境 27
1.6.2 外殼程式 28
1.6.3 運行期環境 29
第2章 javascript的語法 31
2.1 語法綜述 31
2.1.1 標識符所綁定的語義 32
2.1.2 識別語法錯誤與運行錯誤 33
2.2 javascript的語法:變數聲明 33
2.2.1 變數的數據類型 34
2.2.1.1 基本數據類型 34
2.2.1.2 值類型與引用類型 35
2.2.2 變數聲明 36
2.2.3 變數與直接量 37
2.2.3.1 字元串直接量、轉義符 38
2.2.3.2 數值直接量 40
2.2.4 函式聲明 41
2.3 javascript的語法:表達式運算 42
2.3.1 一般表達式運算 43
2.3.2 邏輯運算 44
2.3.3 字元串運算 45
2.3.4 比較運算 46
2.3.4.1 等值檢測 46
2.3.4.2 序列檢測 48
2.3.5 賦值運算 50
2.3.6 函式調用 51
2.3.7 特殊作用的運算符 51
2.3.8 運算優先權 53
2.4 javascript的語法:語句 55
2.4.1 表達式語句 56
2.4.1.1 一般表達式語句 57
2.4.1.2 賦值語句與隱式的變數聲明 59
2.4.1.3 (顯式的)變數聲明語句 59
2.4.1.4 函式調用語句 61
2.4.2 分支語句 65
2.4.2.1 條件分支語句(if語句) 65
2.4.2.2 多重分支語句(switch語句) 66
2.4.3 循環語句 68
2.4.4 流程控制:一般子句 70
2.4.4.1 標籤聲明 70
2.4.4.2 break子句 71
2.4.4.3 continue子句 73
2.4.4.4 return子句 75
2.4.5 流程控制:異常 75
2.5 面向對象編程的語法概要 77
2.5.1 對象直接量聲明與實例創建 78
2.5.1.1 使用構造器創建對象實例 78
2.5.1.2 對象直接量聲明 81
2.5.1.3 數組直接量聲明 82
2.5.1.4 正則表達式直接量聲明 83
2.5.1.5 【es5】在對象直接量中使用屬性讀寫器 85
2.5.1.6 討論:初始器與直接量的區別 86
2.5.2 對象成員 87
2.5.2.1 對象成員列舉、存取和刪除 87
2.5.2.2 屬性存取與方法調用 91
2.5.2.3 對象及其成員的檢查 92
2.5.2.4 可列舉性 94
2.5.3 默認對象的指定 95
2.6 【es5】嚴格模式下的語法限制 96
2.6.1 語法限制 97
2.6.2 嚴格模式的範圍 99
2.7 運算符的二義性 100
2.7.1 加號“+”的二義性 102
2.7.2 括弧“( )”的二義性 103
2.7.3 冒號“:”與標籤的二義性 105
2.7.4 大括弧“{ }”的二義性 106
2.7.5 逗號“,”的二義性 109
2.7.6 方括弧“[ ]”的二義性 111
第2部分 語言特性及基本套用
第3章 javascript的非函式式語言特性 117
3.1 概述 117
3.1.1 命令式語言與結構化編程 118
3.1.2 結構化的疑難 120
3.1.3 “面向對象語言”是突破嗎 122
3.1.4 更高層次的抽象:接口 125
3.1.5 再論語言的分類 127
3.1.6 javascript的語源 129
3.2 基本語法的結構化含義 131
3.2.1 基本邏輯與代碼分塊 131
3.2.2 模組化的層次:語法作用域 134
3.2.2.1 主要的語法作用域及其效果 135
3.2.2.2 語法作用域之間的相關性 138
3.2.3 執行流程及其變更 139
3.2.3.1 級別2:“break ”等語法 140
3.2.3.2 級別3:return子句 143
3.2.3.3 級別4:throw語句 144
3.2.3.4 執行流程變更的內涵 145
3.2.4 模組化的效果:變數作用域 147
3.2.4.1 級別1:表達式 148
3.2.4.2 級別2:語句 149
3.2.4.3 級別3:函式(局部) 150
3.2.4.4 級別4:全局 151
3.2.4.5 變數作用域中的次序問題 153
3.2.4.6 變數作用域與變數的生存周期 154
3.2.5 語句的副作用 155
3.3 javascript中的原型繼承 157
3.3.1 空對象(null)與空的對象 158
3.3.2 原型繼承的基本性質 159
3.3.3 空的對象是所有對象的基礎 159
3.3.4 構造複製?寫時複製?還是讀遍歷? 160
3.3.5 構造過程:從函式到構造器 162
3.3.6 預定義屬性與方法 163
3.3.7 原型鏈的維護 165
3.3.7.1 兩個原型鏈 165
3.3.7.2 constructor屬性的維護 167
3.3.7.3 內部原型鏈的作用 170
3.3.7.4 【es5】在spidermonkey與es5中的原型鏈維護 170
3.3.8 原型繼承的實質 172
3.3.8.1 原型修改 172
3.3.8.2 原型繼承 173
3.3.8.3 原型繼承的實質:從無到有 174
3.3.8.4 如何理解“繼承來的成員” 175
3.4 javascript的對象系統 177
3.4.1 封裝 177
3.4.2 多態 179
3.4.3 事件 181
3.4.4 類抄寫?或原型繼承? 182
3.4.4.1 類抄寫 183
3.4.4.2 原型繼承存在的問題 186
3.4.4.3 如何選擇繼承的方式 186
3.4.5 javascript中的對象(構造器) 187
3.4.6 不能通過繼承得到的效果 190
3.5 【es5】可定製的對象屬性 192
3.5.1 屬性描述符 192
3.5.1.1 (一般的)數據屬性描述符 193
3.5.1.2 (帶讀寫器的)存取屬性描述符 193
3.5.1.3 直接量形式的初始器是語法格式,而非描述符 194
3.5.2 定製對象屬性 195
3.5.2.1 屬性聲明以及獲取屬性描述符 195
3.5.2.2 新的對象創建方法:object.create() 197
3.5.3 屬性狀態維護 198
3.5.3.1 取屬性列表 198
3.5.3.2 使用defineproperty來維護屬性的性質 199
3.5.3.3 對於繼承自原型的屬性,修改其值的效果 200
3.5.3.4 重寫原型繼承來的屬性的描述符 201
第4章 javascript的函式式語言特性 203
4.1 概述 203
4.1.1 從代碼風格說起 204
4.1.2 為什麼常見的語言不贊同連續求值 204
4.1.3 函式式語言的淵源 206
4.2 函式式語言中的函式 208
4.2.1 函式是運算元 208
4.2.2 在函式內保存數據 209
4.2.3 函式內的運算對函式外無副作用 210
4.3 從運算式語言到函式式語言 211
4.3.1 javascript中的幾種連續運算 212
4.3.1.1 連續賦值 212
4.3.1.2 三元表達式的連用 212
4.3.1.3 一些運算連用 214
4.3.1.4 函式與方法的調用 214
4.3.2 運算式語言 216
4.3.2.1 運算的實質是值運算 216
4.3.2.2 有趣的運算:在ie和j2ee中 218
4.3.3 如何消滅掉語句 220
4.3.3.1 通過表達式消滅分支語句 221
4.3.3.2 通過函式遞歸消滅循環語句 222
4.3.3.3 其他可以被消滅的語句 223
4.4 函式:對運算式語言的補充和組織 224
4.4.1 函式是必要的補充 224
4.4.2 函式是代碼的組織形式 226
4.4.3 重新認識“函式” 227
4.4.3.1 “函式”==“lambda” 228
4.4.3.2 當運算符等義於某個函式時 228
4.4.4 javascript語言中的函式式編程 230
4.5 javascript中的函式 231
4.5.1 可變參數與值參數傳遞 231
4.5.2 非惰性求值 235
4.5.3 函式是第一型 237
4.5.4 函式是一個值 239
4.5.5 可遍歷的調用棧 239
4.5.5.1 callee:我是誰 240
4.5.5.2 caller:誰呼(叫)我 242
4.6 閉包 244
4.6.1 閉包與函式實例 244
4.6.1.1 什麼是閉包 245
4.6.1.2 什麼是函式實例與函式引用 246
4.6.1.3 (在被調用時,)每個函式實例至少擁有一個閉包 248
4.6.2 閉包與調用對象 250
4.6.2.1 “調用對象”的局部變數維護規則 252
4.6.2.2 “全局對象”的變數維護規則 252
4.6.2.3 函式閉包與“調用對象”的生存周期 253
4.6.3 閉包相關的一些特性 255
4.6.3.1 引用與泄漏 256
4.6.3.2 函式實例擁有多個閉包的情況 258
4.6.3.3 語句或語句塊中的閉包問題 260
4.6.3.4 閉包中的標識符(變數)特例 262
4.6.3.5 函式對象的閉包及其效果 265
4.6.4 閉包與可見性 266
4.6.4.1 函式閉包帶來的可見性效果 266
4.6.4.2 對象閉包帶來的可見性效果 269
4.6.4.3 匿名函式的閉包與可見性效果 273
4.7 【es5】嚴格模式與閉包 274
4.7.1 嚴格模式下的執行限制 275
4.7.2 嚴格模式下的匿名函式遞歸問題 276
第5章 javascript的動態語言特性 279
5.1 概述 279
5.1.1 動態數據類型的起源 280
5.1.2 動態執行系統的起源 280
5.1.2.1 編譯系統、解釋系統與編碼 280
5.1.2.2 動態執行 281
5.1.3 腳本系統的起源 282
5.1.4 腳本只是一種表面的表現形式 283
5.2 動態執行 285
5.2.1 動態執行與閉包 285
5.2.1.1 eval使用全局閉包 286
5.2.1.2 eval使用當前函式的閉包 287
5.2.2 動態執行過程中的語句、表達式與值 289
5.2.3 奇特的、甚至是負面的影響 291
5.3 動態方法調用(call、apply與bind) 293
5.3.1 動態方法調用中指定this對象 293
5.3.2 丟失的this引用 295
5.3.3 棧的可見與修改 296
5.3.4 兼容性:低版本中的call()與apply() 298
5.3.5 【es5】兼容性:es5中的call()、apply() 301
5.3.6 【es5】bind()方法與函式的延遲調用 302
5.4 重寫 303
5.4.1 原型重寫 304
5.4.2 構造器重寫 305
5.4.2.1 語法聲明與語句含義不一致的問題 307
5.4.2.2 對象檢測的麻煩 310
5.4.2.3 構造器的原型(prototype屬性)不受重寫影響 311
5.4.2.4 “內部對象系統”不受影響 312
5.4.2.5 讓用戶對象系統影響內部對象系統 313
5.4.2.6 構造器重寫對直接量聲明的影響 314
5.4.2.7 構造綁定 315
5.4.2.8 內置構造器重寫的概述 317
5.4.3 對象成員的重寫 318
5.4.3.1 成員重寫的檢測 318
5.4.3.2 成員重寫的刪除 319
5.4.4 宿主對重寫的限制 321
5.4.5 引擎對重寫的限制 323
5.4.5.1 this的重寫 323
5.4.5.2 語句語法中的重寫 324
5.4.5.3 結構化異常處理中的重寫 326
5.5 包裝類:面向對象的妥協 327
5.5.1 顯式包裝元數據 328
5.5.2 隱式包裝的過程與檢測方法 329
5.5.3 包裝值類型數據的必要性與問題 332
5.5.4 其他直接量與相應的構造器 333
5.5.4.1 函式特例 333
5.5.4.2 正則表達式特例 334
5.6 關聯數組:對象與數組的動態特性 335
5.6.1 關聯數組是對象系統的基礎 336
5.6.2 用關聯數組實現的索引數組 336
5.6.3 乾淨的對象 339
5.7 類型轉換 342
5.7.1 宿主環境下的特殊類型系統 343
5.7.2 值運算:類型轉換的基礎 345
5.7.3 隱式轉換 346
5.7.3.1 運算導致的類型轉換 346
5.7.3.2 語句(語義)導致的類型轉換 348
5.7.4 值類型之間的轉換 348
5.7.4.1 undefined的轉換 349
5.7.4.2 number的轉換 349
5.7.4.3 boolean的轉換 350
5.7.4.4 string的轉換 351
5.7.4.5 值類型數據的顯式轉換 351
5.7.5 從引用到值:深入探究valueof()方法 353
5.7.6 到字元串類型的顯式轉換 355
5.7.6.1 重寫tostring()方法 356
5.7.6.2 從數值到字元串的顯式轉換 357
5.7.6.3 其他類型的顯式轉換 358
5.7.6.4 序列化 358
第3部分 編程實踐
第6章 元語言:qobean核心技術與實現 363
6.1 qobean語言層的基本特性 363
6.1.1 qobean語言層概要 363
6.1.1.1 如何使用qobean 364
6.1.1.2 qobean中的面向對象(oop) 365
6.1.1.3 qobean中的接口(interface) 367
6.1.1.4 qobean中的切面(aspect) 369
6.1.2 qomo的體系結構及其與qobean的關係 373
6.2 qobean的元語言特性 374
6.2.1 qobean如何理解元語言 374
6.2.2 算法與數據結構 375
6.2.2.1 引用類型與值類型的數據 376
6.2.2.2 函式調用 376
6.2.2.3 源起 377
6.2.2.4 小結 377
6.2.3 代碼組織形式 379
6.2.3.1 塊,以及基於塊的編織 379
6.2.3.2 更強的編織 381
6.2.3.3 邏輯代碼塊:局部、全局,以及閉包 382
6.2.3.4 邏輯的屬主 384
6.2.4 對“如何組織對象”的補充 385
6.2.4.1 原子,與原子聯結的友類、友函式 386
6.2.4.2 對象唯一化 387
6.2.5 綜述 390
6.3 基於元語言實現的語言特性 391
6.3.1 基於元語言的類繼承框架 391
6.3.1.1 類註冊過程 392
6.3.1.2 示例:實現metaclass與metaobject的約定 393
6.3.1.3 完整的qomo語法實現 396
6.3.1.4 類類型樹的建立 400
6.3.2 多投事件 401
6.3.3 其他語言特性的實現 403
6.4 基於元語言實現的dsl 405
6.4.1 dsl的基本設計 405
6.4.2 dsl的基本實現 406
6.4.3 dsl的基本套用 409
6.4.4 一些修補 410
6.4.5 基於嚴格模式的一些修補 412
第7章 一般性的動態函式式語言技巧 415
7.1 消除代碼的全局變數名占用 415
7.2 一次性的構造器 417
7.3 對象充當識別器 418
7.4 識別new運算進行的構造器調用 420
7.5 使用直接量及其包裝類快速調用對象方法 421
7.6 三天前是星期幾 422
7.7 使用對象的值含義來構造複雜對象 423
7.8 控制字元串替換過程的基本模式 426
7.9 實現二叉樹 427
7.10 將函式封裝為方法 429
7.11 使用with語句來替代函式參數傳遞 431
7.12 使用對象閉包來重置重寫 432
7.13 構造函式參數 434
7.14 使用更複雜的表達式來消減if語句 437
7.15 利用鉤子函式來擴展功能 439
7.16 安全的字元串 441
附錄a 術語表 443
附錄b 主要引擎的特性差異列表 447
附錄c 附圖 449
附錄d 參考書目 453
附錄e 本書各版次主要修改 455

前言

語言是一種交流的工具,這約定了語言的“工具”本質,以及“交流”的功用。“工具”的選擇只在於“功用”是否能達到,而不在於工具是什麼。
在數千年之前,遠古祭師手中的神杖就是他們與神交流的工具。祭師讓世人相信他們敬畏的是神,而世人只需要相信那柄神杖。於是,假如祭師不小心丟掉了神杖,就可以堂而皇之地再做一根。甚至,他們可以隨時將舊的換成更新或更舊的神杖,只要他們宣稱這是一根更有利於通神的杖。對此,世人往往做出迷惑的表情,或者呈現歡欣鼓舞的情狀。今天,這種表情或情狀一樣地出現在大多數程式設計師的臉上,出現在他們聽聞到新計算機語言被創生的時刻。
神杖換了,祭師還是祭師,世人還是會把頭叩得山響。祭師掌握了與神交流的方法(如果真如同他們自己說的那樣),而世人只看見了神杖。
所以,泛義的工具是文明的基礎,而確指的工具卻是愚人的器物。
計算機語言有很多種分類方法,例如高級語言或者低級語言。其中一種分類方法,就是將計算機語言分為“靜態語言”和“動態語言”——事物就是如此,如果用一對絕對反義的詞來分類,就相當於涵蓋了事物的全體。當然,按照中國人中庸平和的觀點,以及保守人士對未知可能性的假設,我們還可以設定一種中間態:半動態語言。你當然也可以叫它半靜態語言。
所以,我們現在是在討論一種很泛義的計算機語言工具。至少在眼下,它(在分類概念中)涵蓋了計算機語言的二分之一。當然,限於我自身的能力,我只能討論一種確指的工具,例如JavaScript。但我希望你由此看到的是計算機編程方法的基礎,而不是某種愚人的器物。JavaScript 的生命力可能足夠頑強,我假定它比C 語言還頑強,甚至比你我的生命都頑強。但它只是愚人的器物,因此反過來說:它能不能長久地存在並不重要,重要的是它能不能作為這“二分之一的泛義”來供我們討論。
分類法
打開一副新撲克牌,我們總看到它被整齊地排在那裡,從A 到K 及大小王。接下來,我們將它一分為二,然後交叉在一起;再分開,再交叉……但是在重新開局之前,你是否注意到:在上述過程中,牌局的複雜性其實不是由“分開”這個動作導致的,而是由“交叉”這個動作導致的。
所以分類法本身並不會導致複雜性。就如同一副新牌只有4 套A~K,我們可以按13種牌面來分類,也可以按4 種花色來分類。當你從牌盒裡把它們拿出來的時候,無論它們是以哪種方式分類的,這副牌都不混亂。混亂的起因,在於你交叉了這些分類。
同樣的道理,如果世界上只有動態、靜態兩種語言,或者真有半動態語言而你又有明確的“分類法”,那么開發人員將會迎來清醒、明朗的每一天:我們再也不需要花更多的時間去學習更多的古怪語言了。
然而,第一個問題便來自於分類本身。因為“非此即彼”的分類必然導致特性的缺失——如果沒有這樣“非此即彼”的標準就不可能形成分類,但特性的缺失又正是開發人員所不能容忍的。
我們一方面吃著碗裡的,一方面念著鍋里的。即使鍋里漂起來的那片菜葉未見得有碗裡的肉好吃,我們也一定要撈起來嘗嘗。而且大多數時候,由於我們吃肉吃膩了嘴,因此會覺得那片菜葉味道更好。所以,是我們的個性決定了我們做不成絕對的素食者或肉食者。
當然,更有一些人說我們的確需要一個新的東西來使我們更加強健。但不幸的是,大多數提出這種需求的人,都在尋求純質銀彈或混合毒劑。無論如何,他們要么相信總有一種事物是完美武器,要么相信更多的特性放在一起就變成了魔力的來源。
我不偏向兩種方法之任一。但是我顯然看到了這樣的結果,前者是我們在不斷地創造並特化某種特性,後者是我們在不斷地混合種種特性。
更進一步地說,前者在產生新的分類法以試圖讓武器變得完美,後者則通過混淆不同的分類法,以期望通過突變而產生奇蹟。二者相同之處,在於都需要更多的分類法。
函式式語言就是來源於另外的一種分類法。不過要說明的是,這種分類法是計算機語言的原理之一。基本上來說,這種分類法在電子計算機的實體出現以前就已經誕生了。這種分類法的基礎是“運算產生結果,還是運算影響結果”。前一種思想產生了函式式語言(如LISP)所在的“說明式語言”這一分類,後者則產生了我們現在常見的C、C++等語言所在的“命令式語言”這一分類。
然而我們已經說過,人們需要更多的分類的目的,是要么找到類似銀彈的完美武器,要么找到混合毒劑。所以一方面很多人宣稱“函式式是語言的未來”,另一方面也有很多人把這種分類法與其他分類法混在一起,於是變成了我們這本書所要講述的“動態函式式語言”。毋庸置疑的是:還會有更多的混合法產生。因為保羅? 格雷厄姆(PaulGraham)已經做過這樣的總結:“二十年來,開發新程式語言的一個流行的秘訣是:取C 語言的計算模式,逐漸地往上加LISP 模式的特性,例如運行時類型和無用單元收集。”
然而,這畢竟只是“創生一種新語言”的魔法。那么,到底有沒有讓我們在這浩如煙海的語言家族中,找到學習方法的魔法呢?
我的答案是:看清語言的本質,而不是試圖學會一門語言。當然,這看起來非常概念化。甚至有人說我可能是從某本教材中抄來的,另外一些人又說我試圖在這本書里宣講類似於我那本《大道至簡》里的老莊學說。
. 其實這很冤枉。我想表達的意思不過是:如果你想把一副牌理順,最好的法子,是回到它的分類法上,要么從A 到K 整理,要么按4 個花色整理。畢竟,兩種或更多種分類法作用於同一事物,只會使事物混淆而不是弄得更清楚。
因此,本書從語言特性出發,把動態與靜態、函式式與非函式式的語言特性分列出來。先講述每種特性,然後再討論如何去使用(例如交叉)它們。
特性
無論哪種語言(或其他工具)都有其獨特的特性,以及借鑑自其他語言的特性。有些語言通體沒有“獨特特性”,只是另外一種語言的副本,這更多的時候是為了“滿足一些人使用語言的習慣”。還有一些語言則基本上全是獨特的特性,這可能導致語言本身不實用,但卻是其他語言的思想庫。
我們已經討論過這一切的來源。
對於 JavaScript 來說,除了動態語言的基本特性之外,它還有著與其創生時代背景密切相關的一些語言特性。直到昨天,JavaScript 的創建者還在小心翼翼地增補著它的語言特性。JavaScript 輕量的、簡潔的、直指語言本質的特性集設計,使它成為解剖動態語言的有效工具。這個特性集包括:
? 一套參考過程式語言慣例的語法。
? 一套以原型繼承為基礎的對象系統。
? 一套支持自動轉換的弱類型系統。
? 動態語言與函式式語言的基本特性。
需要強調的是,JavaScript 1.x 非常苛刻地保證這些特性是相應語言領域中的最小特性集(或稱之為“語言原子”),這些特性在JavaScript 中相互混合,通過交錯與補充而構成了豐富的、屬於JavaScript 自身的語言特性。
本書的主要目的之一,就是分解出這些語言原子,並探究重新將它們混合在一起的過程與方法。通過從複雜性到單一語言特性的還原過程,讓讀者了解到語言的本質,以及“層出不窮的語言特性”背後的真相。
技巧
技巧是“技術的取巧之處”,所以根本上來說,技巧也是技術的一部分。很多人(也包括我)反對技巧的使用,是因為難以控制,並且容易破壞代碼的可讀性。
哪種情況下代碼是需要“易於控制”和“可讀性強”呢?通常,我們認為在較大型的工程下需要“更好地控制代碼”;在更多人共同開發的項目代碼上要求“更好的可讀性”。然而,反過來說,在一些更小型的、不需要更多人參與的項目中,“適度地”使用技巧是否就可以接受呢?
這取決於“需要、能夠”維護這個代碼的人對技巧的理解。這包括:
技巧是一種語言特性,還是僅特定版本所支持或根本就是BUG?
? 技巧是不是唯一可行的選擇,有沒有不需要技巧的實現?
? 技巧是為了實現功能,還是為了表現技巧而出現在代碼中的?
即使知曉問題的答案,我仍然希望每一個技巧的使用都有說明,甚至示例。如果維護代碼的人不能理解該技巧,那么連代碼本身都失去了價值,更何論技巧存在於這份代碼中的意義呢?
所以,雖然本書中的例子的確要用到許多“技巧”,但我一方面希望讀者能明白,這是語言核心或框架核心實現過程中必需的,另一方面也希望讀者能從這些技巧中學習到它原本的技術和理論,以及活用的方法。
然而對於很多人來說,本書在講述一個完全不同的語言類型。在這種類型的語言中,本書所講述的一切,都只不過是“正常的方法”;在其他類型的一些語言中,這些方法看起來就成了技巧。例如,在JavaScript 中要改變一個對象方法指向的代碼非常容易,並且是語言本身賦予的能力;而在Delphi/C++中,卻成了“破壞面向對象設計”的非正常手段。
所以你最好能換一個角度來看待本書中講述的“方法”。無論它對你產生多大的衝擊,你應該先想到的是這些方法的價值,而不是它對於“你所認為的傳統”的挑戰。事實上,這些方法,在另一些“同樣傳統”的語言類型中,已經存在了足夠長的時間——如同“方法”之於“對象”一樣,原本就是那樣“(至少看起來)自然而然”地存在於它所在的語言體系之中。
語言特性的價值依賴於環境而得以彰顯。橫行的螃蟹看起來古怪,但據說那是為了適應一次地磁反轉。螃蟹的成功在於適應了一次反轉,失敗(我們是說導致它這樣難看)之處,也在於未能又一次反轉回來。
這本書
你當然可以置疑:為什麼要有這樣的一本書?是的,這的確是一個很好的問題。
首先,這本書並不講 Web 瀏覽器(Web Browser,例如Internet Explorer)。這可能令人沮喪,但的確如此。儘管在很多人看來,JavaScript 就是為瀏覽器而準備的一種輕量的語言,並認為它離開了DOM、HTML、CSS 就沒有意義。在同樣的“看法”之下,國內外的書籍在談及JavaScript 時,大多會從“如何在Web 頁面上驗證一個輸入框值的有效性”講起。
是的,最初我也是這樣認為的。因為本書原來就是出自我在寫《B 端開發》一書的過程之中。《B 端開發》是一本講述“在瀏覽器(Browser)上如何用JavaScript 開發”的書。然而,《B 端開發》寫到近百頁就放下了,因為我覺得應該寫一本專門講JavaScript的書,這更重要。
所以,現在你將要看到的這本書就與瀏覽器無關。在本書中我會把JavaScript 提升到與Java、C#或Delphi 一樣的高度,來講述它的語言實現與擴展。作為實踐,本書還在最後一部分內容中,藉助名為“QoBean”的元語言框架討論了語言擴展的方法7。但是,總的來說,本書不講瀏覽器,不講Web,也並不講“通常概念下的”AJAX。
JavaScript 是一門語言,有思想的、有內涵的、有靈魂的語言。如果你沒意識到這一點,那么你可能永遠都只能拿它來做那個“驗證一個輸入框值的有效性”的代碼。本書講述JavaScript 的這些思想、核心、靈魂,以及如何去豐富它的血肉。最為核心的內容是在第2 章至第6 章,包括:
? 以命令式為主的一般化的JavaScript 語言特性,以及其對象系統。
? 動態、函式式語言,以及其他語言特性在JavaScript 中的表現與套用。
? 使用動態函式式特性來擴展JavaScript 的特性與框架。
在撰述這些內容的整個過程中,我一直在試圖給這本書找到一個適合的讀者群體,但我發現很難。因為通常的定義是低級、中級與高級,然而不同的用戶對自己的“等級”的定義標準並不一樣。在這其中,有“十年學會編程”的謙謹者,也有“三天學會某某語言”的速成家。所以,我認為這樣定位讀者的方式是徒勞的。
如果你想知道自己是否適合讀這本書,建議你先看一下目錄,然後試讀一些章節。你可以先選讀一些在你的知識庫中看來很新鮮的,以及一些你原本已經非常了解的內容。通過對比,你應該知道這本書會給你帶來什麼。
不過我需要強調一些東西。這本書不是一本讓你“學會某某語言”的書,也不是一本讓初學者“學會編程”的書。閱讀本書,你至少應該有一點編程經驗(例如半年至一年),而且要擯棄某些偏見(例如C 語言天下無敵或JavaScript 是新手玩具)。
最後,你至少要有一點耐心與時間。
序言
《世界需要一種什麼樣的語言》節選
什麼才是決定語言的未來的思想呢?或者我們也可以換個角度來提出這個問題:世界需要一種什麼樣的語言?
特性眾多、適應性強,就是將來語言的特點嗎?我們知道現在的C#與Java 都在這條道路上前進。與特定的系統相關,就是語言的出路嗎?例如曾經的VC++,以及它面向不同平台的版本。當然,與此類似的語言,還有C,以及彙編語言等。
這些例舉其實都是在特定環境下的特定語言,所不同的無非是此處的環境的大小。這其實也是程式設計師的心病:我們到底選Windows 平台,還是Java 平台,或者Linux 系統,再或者是……我們總是在不同的廠商及其支持的平台中選擇,而最終這種選擇又決定了我們所使用的語言。這與喜好無關,也與語言的好壞無關,不過是一種趨利的選擇罷了。所以你在使用著的也許只是一種“並不那么‘好’”,以及並不能令你那么開心地編程的語言。你越發辛勤地工作,越發地為這些語言搖旗鼓譟,你也就離語言的真相越來越遠。
當然,這不過是一種假設。但是,真相不都是從假設開始的嗎?
語言有些很純粹,有些則以混雜著稱。如果編程世界只有一種語言,無論它何等複雜,也必因毫無比較而顯得足夠純粹。所以只有在多種語言之間比較,才會有純粹或混雜的差異:純粹與混雜總是以一種或多種分類法為背景來描述的。
因此我們了解這些類屬概念的標準、原則,也就回溯到了種種語言的本質:它是什麼、怎么樣,以及如何工作。這本書,將這些分類回溯到兩種極端的對立:命令式與說明式、動態與靜態。我講述除了靜態語言(一般是指類似C、C++、Delphi 等的強類型、靜態、編譯型語言)之外的其他三種類型。正是從根底里具有這三種類型的特性,所以JavaScript 具有令人相當困擾的混合語言特性。分離它們,並揭示將它們混沌一物的方法與過程,如歷經涅磐。在這一經歷中,這本書就是我的所得。
多年以來,我在我所看不見的黑暗與看得見的夢境中追尋著答案。這本書是我最終的結論,或者結論面前的最後一層表象:我們需要從純化的語言中領悟到編程的本質,並以混雜的語言來創造我們的世界。我看到:局部的、純化的語言可能帶來獨特的性質,而從全局來看,世界是因為混雜而變得有聲有色。如果上帝不說“要有光”,那么我們將不能了解世象之表;而世象有了表面,便有了混雜的色彩,我們便看不見光之外的一切事物。我們依賴於光明,而事實是光明遮住了黑暗。如同你現在正在使用的那一種、兩種或更多種語言,阻礙了你看到你的未來。
周愛民
2009 年1 月於本書精簡版序
第 1 版 代序
學兩種語言
《我的程式語言實踐》節選
《程式設計語言——實踐之路》一書對“語言”有一個分類法,將語言分類為“說明式”與“命令式”兩種。Delphi 以及C、C++、Java、C#等都被分在“命令式”語言范型的範疇,“函式式”語言則是“說明式”范型中的一種。如今我回顧自己對語言的學習,其實十年也就學會了兩種語言:一種是命令式的Pascal/Delphi,另一種則是說明式的JavaScript。當然,從語言的實現方式來看,一種是靜態的,一種是動態的。
這便是我程式設計師生涯的全部了。
我畢竟不是計算機科學的研究者,而只是其套用的實踐者,因此我從一開始就缺乏對“程式”的某些科學的或學術層面上的認識是很正常的。也許有些人一開始就認識到程式便是如此,或者一種語言就應當是這樣構成和實現的,那么他可能是從計算機科學走向套用,故而比我了解得多些。而我,大概在十年前學習編程以及在後來很多年的實踐中,僅被要求“寫出代碼”而從未被要求了解“什麼是語言”。所以我才會後知後覺,才會在很長的時間裡迷失於那些精細的、溝壑縱橫的語言表面而不自知。然而一如我現在所見到的,與我曾相同地行進於那些溝壑的朋友,仍然在持續地迷惑著、盲目著,全然無覺於溝壑之外的瑰麗與宏偉。
前些天寫過一篇部落格,是推薦那篇“十年學會編程”的。那篇文章道出了我在十年編程實踐之後,對程式語言的最深刻的感悟。我們學習語言其實不必太多,深入一兩種就可以了。如果在一種類型的語言上翻來覆去,例如不斷地學C、Delphi、Java、C#……無非是求生存、討生活,或者用以裝點個人簡歷,於編程能力的提高用處是不大的。更多的人,因為面臨太多的語言選擇而淺嘗輒止,多年之後仍遠離程式根本,成為書寫代碼的機器,把書寫代碼的行數、程式個數或編程年限作為簡歷中最顯要的成果。這在明眼人看來,不過是熟練的砌磚工而已。
我在《大道至簡》中說“如今我已經不再專注於語言”。其實在說完這句話之後,我就已經開始了對JavaScript 的深入研究。在如此深入地研究一種語言,進而與另一種全然有別的語言比較之後,我對“程式=算法+結構”有了更深刻的理解與認識。儘管這句名言從來未因我的認識而變化過,從來未因說明與命令的編程方式而變化過,也從來未因動態與靜態的實現方法而變化過。
動靜之間,不變的是本質。我之所以寫這篇文章,並非想說明這種本質是什麼抑或如何得到,只是期望讀者能在匆忙的行走中,時而停下腳步,遠遠地觀望一下目標罷了。而我此刻,正在做一個駐足觀望的路人。
周愛民
2007 年11 月於個人部落格

相關詞條

熱門詞條

聯絡我們