JS高階函數與函數柯里化

高階函數數組

知足下列條件之一的函數:閉包

函數做爲參數被傳遞(如回調函數);app

函數能夠做爲返回值輸出;函數

 

一些內置高階函數的例子:測試

Array.prototype.mapthis

map()方法經過調用對輸入數組中的每一個元素調用回調函數來建立一個新數組。spa

map()方法將獲取回調函數的每一個返回值,並使用這些值建立一個新數組。prototype

map()方法的回調函數總共接收3個參數:element,index和array。rest

例子:code

假設咱們有一個數字數組,咱們想建立一個新數組,新數組的每一個值是原數組對應值的兩倍。

不使用高階函數:

const arr1 = [1, 2, 3];
const arr2 = [];
for(let i = 0; i < arr1.length; i ++) {
  arr2.push(arr[i] * 2);      
}

console.log(arr2);

//[2, 4, 6]

使用高階函數:

const arr1 = [1, 2, 3];
const arr2 = arr1.map(item=> item * 2);
console.log(arr2);

 

Array.prototype.filter

filter()方法會建立一個新數組,其中包含全部經過回調函數測試的元素。傳遞給filter()方法的回調函數接受3個參數:element,index和array

例子:

假設咱們有一個包含數字值的數組,選擇其中的數值建立一個數值大於18的數組

不使用高階函數:

const arr1 = [1, 2, 19, 20];
const arr2 = [];
for(let i = 0; i < arr1.length; i ++) {
  if(arr1[i] > 18) {
   arr2.push(arr1[1])       
    }      
}

console.log(arr2)

使用高階函數:

const arr1 = [1, 2, 19, 20];
const arr2 = arr1.filter(item=> item > 18);

console.log(arr2)

 

Array.prototype.reduce

reduce()方法對調用數組的每一個元素執行回調函數,最後生成一個單一的值並返回。

reduce()方法接受兩個參數:redecer函數(回調),一個可選的initialValue

reducer函數(回調)接受4個參數:accumulater, currentValue, currentIndex, sourceArray

若是提供了 initialValue,則累加器將等於 initialValue,currentValue 將等於數組中的第一個元素。

若是沒有提供 initialValue,則累加器將等於數組中的第一個元素,currentValue 將等於數組中的第二個元素。

例子:

對一個數字數組的求和:

不使用高階函數:

const arr = [1, 2, 7];
let sum = 0;
for(let i = 0; i< arr.length; i++) {
  sum = sum + arr[i]  
}
// 10

使用高階函數:

const arr = [1, 2, 7];
const sum = arr.reduce((accumulator,currentValue)=> 
    return accumulator + currentValue )    
// 10

還能夠爲它提供初始值:

const arr = [1, 2, 7];
const sum = arr.reduce((accumulator,currentValue)=> 
    return accumulator + currentValue,10 )    
// 20

 

 

柯里化

柯里化(currying)又稱部分求值。一個currying的函數首先會接受一些參數,接受這些參數以後,函數並不會當即求值,而是繼續返回另外一個函數,剛纔傳入的參數在函數造成的閉包中被保存起來。待到函數被真正須要求值的時候,以前傳入的全部參數都會被一次性用於求值。

計算天天的花銷

var currying = function(fn) {

    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())

 通俗地講,柯里化就是一個部分配置多參數函數的過程,每一步都返回一個接受單個參數的部分配置好的函數。一些極端的狀況可能須要分不少次來部分配置一個函數,好比說屢次相加:

multiPlus(1)(2)(3); // => 6

上代碼

// ES5
function curry(fn) {
  function _c(restNum, argsList) {
    return restNum === 0 ?
      fn.apply(null, argsList) :
      function(x) {
        return _c(restNum - 1, argsList.concat(x));
      };
  }
  return _c(fn.length, []);
}

// ES6
const curry = fn => {
  const _c = (restNum, argsList) => restNum === 0 ?
    fn(...argsList) : x => _c(restNum - 1, [...argsList, x]);

  return _c(fn.length, []);
}

/***************** 使用 *********************/

var plus = curry(function(a, b) {
  return a + b;
});

分析:

咱們須要一個curry函數,它接受一個待柯里化的函數爲參會素,返回一個用於接收一個參數的函數,接收到的參數放到一個列表中,當參數數量足夠時,執行原函數並返回結果。

實現方式:

簡單思考能夠知道,柯里化部分配置函數的步驟數等於 fn 的參數個數,也就是說有兩個參數的 plus 函數須要分兩步來部分配置。函數的參數個數能夠經過fn.length獲取。

總的想法就是每傳一次參,就把該參數放入一個參數列表 argsList 中,若是已經沒有要傳的參數了,那麼就調用fn.apply(null, argsList)將原函數執行。要實現這點,咱們就須要一個內部的判斷函數 _c(restNum, argsList),函數接受兩個參數,一個是剩餘參數個數 restNum,另外一個是已獲取的參數的列表 argsList_c 的功能就是判斷是否還有未傳入的參數,當 restNum 爲零時,就是時候經過fn.apply(null, argsList)執行原函數並返回結果了。若是還有參數須要傳遞的話,也就是說 restNum 不爲零時,就須要返回一個單參數函數

function(x) {
  return _c(restNum - 1, argsList.concat(x));
}

來繼續接收參數。這裏造成了一個尾遞歸,函數接受了一個參數後,剩餘須要參數數量 restNum 減一,並將新參數 x 加入 argsList 後傳入 _c 進行遞歸調用。結果就是,當參數數量不足時,返回負責接收新參數的單參數函數,當參數夠了時,就調用原函數並返回。

如今再來看:

function curry(fn) {
  function _c(restNum, argsList) {
    return restNum === 0 ?
      fn.apply(null, argsList) :
      function(x) {
        return _c(restNum - 1, argsList.concat(x));
      };
  }
  return _c(fn.length, []); // 遞歸開始
}

可使用ES6寫法,用數組結構和箭頭函數:

// ES6
const curry = fn => {
  const _c = (restNum, argsList) => restNum === 0 ?
    fn(...argsList) : x => _c(restNum - 1, [...argsList, x]);

  return _c(fn.length, []);
}

另一種方法:

function curry(fn) {
  const len = fn.length;
  return function judge(...args1) {
    return args1.length >= len ?
    fn(...args1):
    function(...args2) {
      return judge(...[...args1, ...args2]);
    }
  }
}

// 使用箭頭函數
const curry = fn => {
  const len = fn.length;
  const judge = (...args1) => args1.length >= len ?
    fn(...args1) : (...args2) => judge(...[...args1, ...args2]);
  return judge;
}
相關文章
相關標籤/搜索