在一般的編程語言中,函數的參數只能是基本類型或者對象引用,返回值也只是基本數據類型或對象引用。但在Javascript中函數做爲一等公民,既能夠當作參數傳遞,也能夠被當作返回值返回。所謂高階函數就是能夠把函數做爲參數,或者是將函數做爲返回值的函數。這兩種情形在實際開發中有不少應用場景,本文是我在工做學習中遇到的幾種應用場景的總結。node
回調函數編程
代碼複用是衡量一個應用程序的重要標準之一。經過將變化的業務邏輯抽離封裝在回調函數中可以有效的提升代碼複用率。好比ES5中爲數組增長的forEach方法,遍歷數組,對每一個元素調用同一個函數。設計模式
array = {}; array.forEach = function(arr, fn){ for (var i = 0, len = arr.length; i < len; i++) { fn(arr[i], i, arr); } }
經過回調函數將業務的重點聚焦在回調函數中,而沒必要每次都要重複編寫遍歷代碼。數組
偏函數瀏覽器
做爲將函數當作返回值輸出的典型應用就是偏函數。所謂偏函數是指建立一個調用另一個部分——參數或變量已經預置的函數——的函數的用法。反正看着定義我是沒理解這東東干嗎的。我們仍是先看例子吧,偏函數最典型的例子就是類型判斷。閉包
Javascript對象都擁有三個屬性:原型屬性、類屬性、可擴展性。(不知道的同窗要回去翻犀牛書哦,page:138)類屬性是一個字符串,Javascript中並未直接提供,但咱們能夠利用Object.prototype.toString來間接獲得。該函數老是返回以下形式:app
[object Class]
所以咱們能夠編寫一系列isType函數。代碼以下:dom
isString = function(obj){ return Object.prototype.toString.call(obj) === "[object String]"; } isNumber = function(obj){ return Object.prototype.toString.call(obj) === "[object Number]"; } isArray = function(obj){ return Object.prototype.toString.call(obj) === "[object Array]"; }
這幾個函數中大部分代碼是重複的,這時高階函數便華麗麗的登場了:編程語言
isType = function(type) { return function(obj) { return Object.prototype.toString.call(obj) === "[object " + type + "]"; } } isString = isType('String'); isNumber = isType('Number'); isArray = isType('Array');
因此經過指定部分參數來返回一個新的定製函數的形式就是偏函數。函數
currying(柯里化)
currying又稱部分求值。一個currying的函數首先會接受一些參數,接受這些參數以後,函數並不會當即求值,而是繼續返回另外一個函數,剛纔傳入的參數在函數造成的閉包中被保存起來。待到函數被真正須要求值的時候,以前傳入的全部參數都會被一次性用於求值。
var currying = function(fn) { var args = []; return function() { if (arguments.length === 0) { return fn.applay(this, args); } else { args = args.concat(arguments); return arguments.callee; } } }
假設咱們以計算一個月天天花銷爲例:
var currying = function(fn) { debugger; var args = []; return function() { if (arguments.length === 0) { return fn.apply(this, args); } else { Array.prototype.push.apply(args, arguments); return arguments.callee; } } } cost = function(){ var sum = 0; for (var i = 0, len = arguments.length; i < len; i++) { sum += arguments[i]; } return sum; } var cost = currying(cost); cost(100); cost(200); alert(cost())
事件節流
在某些場景下,某些事件可能會被重複的觸發,但事件處理函數並不須要每次都執行。好比在window.resize事件中進行復雜的邏輯計算,若是用戶頻繁的改變瀏覽器大小,複雜計算會對性能形成嚴重影響;有時這些邏輯計算並不須要每次rezise時都觸發,只須要計算有限的幾回即可以。這時咱們須要根據時間段來忽略一些事件請求。請看如下節流函數:
function throttle(fn, interval) { var doing = false; return function() { if (doing) { return; } doing = true; fn.apply(this, arguments); setTimeout(function() { doing = false; }, interval); } } window.onresize = throttle(function(){ console.log('execute'); }, 500);
經過控制函數執行時間,能夠在函數執行次數與功能需求之間達到完美平衡。另外一個事件是mousemove。若是咱們給一個dom元素綁定該事件,鼠標在改元素上移動時,該事件便會重複觸發。
事件結束
對於某些能夠頻繁觸發的事件,有時候咱們但願在事件結束後進行一系列操做。這時咱們能夠利用高階函數作以下處理:
function debounce(fn, interval) { var timer = null; function delay() { var target = this; var args = arguments; return setTimeout(function(){ fn.apply(target, args); }, interval); } return function() { if (timer) { clearTimeout(timer); } timer = delay.apply(this, arguments); } }; window.onresize = throttle(function(){ console.log('resize end'); }, 500);
若是在這一過程當中事件被觸發則清除上一次事件句柄,從新綁定執行時間。
參考資料:
《深刻淺出node》
《Javascript設計模式與開發實踐》