函数柯里化

函数柯里化

一种函数式编程的思想,柯里化是编程语言中的一个通用的概念,是指把接收多个参数的函数变换成接收单一参数的函数,嵌套返回直到所有参数都被使用并返回最终结果。更简单地说,柯里化是一个函数变换的过程,是将函数从调用方式:f(a,b,c)变换成调用方式:f(a)(b)(c)的过程。柯里化不会调用函数,它只是对函数进行转换。

柯里化应用及特点

参数复用:当在多次调用同一个函数,并且传递的参数绝大多数是相同的,那么就可以用柯里化进行函数的复用,如示例一
提前返回:多次调用内部判断,可以直接把第一次判断的结果返回外部接收
延迟执行:避免重复的去执行程序,等真正需要结果的时候再执行

示例一:(参数复用)

function url_currying(protocol) {
    return function(hostname, pathname) {
    	return `${protocol}${hostname}${pathname}`
    }
}
const url_https = url_currying('https://')
console.log(url_https('domain.com', '/dashboard')) // "https://domain.com/dashboard"

示例二:(提前返回)

const addEvent = (function() {
    if(window.addEventListener) {
       /**
        * element: 需要添加事件监听的元素
        * type: 为元素添加什么类型的事件
        * listener: 执行的回调函数
        * useCapture: 要进行事件冒泡或者事件捕获的选择
        */ 
        return function(element, type, listener, useCapture) {
            element.addEventListener(type, function(e){
              listener.call(element, e) // 用 call 进行 this 的绑定
            }, useCapture)
        }
    } else if(window.attachEvent) {
        // IE(IE9 之前) 支持的是 attachEvent 方法
        // 不需要第四个参数,因为 IE 支持的是事件冒泡
    	return function(element, type, listener) {
            element.attachEvent('on'+type, function(e) {
              listener.call(element, e)
            })
        }
    }
})()

// 测试
let button = document.querySelector('button');
addEvent(button, 'click', (e) => {console.log('点击了 div');}, true);

示例三

/**
 * 实现一个 add 方法
 * add(1, 2, 3)
 * add(1, 2)(3)
 * add(1)(2)(3)(4)(5)
 */ 
function add() {
  // 第一次执行时,arguments 对象转化为数组
  let args = Array.prototype.slice.call( arguments )

  // 内部声明一个函数,利用闭包的特性,保存args,并且收集所有的参数值
  let next = function() {
    args.push( ...arguments )
    return next
  }

  // 当返回值是一个函数时,自动隐士转换为字符串
  // 利用隐士转换的规则,修改toString方法的内部逻辑
  next.toString = function() {
    return args.reduce(function(total, current) { 
      return total + current
    }, 0)
  }
  return next
}
console.log( add(1)(2, 5)(3)(4) )         // ƒ () { args.push( ...arguments ) return next }
console.log( typeof add(1)(2, 5)(3)(4) )  // function
console.log( +add(1)(2, 5)(3)(4) )        // 15


/**
 * 重写函数 fn 的 toString 方法能够改变函数的隐式转换结果
 * 重写函数 fn 的 valueOf 方法也能够改变函数的隐式转换结果
 * 同时重写函数的toString方法与valueOf方法时,最终的结果会取valueOf方法的返回结果
 */   
function fn() { return 20; }
fn.valueOf = function() { return 60 }
fn.toString = function() { return 30 }
console.log( fn + 10 ); // 70

currying 实现

// currying 原理
// 内部返回一个函数
// 收集参数
// 判断 fn.length 原函数的形参 与 args.length 的长度
// args.length >= fn.length 执行返回
// args.length < fn.length 递归执行 next
function currying(fn) {
  return function next() {
    let args = Array.prototype.slice.call(arguments)
    if(args.length >= fn.length) {
      return fn.call(this, ...args)
    }
    return function(...arg) {
      return next(...args, ...arg)
    }
  }
}
// 测试
function addNum(a, b, c) {
  return a + b + c;
}
const addCurry = currying(addNum);
console.log( addCurry(1)(2)(3) );  // 6
console.log( addCurry(1) );     // ƒ (...arg) { return next(...args, ...arg) }
console.log( addCurry(1)(2) );  // ƒ (...arg) { return next(...args, ...arg) }
console.log( addCurry(1)(2, 3) ); // 6
console.log( addCurry(1, 2)(3) ); // 6
console.log( addCurry(1, 2, 3) ); // 6

参考文献

posted @ 2022-01-05 10:38  清水渡白吟堤你如风  阅读(453)  评论(0编辑  收藏  举报