javascript面试:函数柯里化

函数柯里化

概念

柯里化函数是指将原本接收多个参数的函数变为直接收单一参数的函数,并且该函数会返回一个函数,这个返回函数接收余下的参数。柯里化是一种高阶函数,实际工作中会经常遇到。

举个简单的例子,例如我们实现一个传入三个参数的求和函数,原本我们是这么写的

function add(a,b,c){
    
    
	return a+b+c;
}

是这么传参数的

let result = add(1,2,3);
console.log(result);

经过函数柯里化之后,变为这样:

let result = add(1)(2)(3)

为什么要柯里化?

1、让函数功能单一。在函数式编程中,我们希望一个函数处理的问题尽可能的单一,而不是将一大堆的处理过程交给一个函数来处理,通过柯里化,我们就可以将每次传入的参数在单一的函数中进行处理,处理完成后就在下一个函数中在使用处理后的结果。

2、函数逻辑复用。

使用场景

  • 延迟执行
    这里的延迟执行是指只有把参数全部传齐了才会执行
/**
 * @param fn 传入的需要进行柯里化的函数
 * @return 返回柯里化后的函数
 */
function curry(fn) {
    
    
	// 返回柯里化后的函数 args是柯里化函数的参数
    return function curried(...args) {
    
    
		// 当柯里化后函数参数的个数大于原本函数的个数时才会执行原函数的功能
        if (args.length >= fn.length) {
    
    
            return fn.apply(this, args)
        } else {
    
    
        	// 没达到个数时则返回一个新的函数,继续接收参数args_2
            return function (...args_2) {
    
    
            	// 接收到参数后,继续递归调用curried函数,并且将之前的参数和新传入的参数重新拼接成一个数组
                return curried.apply(this, args.concat(args_2))
            }
        }
    }
}

// 加法运算
function add(a, b, c) {
    
    
    return a+b+c;
}

// 函数柯里化,返回柯里化后的函数
let curriedAdd = curry(add)

console.log(curriedAdd(1)); // [Function (anonymous)] 因为只传入了一个参数,并没有达到原函数参数个数的标准,所以返回函数

// 当传入的参数最终个数大于等于原函数参数时,原函数才会执行
console.log(curriedAdd(1)(2)(3)); // 6
console.log(curriedAdd(1,2)(3)); // 6
console.log(curriedAdd(1,2,3)); // 6

二、通过柯里化实现参数复用

// 原本我们需要通过传入三个参数来实现链接的拼接
function webConcat(protocol, hostname, pathname) {
    
    
    return `${
      
      protocol}${
      
      hostname}${
      
      pathname}`
}
// 当如果需要拼接很多个连接时,就会重复很多操作,例如第一个参数三者都重复了,明显可以复用
console.log(webConcat("https://", "www.baidu.com", "/direction")); // https://www.baidu.com/direction
console.log(webConcat("https://", "www.tentent.com", "/direction")); // https://www.tencent.com/direction
console.log(webConcat("https://", "www.alibaba.com", "/direction")); // https://www.alibaba.com/direction

// 通过柯里化实现参数复用
function first_curry_example(protocol) {
    
    
    return function (hostname, pathname) {
    
    
        return `${
      
      protocol}${
      
      hostname}${
      
      pathname}`
    }
}

// 传入一个参数,这个参数是重复次数比较多的,这里直接按照固定值进行处理
let uri_http = first_curry_example("http://")

// 接下来只需要传入剩下的参数
let uri1 = uri_http("www.baidu.com", "direction")
let uri2 = uri_http("www.tencent.com", "direction")
let uri3 = uri_http("www.alibaba.com", "direction")

console.log(uri1, uri2, uri3);

三、柯里化对浏览器兼容性的检测

// 例如这里实现对浏览器中addEventListener(Chrome等浏览器的)事件和attachEvent事件(IE的)兼容性的判断
// 这里只写个大致原理,因为涉及到事件,所以需要到html中去检测
const whichEvent = (function () {
    
    
    // 先判断是否支持addEventListener
    if (window.addEventListener) {
    
    
        /**
         * @description: 返回一个回调函数
         * @param element 触发事件的节点对象
         * @param type 事件类型
         * @param listener 回调事件
         * @param useCapture 事件冒泡/捕获
         * @return 
         */
        return function (element, type, listener, useCapture) {
    
    
            // 调用原生的addEventListener事件
            element.addEventListener(type, function (e) {
    
    
                // 指定节点对象出发函数回调
                listener.call(element, e)
            }, useCapture)
        }
    }
    // 判断是否支持attachEvent事件
    else if (window.attachEvent) {
    
    
        return function (element, type, handler) {
    
    
            element.attachEvent("on" + type, function (e) {
    
    
                handler.call(element, e)
            })
        }
    }
})()

// notes: 2-1使2用立即执行函数的原因:为了让页面加载后立即进行判断,并且立即执行函数中即便出发了多次事件,if也只会进行一次判断

猜你喜欢

转载自blog.csdn.net/lalala_dxf/article/details/131625240