js函數柯里化的理解

函數柯里化理解示例

// 求和
let add = function (a, b, c) {
  return a + b + c
}

{
  /* 簡單版curry函數示例 */

  // 對求和函數做curry化
  let f1 = curry(add, 1, 2, 3)
  console.log('簡單版', f1())// 6

  // 對求和函數做curry化
  let f2 = curry(add, 1, 2)
  console.log('簡單版', f2(3))// 6

  // 對求和函數做curry化
  let f3 = curry(add)
  console.log('簡單版', f3(1, 2, 3))// 6

  // 但是簡單版curry函數多次調用會報錯,如下:
  // console.log('簡單版',f3(1)(2)(3))// Uncaught TypeError: f3(...) is not a function

  // 簡單版(只能調用1次)
  function curry(fn) {
    // 緩存除函數fn之外的所有參數
    let args = Array.prototype.slice.call(arguments, 1)
    // 閉包
    return function () {
      // 連接已緩存的老的參數和新傳入的參數(即把每次傳入的參數全部先保存下來,但是並不執行)
      let newArgs = args.concat(Array.from(arguments))
      // 調用
      return fn.apply(this, newArgs)
    }
  }
}

{
  /* 複雜版curry函數示例 */

  // 對求和函數做curry化
  let f1 = curry(add, 1, 2, 3)
  console.log('複雜版', f1())// 6

  // 對求和函數做curry化
  let f2 = curry(add, 1, 2)
  console.log('複雜版', f2(3))// 6

  // 對求和函數做curry化
  let f3 = curry(add)
  console.log('複雜版', f3(1, 2, 3))// 6

  // 複雜版curry函數可以多次調用,如下:
  console.log('複雜版', f3(1)(2)(3))// 6
  console.log('複雜版', f3(1, 2)(3))// 6
  console.log('複雜版', f3(1)(2, 3))// 6

  // 複雜版(每次可傳入不定數量的參數,當所傳參數總數不少於函數的形參總數時,纔會執行)
  function curry(fn) {
    // 閉包
    // 緩存除函數fn之外的所有參數
    let args = Array.prototype.slice.call(arguments, 1)
    return function () {
      // 連接已緩存的老的參數和新傳入的參數(即把每次傳入的參數全部先保存下來,但是並不執行)
      let newArgs = args.concat(Array.from(arguments))
      if (newArgs.length < fn.length) {// 累積的參數總數少於fn形參總數
        // 遞歸傳入fn和已累積的參數
        return curry.call(this, fn, ...newArgs)
      } else {
        // 調用
        return fn.apply(this, newArgs)
      }
    }
  }
}

前端開發者進階之函數柯里化Currying
JavaScript專題之函數柯里化

js bind函數理解示例(使用了柯里化)

// 全局變量
a = 1

// 求和
let add = function (b, c) {
  return this.a + b + c
}

{
  /* 作爲普通函數直接調用 */
  // 此時this指向window,window.a是1
  console.log(add(2, 3))// 6
}

{
  /* 簡單版bind實現,不具備curry化效果(不支持在傳入對象的同時傳入其他參數) */
  Function.prototype.bind = function (obj) {
    // 緩存Function構造函數的this(表達式函數聲明時相當於new Function...)
    const self = this
    // 閉包
    return function () {
      // 調用
      return self.apply(obj, arguments)
    }
  }

  let f1 = add.bind({ a: 0 })
  console.log(f1(2, 3))// 5

  // 但是簡單版bind實現不支持對象和其他參數同時傳入,如下:
  let f2 = add.bind({ a: 0 }, 2, 3)
  // 因爲上一行代碼傳入的2、3並沒有接收到,所以求和後返回NaN
  console.log(f2())// NaN
}

{
  /* 複雜版bind實現,具備部分curry化效果(之所以說部分,是因爲它支持在傳入對象的同時可以其他參數,但是不支持多次調用) */
  Function.prototype.bind = function (obj) {
    // 緩存Function構造函數的this(表達式函數聲明時相當於new Function...)
    const self = this
    // 緩存除函數fn之外的所有參數
    let args = Array.prototype.slice.call(arguments, 1)
    // 閉包
    return function () {
      // 連接已緩存的老的參數和新傳入的參數(即把每次傳入的參數全部先保存下來,但是並不執行)
      let newArgs = args.concat(Array.from(arguments))
      // 調用
      return self.apply(obj, newArgs)
    }
  }

  let f1 = add.bind({ a: 0 })
  console.log(f1(2, 3))// 5

  // 複雜版bind實現支持對象和其他參數同時傳入,如下:
  let f2 = add.bind({ a: 0 }, 2, 3)
  console.log(f2())// 5

  // 但是複雜版bind實現多次調用會報錯,如下:
  let f3 = add.bind({ a: 0 })
  // console.log(f3(2)(3))// Uncaught TypeError: f3(...) is not a function
}

JS bind() 方法之人和狗的故事
JS中的bind的實現以及使用

裝飾者模式理解示例(使用了柯里化)

大家看以下代碼,是不是思路都是想通的,詳細的大家就自行拓展吧

// 在之前執行
Function.prototype.before = function (fn) {
  let self = this
  return function () {
    fn.apply(this, arguments)
    return self.apply(this, arguments)
  }
}
// 在之後執行
Function.prototype.after = function (fn) {
  let self = this
  return function () {
    let res = self.apply(this, arguments)
    fn.apply(this, arguments)
    return res
  }
}
function foo(params) {
  console.log(params, 1)
  return 22
}
// 鉤子可以共享參數和返回值
console.log(foo.before((params) => {
  console.log(params, 2)
}).after((params) => {
  console.log(params, 3)
})({ a: 1 }))
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章