JavaScript需要Blocks

發表於2012-01-13

英文出自:yehudakatz.com,編譯:CSDN趙紅

常常會遇到有人將Ruby的區塊(Blocks)看作相當於JavaScript的“first class functions ”的誤解。由於傳遞功能,尤其是當你可以建立匿名的傳遞功能,這是非常強大的。事實上,JavaScript和Ruby有一個機制使其自然會認為等值。

人們在談到為什麼Ruby的區塊不同於Python的函式時,通常會講到一些關於Ruby和JavaScript的匿名分享,但Python沒有。初看之下,一個Ruby區塊就是一個“匿名函式”(或俗稱一個“封裝”),正如JavaScript函式就是其中之一。

作為一個早期的Ruby/JavaScript開發者,無可否認我也有過這樣的觀點分享。錯過一個重要的細節,對結果會產生較大影響。這個原理常被稱為“Tennent’s Correspondence Principle”,這條原理說:“For a given expression expr, lambda expr should be equivalent.”這就是被稱為抽象的原則,因為這意味著,用“區塊”的方法很容易重構通用程式碼。例如,常見檔案資源管理的情況。試想在Ruby中,File.open塊形式是不存在的,你會看到以下程式碼:

乍一看,在Ruby和JavaScript中確實如此。例如,假設你正在使用的檔案列印它的mtime。您可以輕鬆地重構相當於在JavaScript:

到這裡:

事實上,這樣的情況往往給人錯誤的印象,Ruby和JavaScript有同樣用匿名函式重構常用功能的能力。

不過,再來一個稍微複雜一些的例子。我們首先在Ruby中編寫一個簡單的類,計算檔案的mtime和檢索它的正文:

同樣地,需要注意的重點是,我們構建區塊卻並不改變它的內部程式碼。但不幸的是,這個相同情況的例子無法在JavaScript中正常工作。讓我們在JavaScript中來寫等同的FileInfo類:

如果我們試圖將其轉換成一個接受重複函式的程式碼,那mtime方法看起來將是:在這裡有兩個非常普遍的問題。首先是上下文改變了。我們可以通過允許繫結第二引數,但這意味著每次重構時需要確認並通過一個變數傳遞引數,就是說這一情況會在因為缺乏JavaScript信任元件時而出現。

這很煩人,更棘手的還在於,它是從內部返回結果而不是從函式外部。這個真實的結果違反了抽象原則中的對應原理。相反,在函式中用區塊方法毫不費力地重構具有相同開始和結束的程式碼時,JavaScript庫作者需要考慮使用者對API處理巢狀函式時進行的一些操作。作為一個JavaScript庫資源的編寫者和使用者看來,這提供了一個很好的基於區塊的API。

迭代和回撥

值得注意的是,區塊lambda函式接受功能呼叫的案例包括迭代器、同步與互斥、資源管理(如區塊形式的File.open)。

使用函式作為回撥時,關鍵字不再有意義。從一個已經返回的函式返回是什麼意思?在這種情況下,通常涉及回撥函式lambda表示式做出了很大的意義。在我看來,這解釋了為什麼JavaScript事件觸發程式碼,涉及了大量的回撥。

由於這些問題,ECMA工作組負責的ECMAScript,TC39,正在考慮加入塊lambda表示式語言。這將意味著,上面的例子可重構:

TC39並沒有實質性改變這個例子,並且要注意區塊lambda表示式自動返回他們的最後一個語句。

經驗顯示,Smalltalk和Ruby不需要理解一種語言可怕的對應原理,滿足它獲得自己想要的結果。“迭代”概念並不內建到語言,而是被自然塊定義的結果。這使得Ruby以及其他常用語言的開發者可建立自定義的豐富、內建的迭代設定。作為一個JavaScript實踐者,我經常碰到的情況是,用一個for迴圈比使用forEach更為簡單明瞭。

業界人士觀點

munificent :

In order to have a language with return (and possibly super and other similar keywords) that satisfies the correspondence principle, the language must, like Ruby and Smalltalk before it, have a function lambda and a block lambda. Keywords like return always return from the function lambda, even inside of block lambdas nested inside.In case you want to get your google/wikipedia on, what Katz is talking about here is a “non-local return”.

更多評論詳細請點選這裡>>

ericbb :Alternate formulation with hypothetical shift/reset (delimited continuation support) and blocks that return

 

It’s the computation within the reset block that comes after the shift form is evaluated。Calling it after returning from the method would not raise an exception.

更多評論詳細請點選這裡>>

 

相關文章