讓JavaScript擁有類似Lambda運算式編程能力的方法

來源:互聯網
上載者:User

但是我後來也跟人說,因為接受的參數太多,所以如果不把智能提示寫得很清楚的話,連我自己都常常搞不清楚該怎麼用。

不過,接受參數多,除了容易弄錯用法以外,還會產生另一個問題,這也是我編寫出今天發布的這個東西的原因。
來看一下JS版的頁碼呈現組件的完全版函數簽名: 複製代碼 代碼如下:function pnView(
currentPage, actionCurrent,
beginPage, endPage,
actionBegin, actionEnd,
currentSiblings, actionCurrentSibling,
preventFolding, actionFolding,
endOfBegin, beginOfEnd,
actionBeginSibling, actionEndSibling
)

可以看到,這個可以最大幅度進行自訂的完全版函數簽名,接受14個參數,而其中有一半——也就是7個參數,是要接受回呼函數的。
由此產生的問題就是需要手工寫入多次的“function(){}”等字元組合,就像這樣: 複製代碼 代碼如下:function ww(s) { document.write(s); }
function ws(n) { ww(' <A href="#">(' + n + ')</A> '); }
pnView(14, function (n) { ww(' [' + n + '] ') },
1, 27,
function (n) { ww('<A href="#">|< ' + n + '</A> '); }, function (n) { ww(' <A href="#">' + n + ' >|</A>'); },
2, ws,
1, function () { ww(' ... '); },
2, 2,
ws, ws
);

當我在網頁中測試這個組件的時候我就覺得自己非常想念C#中的Lambda運算式——雖然有些人、有些JS架構會把匿名函數稱作“Lambda運算式”,但其實那隻相當於C#中的“匿名委託/函數”,而Lambda的(表面)特徵是使用簡短的文法模式來反映一個(回調)函數/過程(或者說動作)的邏輯,而不被“delegate”或者別的什麼關鍵字分散精力。

出於這樣的原因,我編寫了這個可以轉譯JS代碼、把一種特定的模式(pattern)翻譯成函數定義的模組。
在使用這個模組以後,前面的那個調用可以變成這個樣子: 複製代碼 代碼如下:eval(function () {
var ww = (s, document.write(s));
var ws = (n, ww(' <A href="#">(' + n + ')</A> '));
pnView(14, (n, ww(' [' + n + '] ')),
1, 27,
(n, ww('<A href="#">|< ' + n + '</A> ')), (n, ww(' <A href="#">' + n + ' >|</A>')),
2, ws,
1, (0, ww(' ... ')),
2, 2,
ws, ws
);
}.lamda())();

模組的完整代碼如下: 複製代碼 代碼如下:/*!
L-amda "a-Lambda", a module provides Alternate "Lambda" style programming ability for JavaScript.
Created By NanaLich. 2010-09-08
This module is published under WTFPL v2, so you just DO WHAT THE Fxxx YOU WANT TO with it.
*/
!function () {
function attachEntry(o, a, m) {
var i, j, n;
o = [].concat(o);
//if (!(o instanceof Array)) o = [o];
while (i = o.shift()) {
for (j in a) {
if (!i[n = a[j]]) i[n] = m;
}
}
}
var rx0 = /^\s*(0|NaN|null)\s*,$/;
var rx1 = /([\W]\s*)\((\s*0\s*,|(?:\s*[a-z_$][\w$]*\s*,)+)|"(\\[\s\S]|[^\x22])*"|'(\\[\s\S]|[^\x27])*'/gi;
var rx2 = /\)/g;
function rxGetFlags(rx) { // 取出Regex的建立選項
return (rx.global ? 'g' : '') + (rx.ignoreCase ? 'i' : '') + (rx.multiline ? 'm' : '');
//return /\/([gim]*)$/.exec('' + rx)[1];
}
attachEntry(RegExp, ['getFlags'], rxGetFlags);
attachEntry(RegExp.prototype, ['getFlags'], function () {
return rxGetFlags(this);
});

function translateLambda(s) {
if (typeof (s) != 'string') s = '' + s;
var x = new RegExp(rx1.source, rx1.getFlags());
var y = new RegExp(rx2.source, rx2.getFlags()); // 由於firefox、safari等瀏覽器對全域匹配Regex有過度的最佳化,所以這裡採用一種迂迴的辦法建立不重複的Regex執行個體
var m, l = 0, r = '';
while (m = x.exec(s)) {
var c = m[0], h, p, q, t;
switch (c.charAt(0)) { // 判斷期待的文法成分
case '$':
case ')':
case ']':
case '"':
case "'":
continue; // 函數傳參,跳過
}

h = m[2];
if (rx0.test(h))
h = '';
else
h = h.substr(0, h.length - 1); // 去掉末尾的逗號
r += s.substring(l, p = m.index); // 在結果字串上附加之前餘留的內容
y.lastIndex = l = p + c.length; // 從逗號之後開始尋找右括弧
while (q = y.exec(s)) {
q = q.index;
try {
t = 'return ' + s.substring(l, q) + ';';
new Function(t); // 文法測試
//r += c + 'function(' + h + '){ ' + translateLambda(t) + ' }'; // 翻譯裡面的內容
r += m[1] + 'function(' + h + '){ ' + translateLambda(t) + ' }'; // 翻譯裡面的內容
x.lastIndex = l = q + 1; // 下一次尋找從括弧之後開始
break;
} catch (ex) { }
}
if (!q) l = p; // 說明找不到右括弧或者有效代碼,直接附加所有匹配的內容
}
try {
r += s.substr(l);
if (/[\w$]*|"(\\[\s\S]|[^\x22])*"|'(\\[\s\S]|[^\x27])*'/.exec(r)[0] == 'function') // 粗略判斷產生的是不是函數(可以應付絕大部分情況)
r = '0,' + r; // 使用這種“怪”形式可以在所有瀏覽器(包括IE)中得到預期的效果
new Function(r); // 文法測試
return r;
} catch (ex) { // 失敗,返回原文
return s;
}
};

var lamdaAliases = ["translateLambda", "lambda", "lamda"];
attachEntry([Function, String], lamdaAliases, translateLambda);
attachEntry([Function.prototype, String.prototype], lamdaAliases, function () {
return translateLambda(this);
});
} ();

如果和C#中的Lambda運算式相比的話,我的這個模組還是有很多不足的,不過現在這個樣子已經讓我很滿意了,至少我不用再寫太多的“function”了。
簡單來說,這個模組的規格特性是這樣的——
優點:
減少編寫代碼時“function”的出現次數;
使用可以在一般的JavaScript編輯器中正常編輯的文法模式(pattern),直接寫在函數體中不會導致語法錯誤。
局限性:
在任何時候使用這個模組都必須調用轉譯方法(“translateLambda”、“lambda”或者“lamda”)和eval函數,無法省略;
如果存在一個函數A,不可能通過對A進行處理來達到轉譯傳遞至A的參數的目的(也就是說a.lambda()或者類似的操作並不會讓a((x, x * 2))等同於a(function(x){ return x * 2; }));
不能包含運算式之外的任何語句、不能包含使用“;”來分隔的多條語句。
缺點:
連續出現的括弧可能會讓代碼變得難以理解;
任何編輯器都無法為Lambda運算式提供文法高亮;
存在錯誤地轉譯現有代碼的可能性——這個模組選擇進行匹配的模式是在正常的代碼中沒有實用價值、也通常不會出現的模式,如:(x, x * 2)等價於單純的x * 2、(0, a.method())等價於單純的a.method(),所以這個缺點影響到實際代碼的可能性無限趨近於0。
以下是幾種不當的用法:
1、使用這個模組並不會節省很多代碼量的時候:本末倒置。 複製代碼 代碼如下:eval(function(){ // 不僅沒減少代碼量,反而還增加了
window.onload = (0, alert('載入完成!'));
}.lambda());

2、對接受參數的函數進行轉譯處理:之前已經提到過這種情況。 複製代碼 代碼如下:eval(somefunction.lambda())((x, x.toString(16))); // somefunction可能會產生預料外的結果,而且收到的參數會是x.toString(16)的結果(如果x在此處並沒有被定義過,還會產生一個“變數不存在”的異常),而非一個回呼函數。

3、為了使用此模組而規避語法檢查:
因為使用的是在JavaScript中有效但無實用價值的文法,所以規避語法檢查是完全沒有必要的。 複製代碼 代碼如下:eval("somefunction((x, x.toString(16)))".lamda()); // 失去了語法檢查,可能在啟動並執行時候產生意外

4、在(偽)Lambda中使用過多的操作,甚至多條語句:
在設計這個模組的時候我並沒有找出可以使用多條語句且可以通過語法檢查的模式(pattern),原因是在Lambda運算式中使用多條語句時,“function”、“return”等幾個字元所增加的代碼量通常是可以忽略的,這樣去使用Lambda運算式本身就屬於一種偏離了初衷的做法。 複製代碼 代碼如下:eval(function(){ somefunction((x, y.something(x); return x.toString(16))); }.lamda())(); // 語法錯誤
eval(function(){ somefunction((x, y.something(x), x.toString(16))); }.lamda())(); // 容易產生理解上的歧義
eval(function(){ somefunction((x, ++x)); }.lamda())(); // 簡單的運算式可以被接受

最佳使用場合:
現在很多人寫JavaScript的時候喜歡把自己的代碼都寫在一個閉包裡面,這樣可以避免全域範圍汙染問題,就像這樣: 複製代碼 代碼如下:(function(){
// 所有的代碼都放在這裡
})();

——而這種“大”閉包正好是使用本模組的最佳場合: 複製代碼 代碼如下:eval(function(){ // 括弧前增加eval
// 所有的代碼都放在這裡
}.lamda())(); // 括弧裡增加.lamda()

昨天codeplex抽瘋,代碼和release上傳總出錯。再考慮到這個模組的使用場合比較有限,不適合缺乏JavaScript經驗的人使用,所以不另外提供原始碼打包下載——有需要的話請直接從文中複製。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.