jQuery is DSL (Part 2 - jQuery)
jQuery的Internal DSL形式
在上一篇文章里面,我們了解到了Internal DSL的具體形式,形如:
/* Method Chaining */
computer()
.processor()
.cores(2)
.i386()
.disk()
.size(150)
.disk()
.size(75)
.speed(7200)
.sata()
.end();
然后我們在看看一段典型的jQuery代碼:
$("ul#contacts li.item")
.find("span.name")
.click(function(e) { $(e.target).siblings(".more").toggle(); })
.end()
.find("input.delete")
.click(function(e) { $(e.target).parents(".item").remove(); })
.end()
.find("div.more")
.hide()
.end();
從結構上來說,是不是跟上面那一段Internal DSL的例子很相似?就算我們不看對應的HTML,我們也能猜到這段jQuery代碼的含義:
- 遍歷
- 中的每一個
(這看起來是個聯系人列表)- 對于里面的
- 綁定
click
事件,操作是顯示/隱藏class="more"
兄弟節點
(這是估計聯系人姓名,點擊后切換詳細信息的顯示/隱藏)
- 綁定
- 對于里面的
- 綁定
click
事件,操作是把class="item"
父節點刪除
(這應該是用來刪除聯系人的)
- 綁定
- 對于里面的
- 隱藏這個
div
(默認隱藏詳細信息?)
- 隱藏這個
- 對于里面的
從這里我們已經能夠看出jQuery的Internal DSL形式帶來的好處——編寫代碼時,讓代碼更貼近作者的思維模式;閱讀代碼時,讓讀者更容易理解代碼的含義。不信?我們看看與jQuery擁有相似功能的Prototype是如何實現上述邏輯:
$$("ul#contacts li.item span.name")
.invoke("observe", "click",
function(e) { $(e.target).next(".more").toggle(); });
$$("ul#contacts li.item input.delete")
.invoke("observe", "click",
function(e) { $(e.target).up(".item").remove(); });
$$("ul#contacts li.item div.more")
.invoke("hide");這是我用Prototype所能寫出的最貼近Internal DSL的形式了。(如果你能夠寫出一個更自然的版本,歡迎分享。)在Prototype里面,能夠返回一組元素的操作就只有
$$()
,并且它只能作用于全局,缺乏jQuery中find()
或者filter()
的功能,所以這一組描述聯系人列表行為的語句無法組合在一起,必須逐一定義每類元素的行為。此外,此例子中每類元素都僅僅指定了一個行為,因此Prototype的invoke()
寫法看起來還是和jQuery的click()
寫法很相近的。但如果一類元素擁有多個行為,Prototype的invoke()
就不能好像jQuery那樣鏈式調用下去了,必須每一個行為重頭寫一個$$()
,或者把invoke()
改成each()
加匿名函數。無論是那種做法,都只會降低代碼的可讀性。jQuery的語法分析器
我們都知道,Internal DSL的實現依賴于對語法分析器的封裝,對Internal DSL的調用其實都是對語法分析器的調用,經過語法分析后再構造出對底層API的調用。例如jQuery當中的click()
,它依賴于當前的狀態,也就是前面$()
篩選出來的節點集合,把click()
解釋為要為這一組節點綁定DOM的click事件,最后再調用DOM API完成任務。在這個例子當中,DOM API相對jQuery API而言就是底層API了。jQuery可以說是挑了一個最容易實現的語法模型來做,永遠只有一種token,因此永遠也只有一種狀態,這種狀態當然也是永遠有效的,你根本不可能給jQuery輸入一個當前狀態無效的token。jQuery的唯一狀態就是一個jQuery對象實例,其本質就是一個元素集合。讀入的token可能是各種針對這個元素集合的操作,但它的返回一定還是一個元素集合。這使得jQuery的語法分析器不會進入無效狀態,也就無需判斷無效狀態,因此大大簡化了Internal DSL實現中常見的一個難題。
小結
通過拿jQuery和Prototype做對比,我們可以發現jQuery用非常低的成本實現了Internal DSL,同時帶來了Prototype所沒有的明顯好處。這可以看作是一個很好的范例——如果你需要描述的業務邏輯能夠歸納為簡單的語言模式,為此實現一門Internal DSL的性價比將會是很高的。你需要做的僅僅是為這個簡單的語言模型實現一個簡單的解釋器,接著你就可以享受貼近人類思維模式的接口了。
留言列表