关于前端:JavaScript函数柯里化

JavaScript函数柯里化

一、定义:

柯里化(Currying)是把承受多个参数的函数变换成承受一个繁多参数(最后函数的第一个参数)的函数,并且返回承受余下的参数且返回后果的新函数的技术。

通过一个简略的例子解释一下:

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

add(1, 2); // 3

将函数add转化为柯里化函数_add

function _add(a){
    return function(b){
        return a + b
    }
}

_add(1)(2);  // 3

函数add和函数_add是等价的。

_add可能解决add的所有残余参数,因而柯里化也被称为局部求值

实际上就是把add函数的a,b两个参数变成了先用一个函数接管a而后返回一个函数去解决b参数。

当初思路应该就比拟清晰了:只传递给函数一部分参数来调用,让它返回一个函数去解决剩下的参数。

二、柯里化函数的作用

1、参数复用

案例:拼接地址

依照一般思路去拼接一个地址

// 拼接地址
function getUrl(protocol, hostname, pathname) {
    return `${protocol}${hostname}${pathname}`;
}

const url1 = getUrl('https://', 'www.baidu.com', '/hasa');
const url2 = getUrl('https://', 'www.baidu.com', '/saandsa');
const url3 = getUrl('https://', 'www.baidu.com', '/hasak');

console.log(url1, url2, url3)

每次调用getUrl参数的时候都要反复的传入参数’https://’, ‘www.baidu.com’。

柯里化封装之后:

function curry(protocol) {
    return function (hostname, pathname) {
        return `${protocol}${hostname}${pathname}`;
    }
}

const url_curry = curry('https://');

const url1 = url_curry('www.baidu.com', '/hasa');
const url2 = url_curry('www.baidu.com', '/saandsa');
const url3 = url_curry('www.baidu.com', '/hasak');

console.log(url1, url2, url3)

很显著,通过柯里化封装之后,之后再进行地址拼接的时候,缩小了参数个数,升高了代码反复率。

2、提前确认

案例:兼容IE浏览器事件的监听办法(IE is dead

传统的办法:

/*
* @param    element       Object      DOM元素对象
* @param    type          String      事件类型
* @param    listener      Function    事件处理函数
* @param    useCapture    Boolean     是否捕捉
*/
var addEvent = function (element, type, listener, useCapture) {
    if (window.addEventListener) {
        element.addEventListener(type, function (e) {
            listener.call(element, e)
        }, useCapture)
    } else {
        element.attachEvent('on' + type, function (e) {
            listener.call(element, e)
        })
    }
}

缺点就是,每次对DOM元素进行事件绑定时都须要从新进行判断,其实对于事件监听网页一公布浏览器曾经确定了,就能够通晓浏览器到底是须要哪一种监听形式。所以咱们能够让判断只执行一次。

柯里化封装之后:

var curEvent = (function () {
    if (window.addEventListener) {
        return function (element, type, listener, useCapture) { // return funtion
            element.addEventListener(type, function () {
                listener.call(element)
            }, useCapture)
        }
    } else {
        return function (element, type, listener) {
            element.attachEvent('on' + type, function () {
                listener.call(element)
            })
        }
    }
})

var addEvent = curEvent();
addEvent(element, "click", listener)

立刻执行函数,在触发屡次事件也仍旧只会触发一次if条件判断。

3、提早执行

案例:钓鱼统计分量

传统的办法:

let fishWeight = 0;
const addWeight = function(weight) {
    fishWeight += weight;
};

addWeight(2.3);
addWeight(6.5);
addWeight(1.2);
addWeight(2.5);

console.log(fishWeight);  

每次执行addWeight办法时,都做了一次鱼的体重的加和。

柯里化封装后:

function curryWeight(fn) {
    let _fishWeight = [];
    return function () {
        if (arguments.length === 0) {
            return fn.apply(null, _fishWeight);
        } else {
            _fishWeight = _fishWeight.concat([...arguments]);
        }
    }
}

function addWeight() {
    let fishWeight = 0;
    for (let i = 0, len = arguments.length; i < len; i++) {
        fishWeight += arguments[i];
    }
    return fishWeight;
}

const _addWeight = curryWeight(addWeight);
_addWeight(6.5);
_addWeight(1.2);
_addWeight(2.3);
_addWeight(2.5);
console.log(_addWeight())

在执行_addWeight办法时,并没有做鱼的体重的加和,之后在最初一次执行_addWeight()时,才做了加和。做到了提早执行addWeight办法的成果。

三、柯里化函数的实现

function curry() {
    let args = [...arguments];
    let inner = function () {
        args.push(...arguments);
        return inner;
    }
    
    // 核心内容:隐式转换,调用了外部的toString
    inner.toString = function () {
        return args.reduce(function (pre, next) {
            return pre + next;
        })
    }
    return inner;
}

const result = curry(1)(2);
console.log(Number(result));

四、柯里化总结

函数的柯里化,须要依赖参数以及递归,通过拆分参数的形式,来调用一个多参数的函数办法,以达到缩小代码冗余,减少可读性的目标。

性能方面:

  • 存取arguments对象通常要比存取命名参数要慢一点;
  • 一些老版本的浏览器在arguments.length的实现上是相当慢的;
  • 应用fn.apply( … ) 和 fn.call( … )通常比间接调用fn( … ) 略微慢点;
  • 创立大量嵌套作用域和闭包函数会带来花销,无论是在内存还是速度上。

利用场景:

  1. 缩小反复传递不变的局部参数;
  2. 将柯里化后的callback参数传递给其余函数。

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理