[深刻05] 柯里化 偏函數 函數記憶

導航

[深刻01] 執行上下文
[深刻02] 原型鏈
[深刻03] 繼承
[深刻04] 事件循環
[深刻05] 柯里化 偏函數 函數記憶
[深刻06] 隱式轉換 和 運算符
[深刻07] 瀏覽器緩存機制(http緩存機制)
[深刻08] 前端安全
[深刻09] 深淺拷貝
[深刻10] Debounce Throttle
[深刻11] 前端路由
[深刻12] 前端模塊化
[深刻13] 觀察者模式 發佈訂閱模式 雙向數據綁定
[深刻14] canvas
[深刻15] webSocket
[深刻16] webpack
[深刻17] http 和 https
[深刻18] CSS-interview
[react] Hooks前端

[部署01] Nginx
[部署02] Docker 部署vue項目
[部署03] gitlab-CIvue

[源碼-webpack01-前置知識] AST抽象語法樹
[源碼-webpack02-前置知識] Tapable
[源碼-webpack03] 手寫webpack - compiler簡單編譯流程react

前置知識

函數的參數

  • length:函數的legth屬性,返回函數預期的參數個數(形參)
  • arguments:arguments對象,包含了程序運行時的全部參數(實參)

相似數組的對象轉換成數組

  • [].slice.call(相似數組的對象)
  • [].slice.apply(相似數組的對象)
  • Array.prototype.slice.call(相似數組的對象, x) // x是綁定this後傳入slice函數的參數
  • Array.from()

偏函數和柯里化的概念

  • 柯里化 curry:
    • 將接收多個參數的函數,轉換成接收一個單一參數的函數,並返回接收餘下參數,並返回最終結果的新函數
    • 即當參數小於預期參數時,返回一個能夠接收剩餘參數的函數,參數大於等於預期參數時,返回最終結果
  • 偏函數 partial application:
    • 是固定一個或多個參數,產生另外一個較小元的函數 n元函數 => 轉換成n-x元函數

柯里化 curry

  • 柯里化函數,他接收函數A做爲參數,運行後可以返回一個新的函數,而且這個新的函數可以處理函數A的剩餘參數

1. 柯里化階段一

  • 需求: 將add(1,2,3)轉化成curryAdd(1)(2)(3)
  • 缺點:只能處理3個參數的狀況,不能處理任意多個參數的狀況,毫無擴展性
需求: 將add(1,2,3)轉化成curryAdd(1)(2)(3)
缺點:只能處理3個參數的狀況,不能處理任意多個參數的狀況,毫無擴展性

function curryAdd(a) {
  return function(b) {
    return function(c) {
      return a+b+c
    }
  }
}
const res = curryAdd(1)(2)(3)
console.log(res, 'res1')
複製代碼

2. 柯里化階段二

  • 需求:處理任意多個參數相加
  • 缺點:
    • 1. 處理相加邏輯的代碼,只是在沒有參數時纔會執行,其餘部分都在處理怎麼收集全部參數,會多一次沒有參數的調用
      • 更合理的方式是經過判斷函數能夠接收參數的總和,來判斷是否參數收集完畢
    • 2. 相加邏輯能夠單獨抽離
function curryAdd() {
  let params_arr = [] // 用於收集全部實參
  function closure() {
    const args = Array.prototype.slice.call(arguments) // 每次調用閉包函數傳入的實參,能夠是多個
    if (args.length) {
      params_arr = params_arr.concat(args)
      // concat返回一個拼接事後的新數組,不改變原數組
      return closure
      // 若是還有參數,則繼續返回閉包函數,則繼續繼續傳參調用
    }
    return params_arr.reduce((total, current) => total + current)
    // 若是沒有再傳入參數,則相加全部傳入的參數,缺點是要多一次沒有參數的調用
  }
  return closure // 第一次調用curryAdd返回的閉包
}
const fn = curryAdd()
const res = fn(1,2)(3)(4)()
console.log(res, 'res'); // 10

複製代碼

3. 柯里化階段三

function add(a,b,c,d,e) {
  return Array.prototype.slice.call(arguments).reduce((total, current) => total + current)
  // 注意:這裏拿到的是實參的實際個數,即實參可能大於形參,當實參 (大於等於) 形參時,執行相加
}
function curryAdd(fn) {
  let paramsArr = []
  const paramsMaxLength = fn.length // function.length返回函數的形參個數,預期的參數個數爲最大參數個數,即相加執行條件
  function closure() {
    const args = Array.prototype.slice.call(arguments)
    paramsArr = paramsArr.concat(args)
    if (paramsArr.length < paramsMaxLength) {
      return closure
    }
    // 當參數個數 大於等於 最大的指望個數,即形參的個數時,執行相加函數
    return fn.apply(this, paramsArr)
  }
  return closure
}
const fn = curryAdd(add)
const res = fn(1,2,3)(4)(5,6)
console.log(res, 'res');
複製代碼

4.柯里化變通版

  • 上面版本的缺點:上面的版本須要知道add的參數length
function add() {
  return Array.from(arguments).reduce((total, current) => total + current)
}
function currentAdd(fn) {
  let paramsArr = []
  function closure() { // 該閉包函數只負責收集參數,處理相加能夠在閉包上掛載新的方法getSum
    const args = Array.from(arguments)
    paramsArr = paramsArr.concat(args)
    return closure
  }
  closure.getSum = function() {
    return fn.apply(this, paramsArr) // getSum負責計算,利用了閉包中的變量paramsArr
  }
  return closure
}
const fn = currentAdd(add)
const resAdd = fn(1)(2,3)
const res = resAdd.getSum(); // 該方法的缺點就是須要單獨再調用getSum函數
console.log(res, 'res')
複製代碼

偏函數 partial

  • 將一個或者多個參數,固定到一個函數上,併產生返回一個更小元的函數
function add (a, b) {
  return a + b
}
function partial (fn) {...}

const addPartial = partial(add, 1)  // ------------------ 實現固定一部分參數1
const res = addPartial(2) // 3 -------------------------- 只傳一部分參數 2
複製代碼

偏函數實現方式1

  • 經過bind方法實現
  • bind方法綁定this指向,同時也能夠傳入fn的部分和所有參數,並返回一個新函數,新函數能夠傳入參數做爲fn的剩餘參數
function add(a,b,c,d) {
  return a+b+c+d
}
function partail() {
  const params = Array.prototype.slice.call(arguments)
  const fn = params.shift() // 刪除數組第一個元素,返回該元素,改變原數組
  return fn.bind(this, ...params)
  // 該params執行shift後已經改變\
  // params數組展開後的全部成員,都會做爲fn的參數
  // 而且bind返回的新函數還能夠傳參
}

const fn = partail(add, 1, 2) // 固定了 1,2兩個參數
const res = fn(3,4) // 除了固定的參數,剩下的參數在這裏傳入
console.log(res, 'res')


複製代碼

偏函數實現方式2

function add(a,b,c,d) {
  return Array.from(arguments).reduce((total, current) => total + current)
  // 相加實參
  // 由於實參可能大於形參
}
function partialAdd(fn) {
  let paramsFixed = Array.from(arguments).slice(1)
  // 除去fn的剩餘參數
  // 注意:該方法和curry很類似,current第一調用是不須要傳fn參數的,聲明的是空數組,而在partial中須要傳固定的參數
  const paramsMaxLength = fn.length // 形參個數
  function closure() {
    const args = Array.from(arguments)
    paramsFixed = paramsFixed.concat(args)
    if (paramsFixed.length < paramsMaxLength) {
      return closure
    }
    return fn.apply(this, paramsFixed) // 大於等於時
  }
  return closure
}
const fn = partialAdd(add, 2)
const res = fn(3)(4)(5)
console.log(res, 'res') // 14

複製代碼

函數記憶

  • 函數記憶:指將上次的(計算結果)緩存起來,當下次調用時,若是遇到相同的(參數),就直接返回(緩存中的數據)
  • 實現原理:將參數和對應的結果保存在對象中,再次調用時,判斷對象key是否存在,存在返回緩存的值
    • 注意:函數是須要返回值的
function memoize(fn) {
  const cache = {}
  return function() {
    const key = Array.prototype.join.call(arguments, ',')
    if (key in cache) {
      return cache[key]
    }
    return cache[key] = fn.apply(this, arguments)
  }
}
複製代碼

個人簡書:www.jianshu.com/p/eb583d764…webpack

相關文章
相關標籤/搜索