jQuery中的編程範式(下)
10. extend: 繼承不是必須的
js是基於原型的語言, 並沒有內置的繼承機制, 這一直讓很多深受傳統面向對象教育的同學們耿耿於懷。但繼承一定是必須的嗎? 它到底能夠給我們帶來什麼? 最純樸的回答是: 代碼重用。那麼, 我們首先來分析一下繼承作爲代碼重用手段的潛力。
曾經有個概念叫做”多重繼承”, 它是繼承概念的超級賽亞人版, 很遺憾後來被診斷爲存在着先天缺陷, 以致於出現了一種對於繼承概念的解讀: 繼承就是”is a”關係, 一個派生對象”is a”很多基類, 必然會出現精神分裂, 所以多重繼承是不好的。
如果D類從A,B兩個基類繼承, 而A和B類中都實現了同一個函數f, 那麼D類中的f到底是A中的f還是B中的f, 抑或是A中的f+B中的f呢? 這一困境的出現實際上源於D的基類A和B是並列關係, 它們滿足交換律和結合律, 畢竟,在概念層面上我們可能難以認可兩個任意概念之間會出現從屬關係。但如果我們放鬆一些概念層面的要求, 更多的從操作層面考慮一下代碼重用問題, 可以簡單的認爲B在A的基礎上進行操作, 那麼就可以得到一個線性化的結果。也就是說, 放棄A和B之間的交換律只保留結合律, extends A, B 與 extends B,A 會是兩個不同的結果, 不再存在詮釋上的二義性。scala語言中的所謂trait(特性)機制實際上採用的就是這一策略。
面向對象技術發明很久之後, 出現了所謂的面向方面編程(AOP), 它與OOP不同, 是代碼結構空間中的定位與修改技術。AOP的眼中只有類與方法, 不知道什麼叫做意義。AOP也提供了一種類似多重繼承的代碼重用手段, 那就是mixin。對象被看作是可以被打開,然後任意修改的Map, 一組成員變量與方法就被直接注射到對象體內, 直接改變了它的行爲。
prototype.js庫引入了extend函數,
就是Map之間的一個覆蓋運算, 但很管用, 在jQuery庫中也得到了延用. 這個操作類似於mixin, 在jQuery中是代碼重用的主要技術手段—沒有繼承也沒什麼大不了的。
11. 名稱映射: 一切都是數據
代碼好不好, 循環判斷必須少。循環和判斷語句是程序的基本組成部分, 但是優良的代碼庫中卻往往找不到它們的蹤影, 因爲這些語句的交織會模糊系統的邏輯主線, 使我們的思想迷失在疲於奔命的代碼追蹤中。jQuery本身通過each, extend等函數已經極大減少了對循環語句的需求, 對於判斷語句, 則主要是通過映射表來處理. 例如, jQuery的val()函數需要針對不同標籤進行不同的處理, 因此定義一個以tagName爲key的函數映射表。
這樣在程序中就不需要到處寫
可以統一處理
映射表將函數作爲普通數據來管理, 在動態語言中有着廣泛的應用。特別是, 對象本身就是函數和變量的容器, 可以被看作是映射表。jQuery中大量使用的一個技巧就是利用名稱映射來動態生成代碼, 形成一種類似模板的機制。例如爲了實現myWidth和myHeight兩個非常類似的函數, 我們不需要。
而可以選擇動態生成
12. 插件機制:其實我很簡單
jQuery所謂的插件其實就是$.fn上增加的函數, 那這個fn是什麼東西?
顯然, $.fn其實就是jQuery.prototype的簡寫。
無狀態的插件僅僅就是一個函數, 非常簡單。
對於比較複雜的插件開發, jQuery UI提供了一個widget工廠機制,
調用 $(‘#dlg’).dialog(options)時, 實際執行的代碼基本如下所示:
可以看出, 第一次調用$(‘#dlg’).dialog()函數時會創建窗口對象實例,並保存在data中, 此時會調用_create()和_init()函數, 而如果不是第一次調用, 則是在已經存在的對象實例上調用_init()方法. 多次調用$(‘#dlg’).dialog()並不會創建多個實例。
13. browser sniffer vs. feature detection
瀏覽器嗅探(browser sniffer)曾經是很流行的技術, 比如早期的jQuery中
在具體代碼中可以針對不同的瀏覽器作出不同的處理
但是隨着瀏覽器市場的競爭升級, 競爭對手之間的互相模仿和僞裝導致userAgent一片混亂, 加上Chrome的誕生, Safari的崛起, IE也開始加速向標準靠攏, sniffer已經起不到積極的作用。特性檢測(feature detection)作爲更細粒度, 更具體的檢測手段, 逐漸成爲處理瀏覽器兼容性的主流方式。
只基於實際看見的,而不是曾經知道的, 這樣更容易做到兼容未來.
14. Prototype vs. jQuery
prototype.js是一個立意高遠的庫, 它的目標是提供一種新的使用體驗,參照Ruby從語言級別對javascript進行改造,並最終真的極大改變了js的面貌。$, extends, each, bind…這些耳熟能詳的概念都是prototype.js引入到js領域的。它肆無忌憚的在window全局名字空間中增加各種概念, 大有誰先佔坑誰有理, 捨我其誰的氣勢。而jQuery則扣扣索索, 抱着比較實用化的理念, 目標僅僅是write less, do more而已。
不過等待激進的理想主義者的命運往往都是壯志未酬身先死。當prototype.js標誌性的bind函數等被吸收到ECMAScript標準中時, 便註定了它的沒落。到處修改原生對象的prototype, 這是prototype.js的獨門祕技, 也是它的死穴。特別是當它試圖模仿jQuery, 通過Element.extend(element)返回增強對象的時候, 算是徹底被jQuery給帶到溝裏去了。prototype.js與jQuery不同, 它總是直接修改原生對象的prototype, 而瀏覽器卻是充滿bug, 謊言, 歷史包袱並夾雜着商業陰謀的領域, 在原生對象層面解決問題註定是一場悲劇。性能問題, 名字衝突, 兼容性問題等等都是一個幫助庫的能力所無法解決的。Prototype.js的2.0版本據說要做大的變革, 不知是要與歷史決裂, 放棄兼容性, 還是繼續掙扎, 在夾縫中求生。
完!
js是基於原型的語言, 並沒有內置的繼承機制, 這一直讓很多深受傳統面向對象教育的同學們耿耿於懷。但繼承一定是必須的嗎? 它到底能夠給我們帶來什麼? 最純樸的回答是: 代碼重用。那麼, 我們首先來分析一下繼承作爲代碼重用手段的潛力。
曾經有個概念叫做”多重繼承”, 它是繼承概念的超級賽亞人版, 很遺憾後來被診斷爲存在着先天缺陷, 以致於出現了一種對於繼承概念的解讀: 繼承就是”is a”關係, 一個派生對象”is a”很多基類, 必然會出現精神分裂, 所以多重繼承是不好的。
如果D類從A,B兩個基類繼承, 而A和B類中都實現了同一個函數f, 那麼D類中的f到底是A中的f還是B中的f, 抑或是A中的f+B中的f呢? 這一困境的出現實際上源於D的基類A和B是並列關係, 它們滿足交換律和結合律, 畢竟,在概念層面上我們可能難以認可兩個任意概念之間會出現從屬關係。但如果我們放鬆一些概念層面的要求, 更多的從操作層面考慮一下代碼重用問題, 可以簡單的認爲B在A的基礎上進行操作, 那麼就可以得到一個線性化的結果。也就是說, 放棄A和B之間的交換律只保留結合律, extends A, B 與 extends B,A 會是兩個不同的結果, 不再存在詮釋上的二義性。scala語言中的所謂trait(特性)機制實際上採用的就是這一策略。
面向對象技術發明很久之後, 出現了所謂的面向方面編程(AOP), 它與OOP不同, 是代碼結構空間中的定位與修改技術。AOP的眼中只有類與方法, 不知道什麼叫做意義。AOP也提供了一種類似多重繼承的代碼重用手段, 那就是mixin。對象被看作是可以被打開,然後任意修改的Map, 一組成員變量與方法就被直接注射到對象體內, 直接改變了它的行爲。
prototype.js庫引入了extend函數,
就是Map之間的一個覆蓋運算, 但很管用, 在jQuery庫中也得到了延用. 這個操作類似於mixin, 在jQuery中是代碼重用的主要技術手段—沒有繼承也沒什麼大不了的。
11. 名稱映射: 一切都是數據
代碼好不好, 循環判斷必須少。循環和判斷語句是程序的基本組成部分, 但是優良的代碼庫中卻往往找不到它們的蹤影, 因爲這些語句的交織會模糊系統的邏輯主線, 使我們的思想迷失在疲於奔命的代碼追蹤中。jQuery本身通過each, extend等函數已經極大減少了對循環語句的需求, 對於判斷語句, 則主要是通過映射表來處理. 例如, jQuery的val()函數需要針對不同標籤進行不同的處理, 因此定義一個以tagName爲key的函數映射表。
這樣在程序中就不需要到處寫
可以統一處理
映射表將函數作爲普通數據來管理, 在動態語言中有着廣泛的應用。特別是, 對象本身就是函數和變量的容器, 可以被看作是映射表。jQuery中大量使用的一個技巧就是利用名稱映射來動態生成代碼, 形成一種類似模板的機制。例如爲了實現myWidth和myHeight兩個非常類似的函數, 我們不需要。
而可以選擇動態生成
12. 插件機制:其實我很簡單
jQuery所謂的插件其實就是$.fn上增加的函數, 那這個fn是什麼東西?
顯然, $.fn其實就是jQuery.prototype的簡寫。
無狀態的插件僅僅就是一個函數, 非常簡單。
對於比較複雜的插件開發, jQuery UI提供了一個widget工廠機制,
調用 $(‘#dlg’).dialog(options)時, 實際執行的代碼基本如下所示:
可以看出, 第一次調用$(‘#dlg’).dialog()函數時會創建窗口對象實例,並保存在data中, 此時會調用_create()和_init()函數, 而如果不是第一次調用, 則是在已經存在的對象實例上調用_init()方法. 多次調用$(‘#dlg’).dialog()並不會創建多個實例。
13. browser sniffer vs. feature detection
瀏覽器嗅探(browser sniffer)曾經是很流行的技術, 比如早期的jQuery中
在具體代碼中可以針對不同的瀏覽器作出不同的處理
但是隨着瀏覽器市場的競爭升級, 競爭對手之間的互相模仿和僞裝導致userAgent一片混亂, 加上Chrome的誕生, Safari的崛起, IE也開始加速向標準靠攏, sniffer已經起不到積極的作用。特性檢測(feature detection)作爲更細粒度, 更具體的檢測手段, 逐漸成爲處理瀏覽器兼容性的主流方式。
只基於實際看見的,而不是曾經知道的, 這樣更容易做到兼容未來.
14. Prototype vs. jQuery
prototype.js是一個立意高遠的庫, 它的目標是提供一種新的使用體驗,參照Ruby從語言級別對javascript進行改造,並最終真的極大改變了js的面貌。$, extends, each, bind…這些耳熟能詳的概念都是prototype.js引入到js領域的。它肆無忌憚的在window全局名字空間中增加各種概念, 大有誰先佔坑誰有理, 捨我其誰的氣勢。而jQuery則扣扣索索, 抱着比較實用化的理念, 目標僅僅是write less, do more而已。
不過等待激進的理想主義者的命運往往都是壯志未酬身先死。當prototype.js標誌性的bind函數等被吸收到ECMAScript標準中時, 便註定了它的沒落。到處修改原生對象的prototype, 這是prototype.js的獨門祕技, 也是它的死穴。特別是當它試圖模仿jQuery, 通過Element.extend(element)返回增強對象的時候, 算是徹底被jQuery給帶到溝裏去了。prototype.js與jQuery不同, 它總是直接修改原生對象的prototype, 而瀏覽器卻是充滿bug, 謊言, 歷史包袱並夾雜着商業陰謀的領域, 在原生對象層面解決問題註定是一場悲劇。性能問題, 名字衝突, 兼容性問題等等都是一個幫助庫的能力所無法解決的。Prototype.js的2.0版本據說要做大的變革, 不知是要與歷史決裂, 放棄兼容性, 還是繼續掙扎, 在夾縫中求生。
完!
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.