函数柯里化的意义_函数柯里化是什么?

理解函数柯里化:概念、实现与应用
本文介绍了函数柯里化的基本概念,解释了为何需要柯里化,展示了柯里化的实现方式和特点,并探讨了其在浏览器事件绑定兼容处理等方面的应用。柯里化提供了参数复用、延迟执行等优势,但也存在性能损耗的问题。通过柯里化,开发者可以提升代码的灵活性和自由度。

点击上方蓝色字体轻松关注

前言

在计算机科学中,柯里化(Currying),是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。

为什么会有函数柯里化呢?

Currying 的重要意义在于可以把函数完全变成「接受一个参数;返回一个值」的固定形式,这样对于讨论和优化会更加方便。将关注的重点聚焦到函数本身,而不因冗余的数据参数分散注意力。话不多说开始撸

函数柯里化

什么是函数柯里化

函数柯里化在JavaScript中其实是高阶函数的一种应用,先来看一下高级函数的定义.

满足下列条件之一的函数就可以称为高阶函数。1、函数作为参数被传递;2、函数作为返回值输出。

再理解一下函数柯里化

函数柯里化又称部分求值。一个柯里化的函数首先会接受一些参数,接受了这些参数之后,该函数并不会立即求值,而是继续返回另外一个函数,刚才传入的参数在函数形成的闭包中被保存起来。待到函数被真正需要求值的时候,之前传入的所有参数都会被一次性用于求值。

先上几个公式(左边是普通函数,右边就是转化后柯里化函数支持的调用方式)

// 公式类型一fn(a,b,c,d) => fn(a)(b)(c)(d);fn(a,b,c,d) => fn(a, b)(c)(d);fn(a,b,c,d) => fn(a)(b,c,d);// 公式类型二fn(a,b,c,d) => fn(a)(b)(c)(d)();fn(a,b,c,d) => fn(a);fn(b);fn(c);fn(d);fn();

两种公式类型的区别 —— 函数触发执行的机制不同:

  • 公式一当传入参数等于函数参数数量时开始执行
  • 公式二当没有参数传入时(且参数数量满足)开始执行

通过公式,我们先来理解这行代码 fn(a)(b)(c)(d), 执行 fn(a) 时返回的是一个函数,并且支持传参。何时返回的是值而不是函数的触发机制控制权在我们手里,我们可以为函数制定不同的触发机制。

普通的函数调用,一次性传入参数就执行。而通过柯里化,它可以帮我们实现函数部分参数传入执行,这就是函数柯里化的特点:"延迟执行和部分求值"

"函数柯里化:指封装一个函数,接收原始函数作为参数传入,并返回一个能够接收并处理剩余参数的函数"

尝一个栗子

普通实现fn(a,b,c, d)

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

最简柯里化实现fn(a,b,c, d)

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

分析代码知识点:

  1. 函数作为返回值返回,闭包形成,外部环境可访问函数内部作用域
  2. 子函数可访问父函数的作用域,作用域由内而外的作用域链查找规则,作用域嵌套形成
  3. 在函数参数数量不满足时,返回一个函数(该函数可接收并处理剩余参数)
  4. 当函数数量满足我们的触发机制(可自由制定),触发原始函数执行

好看点的柯里化实现

公式1

function add(a, b, c, d) {    return a + b + c + d;};const createCurry = (fn, ...args) => {    let _args = args || [];    let length = fn.length; // fn.length代码函数参数数量    return (...rest) => {        let _allArgs = _args.slice(0);          // 深拷贝闭包共用对象_args,避免后续操作影响(引用类型)        _allArgs.push(...rest);        if (_allArgs.length < length) {            // 参数数量不满足原始函数数量,返回curry函数            return createCurry.call(this, fn, ..._allArgs);        } else {            // 参数数量满足原始函数数量,触发执行            return fn.apply(this, _allArgs);        }    }}const curryAdd = createCurry(add, 2);let sum = curryAdd(3)(4)(5);    // 14

公式2

function add(a, b, c, d) {    return a + b + c + d;};const createCurry = (fn, ...args) => {    let all = args || [];    let length = fn.length;    return (...rest) => {        let _allArgs = all.slice(0);        _allArgs.push(...rest);        if (rest.length > 0 || _allArgs.length < length) {            // 调用时参数不为空或存储的参数不满足原始函数参数数量时,返回curry函数            return createCurry.call(this, fn, ..._allArgs);        } else {            // 调用参数为空(),且参数数量满足时,触发执行            return fn.apply(this, _allArgs);        }    }}const curryAdd = createCurry(add, 2);let sum = curryAdd(3)(4)(5)();  // 14

为实现公式中不同的两种调用公式,两个createCurry方法制定了两种不同的触发机制。记住一个点,函数触发机制可根据需求自行制定。

柯里化的特点

  1. 参数复用(固定易变因素)
  2. 延迟执行
  3. 提前返回

柯里化的缺点

柯里化是牺牲了部分性能来实现的,可能带来的性能损耗:

  1. 存取 arguments 对象要比存取命名参数要慢一些
  2. 老版本浏览器在 arguments.lengths 的实现相当慢(新版本浏览器忽略)
  3. fn.apply() 和 fn.call() 要比直接调用 fn() 慢
  4. 大量嵌套的作用域和闭包会带来开销,影响内存占用和作用域链查找速度

柯里化的应用

  1. 利用柯里化制定约束条件,管控触发机制
  2. 处理浏览器兼容(参数复用实现一次性判断)
  3. 函数节流防抖(延迟执行)
  4. ES5前bind方法的实现

一个应用例子:浏览器事件绑定的兼容处理

// 普通事件绑定函数var addEvent = function(ele, type, fn, isCapture) {    if(window.addEventListener) {        ele.addEventListener(type, fn, isCapture)    } else if(window.attachEvent) {        ele.attachEvent("on" + type, fn)    }}// 弊端:每次调用addEvent都会进行判断// 柯里化事件绑定函数var addEvent = (function() {    if(window.addEventListener) {        return function(ele, type, fn, isCapture) {            ele.addEventListener(type, fn, isCapture)        }    } else if(window.attachEvent) {        return function(ele, type, fn) {             ele.attachEvent("on" + type, fn)        }    }})()// 优势:判断只执行一次,通过闭包保留了父级作用域的判断结果

反思:为何要使用柯里化

函数柯里化是函数编程中的一个重要的基础,它为我们提供了一种编程的思维方式。显然,它让我们的函数处理变得复杂,代码调用方式并不直观,还加入了闭包,多层作用域嵌套,会有一些性能上的影响。

但在一些复杂的业务逻辑封装中,函数柯里化能够为我们提供更好的应对方案,让我们的函数更具自由度和灵活性。

实际开发中,如果你的逻辑处理相对复杂,不妨换个思维,用函数柯里化来实现,技能包不嫌多。

后记

以上就是小编今天给大家分享的内容,喜欢的小伙伴记得收藏转发,点击在看推荐给更多的小伙伴。

大前端实践者,专注于大前端技术领域,分享前端系统架构,框架实现原理,最新最高效的技术实践!

关注大前端实践者,学习前端不迷路,欢迎多多留言交流...

大前端实践者

646c34c1ed7d5f3b2ee243b688ba6b5c.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值