用函數式編程技術編寫優美的 JavaScript_ibm
做者: 字體:[增長 減少] 類型:轉載
函數式編程語言在學術領域已經存在至關長一段時間了,可是從歷史上看,它們沒有豐富的工具和庫可供使用。隨着 .NET 平臺上的 Haskell 的出現,函數式編程變得更加流行。一些傳統的編程語言,例如 C++ 和 JavaScript,引入了由函數式編程提供的一些構造和特性。在許多狀況下,JavaScript 的重複代碼致使了一些拙劣的編碼。若是使用函數式編程,就能夠避免這些問題。此外,能夠利用函數式編程風格編寫更加優美的回調。程序員
由於函數式編程採用了徹底不一樣的組織程序的方式,因此那些習慣於採用命令式範例的程序員可能會發現函數式編程有點難學。在這篇文章中,您將瞭解一些關於如何採用函數式風格,用 JavaScript 編寫良好的、優美的代碼的示例。我將討論: 編程
函數式編程概念,包括匿名函數、調用函數的不一樣方法,以及將函數做爲參數傳遞給其餘函數的方式。 數組
函數式概念的運用,採用的示例包括:擴展數組排序;動態 HTML 生成的優美代碼;系列函數的應用。
函數式編程概念 編程語言
請告訴每一個人。請把這個提交給: 函數式編程
Digg 函數
Slashdot 工具
在那些經過描述 「如何作」 指定解決問題的方法的語言中,許多開發人員都知道如何進行編碼。例如,要編寫一個計算階乘的函數,我能夠編寫一個循環來描述程序,或者使用遞歸來查找全部數字的乘積。在這兩種狀況下,計算的過程都在程序中進行了詳細說明。清單 1 顯示了一個計算階乘的可能使用的 C 代碼。 學習
清單 1. 過程風格的階乘 字體
int factorial (int n)
{
if (n <= 0)
return 1;
else
return n * factorial (n-1);
} this
這類語言也叫作過程性 編程語言,由於它們定義瞭解決問題的過程。函數式編程與這個原理有顯著不一樣。在函數式編程中,須要描述問題 「是什麼」。 函數式編程語言又叫作聲明性 語言。一樣的計算階乘的程序能夠寫成全部到 n 的數字的乘積。計算階乘的典型函數式程序看起來如 清單 2 中的示例所示。
清單 2. 函數式風格的階乘
factorial n, where n <= 0 := 1
factorial n := foldr * 1 take n [1..]
第二個語句指明要獲得從 1 開始的前 n 個數字的列表(take n [1..]),而後找出它們的乘積,1 爲基元。這個定義與前面的示例不一樣,沒有循環或遞歸。它就像階乘函數的算術定義。一旦瞭解了庫函數(take 和 foldr)和標記(list notation [ ])的意義,編寫代碼就很容易,並且可讀性也很好。
只用三行 Miranda 代碼就能夠編寫例程,根據參數,使用廣度優先或深度優先遍歷處理 n 叉樹的每一個節點,並且元素能夠是任何通用類型。
從歷史上看,函數式編程語言不太流行有各類緣由。可是最近,有些函數式編程語言正在進入計算機行業。其中一個例子就是 .NET 平臺上的 Haskell。其餘狀況下,現有的一些語言借用了函數式編程語言中的一些概念。一些 C++ 實現中的迭代器和 continuation,以及 JavaScript 中提供的一些函數式構造(functional construct),就是這種借用的示例。可是,經過借用函數式構造,總的語言編程範例並無發生變化。JavaScript 並沒由於函數式構造的添加就變成了函數式編程語言。
我如今要討論 JavaScript 中的函數式構造的各類美妙之處,以及在平常編碼和工做中使用它們的方式。咱們將從一些基本功能開始,而後用它們查看一些更有趣的應用。
匿名函數
在 JavaScript 中,能夠編寫匿名函數或沒有名稱的函數。爲何須要這樣的函數?請繼續往下讀,但首先咱們將學習如何編寫這樣一個函數。若是擁有如下 JavaScript 函數:
清單 3. 典型的函數
function sum(x,y,z) {
return (x+y+z);
}
而後對應的匿名函數看起來應當以下所示:
清單 4. 匿名函數
function(x,y,z) {
return (x+y+z);
}
要使用它,則須要編寫如下代碼:
清單 5. 應用匿名函數
var sum = function(x,y,z) {
return (x+y+z);
}(1,2,3);
alert(sum);
使用函數做爲值
也能夠將函數做爲值使用。還能夠擁有一些所賦值是函數的變量。在最後一個示例中,還能夠執行如下操做:
清單 6. 使用函數賦值
var sum = function(x,y,z) {
return (x+y+z);
}
alert(sum(1,2,3));
在上面 清單 6 的示例中,爲變量 sum 賦的值是函數定義自己。這樣,sum 就成了一個函數,能夠在任何地方調用。
調用函數的不一樣方法
JavaScript 容許用兩種方式調用函數,如清單 7 和 8 所示。
清單 7. 典型的函數應用
alert (「Hello, World!");
或
清單 8. 用函數做爲表達式
(alert) (「Hello, World!");
因此也能夠編寫如下代碼:
清單 9. 定義函數以後就能夠當即使用它
( function(x,y,z) { return (x+y+z) } ) (1, 2, 3);
能夠在括號中編寫函數表達式,而後傳遞給參數,對參數進行運算。雖然在 清單 8 的示例中,有直接包含在括號中的函數名稱,可是按 清單 9 中所示方式使用它時,就不是這樣了。
將函數做爲參數傳遞給其餘函數
也能夠將函數做爲參數傳遞給其餘函數。雖然這不是什麼新概念,可是在後續的示例中大量的使用了這個概念。能夠傳遞函數參數,如 清單 10 所示。
清單 10. 將函數做爲參數傳遞,並應用該函數
var passFunAndApply = function (fn,x,y,z) { return fn(x,y,z); };
var sum = function(x,y,z) {
return x+y+z;
};
alert( passFunAndApply(sum,3,4,5) ); // 12
執行最後一個 alert 語句輸出了一個大小爲 12 的值。
使用函數式概念
前一節介紹了一些使用函數式風格的編程概念。所給的示例並無包含全部的概念,它們在重要性方面也沒有前後順序,只是一些與這個討論有關的概念而已。下面對 JavaScript 中的函數式風格做一快速總結:
函數並不老是須要名稱。
函數能夠像其餘值同樣分配給變量。
函數表達式能夠編寫並放在括號中,留待之後應用。
函數能夠做爲參數傳遞給其餘函數。
這一節將介紹一些有效使用這些概念編寫優美的 JavaScript 代碼的示例。(使用 JavaScript 函數式風格,能夠作許多超出這個討論範圍的事。)
擴展數組排序
先來編寫一個排序方法,能夠根據數組元素的日期對數據進行排序。用 JavaScript 編寫這個方法很是簡單。數據對象的排序方法接受一個可選參數,這個可選參數就是比較函數。在這裏,須要使用 清單 11 中的比較函數。
清單 11. 比較函數
function (x,y) {
return x.date – y.date;
}
要獲得須要的函數,請使用 清單 12 的示例。
清單 12. 排序函數的擴展
arr.sort( function (x,y) { return x.date – y.date; } );
其中 arr 是類型數組對象。排序函數會根據 arr 數組中對象的日期對全部對象進行排序。比較函數和它的定義一塊兒被傳遞給排序函數,以完成排序操做。使用這個函數:
每一個 JavaScript 對象都有一個 date 屬性。
JavaScript 的數組類型的排序函數接受可選參數,可選參數是用來排序的比較函數。這與 C 庫中的 qsort 函數相似。
動態生成 HTML 的優美代碼
在這個示例中,將看到如何編寫優美的代碼,從數組動態地生成 HTML。能夠根據從數據中獲得的值生成表格。或者,也能夠用數組的內容生成排序和未排序的列表。也能夠生成垂直或水平的菜單項目。
清單 13 中的代碼風格一般被用來從數組生成動態 HTML。
清單 13. 生成動態 HTML 的普通代碼
var str=' ';
for (var i=0;i<arr.length;i++) {
var element=arr[i];
str+=... HTML generation code...
}
document.write(str);
能夠用 清單 14 的代碼替換這個代碼。
清單 14. 生成動態 HTML 的通用方式
Array.prototype.fold=function(templateFn) {
var len=this.length;
var str=' ';
for (var i=0 ; i<len ; i++)
str+=templateFn(this[i]);
return str;
}
function templateInstance(element) {
return ... HTML generation code ...
}
document.write(arr.fold(templateInstance));
我使用 Array 類型的 prototype 屬性定義新函數 fold。如今能夠在後面定義的任何數組中使用該函數。
系列函數的應用
考慮如下這種狀況:想用一組函數做爲回調函數。爲實現這一目的,將使用 window.setTimeout 函數,該函數有兩個參數。第一個參數是在第二個參數表示的毫秒數以後被調用的函數。清單 15 顯示了完成此操做的一種方法。
清單 15. 在回調中調用一組函數
window.setTimeout(function(){alert(‘First!');alert(‘Second!');}, 5000);
清單 16 顯示了完成此操做的更好的方式。
清單 16. 調用系列函數的更好的方式
Function.prototype.sequence=function(g) {
var f=this;
return function() {
f();g();
}
};
function alertFrst() { alert(‘First!'); }
function alertSec() { alert(‘Second!'); }
setTimeout( alertFrst.sequence(alertSec), 5000);
在處理事件時,若是想在調用完一個回調以後再調用一個回調,也可使用 清單 16 中的代碼擴展。這多是一個須要您自行完成的一個練習,如今您的興趣被點燃了吧。
回頁首
結束語
在許多領域中均可以應用 JavaScript 中的函數式編程,以優美的方式完成平常活動。這篇文章中的示例只介紹了幾種狀況。若是您找到了函數式編程的合適場景,並應用這些概念,那麼您就會有更多的理解,而且能夠增長您的優美程度。