0

0

一起来聊聊JavaScript函数柯里化

WBOY

WBOY

发布时间:2022-03-04 18:16:10

|

2237人浏览过

|

来源于CSDN

转载

本篇文章给大家带来了关于javascript中的相关知识,其中主要介绍了javascript中函数柯里化的相关问题,柯里化是把接受多个参数的函数变换成接受一个单一参数的函数,并且返回接受余下的参数且返回结果的新函数的技术,希望对大家有帮助。

一起来聊聊JavaScript函数柯里化

相关推荐:javascript教程

一、简单了解apply和call

  • call 和 apply 都是为了改变某个函数运行时的 context 即上下文而存在的,换句话说,就是为了改变函数体内部 this 的指向。
  • call 和 apply二者的作用完全一样,只是接受参数的方式不太一样。call其实是apply的一种语法糖。
  • 格式:apply(context,[arguments]),call(context,param1,param2,...)

二、什么是函数柯里化?

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

在这里举个例子,有一个add()函数,它是用来处理我们传给它的参数(param1,params2,…)相加求和的一个函数。

立即学习Java免费学习笔记(深入)”;

// 在这里第一个具有两个参数`x`、`y`的`add(x , y)`函数
function add(x , y){
	return x + y;
}

// 调用`add()`函数,并给定两个参数`4`和`6`
add(4,6);

// 模拟计算机操作,第一步 传入第一个参数 4
function add(4 , y){
	return 4 + y;
}

// 模拟计算机操作,第二步 传入第一个参数 6
function add(4 , 6){
	return 4 + 6;
}

如果我们将add()函数柯里化,是什么样子呢?在这里简单的实现一下:

// 柯里化过的add()函数,可以接受部分参数
function add(x ,y){
	if (typeof y === 'undefined') {
		return function (newy){
			return x + newy;
		}
	}
	// 完整应用
	return x + y;
}

// 测试调用
console.log(typeof add(4)); // [Function]
console.log(add(4)(6)); // 10

// 可以创建保存函数
let saveAdd = add(4);
console.log(saveAdd(6)); // 10

从以上简单柯里化的add()函数可以看出,函数可以接受部分函数,然后返回一个新的函数,使其继续处理剩下的函数。

三、写一个公共的柯里化函数

在这里我们创建一个公共的柯里化函数,那样我们就不必每次写一个函数都要在其内部实现复杂的柯里化过程。

// 定义一个createCurry的函数function createCurry(fn){
	var slice = Array.prototype.slice,
	stored_args = slice.call(arguments,1);
	
	return function () {
		let new_args = slice.call(arguments),
		args = stored_args.concat(new_args);
		return fn.apply(null,args);
	}}

在以上公共的柯里化函数中:

  • arguments,并不是一个真的数组,只是一个具有length属性的对象,所以我们从Array.prototype中借用slice方法帮我们把arguments转为一个真正的数组,方便我们更好的操作。
  • 当我们第一次调用函数createCurry的时候,其中变量stored_args 是保持了除去第一个参数以外的参数,因为第一个参数是我们需要柯里化的函数。
  • 当我们执行createCurry函数中返回的函数时,变量new_args获取参数并转为数组。
  • 内部返回的函数通过闭包访问变量stored_args中存储的值和变量new_args的值合并为一个新的数组,并赋值给变量args
  • 最后调用fn.apply(null,args)方法,执行被柯里化的函数。

现在我们来测试公共的柯里化函数

// 普通函数add()
function add(x , y){
	return x + y;
}

// 柯里化得到一个新的函数
var newAdd = createCurry(add,4);
console.log(newAdd(6)); // 10


//另一种简便方式
console.log(createCurry(add,4)(6));// 10

当然这里并不局限于两个参数的柯里化,也可以多个参数:

阿里云AI平台
阿里云AI平台

阿里云AI平台

下载
// 多个参数的普通函数
function add(a,b,c,d){
	return a + b + c + d;
}

// 柯里化函数得到新函数,多个参数可以随意分割
console.log(createCurry(add,4,5)(5,6)); // 20

// 两步柯里化
let add_one = createCurry(add,5);
console.log(add_one(5,5,5));// 20
let add_two = createCurry(add_one,4,6);
console.log(add_two(6)); // 21

通过以上的例子,我们可以发现一个局限,那就是不管是两个参数还是多个参数,它只能分两步执行,如以下公式:

  • fn(x,y) ==> fn(x)(y);
  • fn(x,y,z,w) ==> fn(x)(y,z,w) || fn(x,y)(z,w)||…

如果我们想更灵活一点:

  • fn(x,y) ==> fn(x)(y);
  • fn(x,y,z) ==> fn(x,y)(z) || fn(x)(y)(z);
  • fn(x,y,z,w) ==> fn(x,y)(z)(w) || fn(x)(y)(z)(w) || …;

我们该怎么实现呢?

四、创建一个灵活的柯里化函数

经过以上练习,我们发现我们创建的柯里化函数存在一定局限性,我们希望函数可以分为多步执行:

// 创建一个可以多步执行的柯里化函数,当参数满足数量时就去执行它:
// 函数公式:fn(x,y,z,w) ==> fn(x)(y)(z)(w);
let createCurry = (fn,...params)=> {
	let args = parsms || [];
	let fnLen = fn.length; // 指定柯里化函数的参数长度
	
	return (...res)=> {
		// 通过作用域链获取上一次的所有参数
		let allArgs = args.slice(0);
		// 深度拷贝闭包共用的args参数,避免后续操作影响(引用类型)
		allArgs.push(...res);
		if(allArgs.length < fnLen){
		   // 当参数数量小于原函数的参数长度时,递归调用createCurry函数
		   return createCurry.call(this,fn,...allArgs);
		}else{
		  // 当参数数量满足时,触发函数执行
		  return fn.apply(this,allArgs);
		}
	}
}


// 多个参数的普通函数
function add(a,b,c,d){
	return a + b + c + d;
}

// 测试柯里化函数

let curryAdd = createCurry(add,1);
console.log(curryAdd(2)(3)(4)); // 10

以上我们已经实现了灵活的柯里化函数,但是这里我们又发现了一个问题:

  • 如果我第一次就把参数全部传入,但是它并没有返回结果,而是一个函数(function)。
  • 只有我们再次将返回的函数调用一次才能返回结果:curryAdd(add,1,2,3,4)();
  • 可能有人说如果是全部传参,就调用原来的add()函数就行了,这也是一种办法;但是我们在这里既然是满足参数数量,对于这种情况我们还是处理一下。

在这里我们只需要在返回函数前做一下判断就行了:

let createCurry = (fn,...params)=> {
	let args = parsms || [];
	let fnLen = fn.length; // 指定柯里化函数的参数长度
	
	if(length === _args.length){
	   // 加入判断,如果第一次参数数量以经足够时就直接调用函数获取结果
           return fn.apply(this,args);
        }
	return (...res)=> {
		let allArgs = args.slice(0);
		allArgs.push(...res);
		if(allArgs.length < fnLen){
		   return createCurry.call(this,fn,...allArgs);
		}else{
		  return fn.apply(this,allArgs);
		}
	}}

以上可以算是完成了一个灵活的柯里化的函数了,但是这里还不算很灵活,因为我们不能控制它什么时候执行,只要参数数量足够它就自动执行。我们希望实现一个可以控制它执行的时机该怎么办呢?

五、写一个可控制的执行时间的柯里化函数

我们这里直接说明一下函数公式:

  • fn(a,b,c) ==> fn(a)(b)(c )();
  • fn(a,b,c) ==> fn(a);fn(b);fn(c );fn();
  • 当我们参数足够时它并不会执行,只有我们再次调用一次函数它才会执行并返回结果。在这里我们在以上例子中加一个小小的条件就可以实现。
// 当参数满足,再次执行时调用函数
let createCurry = (fn,...params)=> {
	let args = parsms || [];
	let fnLen = fn.length; // 指定柯里化函数的参数长度
	
	//当然这里的判断需要注释掉,不然当它第一次参数数量足够时就直接执行结果了
	//if(length === _args.length){
	   // 加入判断,如果第一次参数数量以经足够时就直接调用函数获取结果
           //return fn.apply(this,args);
        //}
	return (...res)=> {
		let allArgs = args.slice(0);
		allArgs.push(...res);
		// 在这里判断输入的参数是否大于0,如果大于0在判断参数数量是否足够,
		// 这里不能用 && ,如果用&& 也是参数数量足够时就执行结果了。
		if(res.length > 0 || allArgs.length < fnLen){
		   return createCurry.call(this,fn,...allArgs);
		}else{
		  return fn.apply(this,allArgs);
		}
	}
}


// 多个参数的普通函数
function add(a,b,c,d){
	return a + b + c + d;
}

// 测试可控制的柯里化函数

let curryAdd = createCurry(add,1);
console.log(curryAdd(2)(3)(4)); // function
console.log(curryAdd(2)(3)(4)()); // 10
console.log(curryAdd(2)(3)()); // 当参数不足够时返回 NaN

相关推荐:javascript学习教程

相关文章

java速学教程(入门到精通)
java速学教程(入门到精通)

java怎么学习?java怎么入门?java在哪学?java怎么学才快?不用担心,这里为大家提供了java速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
TypeScript类型系统进阶与大型前端项目实践
TypeScript类型系统进阶与大型前端项目实践

本专题围绕 TypeScript 在大型前端项目中的应用展开,深入讲解类型系统设计与工程化开发方法。内容包括泛型与高级类型、类型推断机制、声明文件编写、模块化结构设计以及代码规范管理。通过真实项目案例分析,帮助开发者构建类型安全、结构清晰、易维护的前端工程体系,提高团队协作效率与代码质量。

26

2026.03.13

Python异步编程与Asyncio高并发应用实践
Python异步编程与Asyncio高并发应用实践

本专题围绕 Python 异步编程模型展开,深入讲解 Asyncio 框架的核心原理与应用实践。内容包括事件循环机制、协程任务调度、异步 IO 处理以及并发任务管理策略。通过构建高并发网络请求与异步数据处理案例,帮助开发者掌握 Python 在高并发场景中的高效开发方法,并提升系统资源利用率与整体运行性能。

46

2026.03.12

C# ASP.NET Core微服务架构与API网关实践
C# ASP.NET Core微服务架构与API网关实践

本专题围绕 C# 在现代后端架构中的微服务实践展开,系统讲解基于 ASP.NET Core 构建可扩展服务体系的核心方法。内容涵盖服务拆分策略、RESTful API 设计、服务间通信、API 网关统一入口管理以及服务治理机制。通过真实项目案例,帮助开发者掌握构建高可用微服务系统的关键技术,提高系统的可扩展性与维护效率。

178

2026.03.11

Go高并发任务调度与Goroutine池化实践
Go高并发任务调度与Goroutine池化实践

本专题围绕 Go 语言在高并发任务处理场景中的实践展开,系统讲解 Goroutine 调度模型、Channel 通信机制以及并发控制策略。内容包括任务队列设计、Goroutine 池化管理、资源限制控制以及并发任务的性能优化方法。通过实际案例演示,帮助开发者构建稳定高效的 Go 并发任务处理系统,提高系统在高负载环境下的处理能力与稳定性。

51

2026.03.10

Kotlin Android模块化架构与组件化开发实践
Kotlin Android模块化架构与组件化开发实践

本专题围绕 Kotlin 在 Android 应用开发中的架构实践展开,重点讲解模块化设计与组件化开发的实现思路。内容包括项目模块拆分策略、公共组件封装、依赖管理优化、路由通信机制以及大型项目的工程化管理方法。通过真实项目案例分析,帮助开发者构建结构清晰、易扩展且维护成本低的 Android 应用架构体系,提升团队协作效率与项目迭代速度。

92

2026.03.09

JavaScript浏览器渲染机制与前端性能优化实践
JavaScript浏览器渲染机制与前端性能优化实践

本专题围绕 JavaScript 在浏览器中的执行与渲染机制展开,系统讲解 DOM 构建、CSSOM 解析、重排与重绘原理,以及关键渲染路径优化方法。内容涵盖事件循环机制、异步任务调度、资源加载优化、代码拆分与懒加载等性能优化策略。通过真实前端项目案例,帮助开发者理解浏览器底层工作原理,并掌握提升网页加载速度与交互体验的实用技巧。

102

2026.03.06

Rust内存安全机制与所有权模型深度实践
Rust内存安全机制与所有权模型深度实践

本专题围绕 Rust 语言核心特性展开,深入讲解所有权机制、借用规则、生命周期管理以及智能指针等关键概念。通过系统级开发案例,分析内存安全保障原理与零成本抽象优势,并结合并发场景讲解 Send 与 Sync 特性实现机制。帮助开发者真正理解 Rust 的设计哲学,掌握在高性能与安全性并重场景中的工程实践能力。

227

2026.03.05

PHP高性能API设计与Laravel服务架构实践
PHP高性能API设计与Laravel服务架构实践

本专题围绕 PHP 在现代 Web 后端开发中的高性能实践展开,重点讲解基于 Laravel 框架构建可扩展 API 服务的核心方法。内容涵盖路由与中间件机制、服务容器与依赖注入、接口版本管理、缓存策略设计以及队列异步处理方案。同时结合高并发场景,深入分析性能瓶颈定位与优化思路,帮助开发者构建稳定、高效、易维护的 PHP 后端服务体系。

532

2026.03.04

AI安装教程大全
AI安装教程大全

2026最全AI工具安装教程专题:包含各版本AI绘图、AI视频、智能办公软件的本地化部署手册。全篇零基础友好,附带最新模型下载地址、一键安装脚本及常见报错修复方案。每日更新,收藏这一篇就够了,让AI安装不再报错!

171

2026.03.04

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
React 教程
React 教程

共58课时 | 6万人学习

TypeScript 教程
TypeScript 教程

共19课时 | 3.4万人学习

Bootstrap 5教程
Bootstrap 5教程

共46课时 | 3.6万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号