当前位置: 首页 > news >正文

JavaScript 函数式编程技巧 - 反柯里化

作为函数式编程语言,JS带来了很多语言上的有趣特性,比如柯里化和反柯里化。

可以对照另外一篇介绍 JS 柯里化 的文章一起看~

1. 简介

柯里化,是固定部分参数,返回一个接受剩余参数的函数,也称为部分计算函数,目的是为了缩小适用范围,创建一个针对性更强的函数。核心思想是把多参数传入的函数拆成单参数(或部分)函数,内部再返回调用下一个单参数(或部分)函数,依次处理剩余的参数。

反柯里化,从字面讲,意义和用法跟函数柯里化相比正好相反,扩大适用范围,创建一个应用范围更广的函数。使本来只有特定对象才适用的方法,扩展到更多的对象。

2. 实现

先来看看反柯里化的通用实现吧~

Function.prototype.unCurrying = function() {
  const self = this
  return function(...rest) {
    return Function.prototype.call.apply(self, rest)
  }
}
复制代码

解释下:

  1. 为Function原型添加uncurrying方法,并在执行的时候保存执行unCurrying的方法到self
  2. 借用apply把要借用的函数作为this环境赋给call,并传入之后的形参作为参数执行

还有一个实现:

Function.prototype.unCurrying = function() {
  return this.call.bind(this)
}
复制代码

如果你觉得把函数放在Function.prototype上不太好,也可以这样:

function unCurrying(fn) {
  return function(tar, ...argu) {
    return fn.apply(tar, argu)
  }
}
复制代码

3. 使用

3.1 简单使用

unCurrying通用实现简单的实用一下试试:

Function.prototype.unCurrying = function() {
  const self = this                        // 这里的self就是Array.prototype.push方法
  return function(...rest) {              // rest为传入的两层参数[[1,2,3],4]
    return Function.prototype.call.apply(self, rest)
  }
}
const push = Array.prototype.push.unCurrying()

~function(...rest) {       // rest:[1,2,3]
  push(rest, 4)
  console.log(rest)    // [1, 2, 3, 4]
}(1, 2, 3)
复制代码

3.2 借用其他方法

反柯里化其实反映的是一种思想,即扩大方法的适用范围,仍然调用刚刚的通用unCurrying方法借用push方法:

const push = Array.prototype.push.unCurrying()

const obj = { a: '嘻嘻' }
push(obj, '呵呵', '哈哈', '嘿嘿')
console.log(obj)                    // { '0': '呵呵', '1': '哈哈', '2': '嘿嘿', a: '嘻嘻', length: 3 }
复制代码

相当于obj.push(...),obj不仅多了类似于数组一样以数字作为索引的属性,还多了个类似于数组的length属性,让引擎自动管理数组成员和length属性;(文后有V8引擎实现push方法的源码) 这样一个数组的push方法就被借用出来,可以应用于任何其他对象了。

只要是方法,unCurrying就可以借用,call方法也可以:

var call = Function.prototype.call.unCurrying();
function $(id) {
    return this.getElementById(id);
}
call($, document, 'demo')            // #demo 元素
复制代码

相当于document.$('demo'),成功的借用了call方法,当然可以把document改成你希望作为this绑定到$的任何对象,比如{ getElementById:T=>console.log(T+'呃') } // demo呃

3.3 借用自己

unCurrying本身也是方法,也可以借用自己...-。-

const unCurrying = Function.prototype.unCurrying.unCurrying()
const map = unCurrying(Array.prototype.map)
map({ 0: 4, 1: 'a', 2: null, length: 3 }, n => n + n)                    // [8, "aa", 0]
复制代码

神奇吧~

4. 总结

简单说,函数柯里化就是对高阶函数的降阶处理,缩小适用范围,创建一个针对性更强的函数。举栗子:

function(arg1,arg2)        // => function(arg1)(arg2)
function(arg1,arg2,arg3)        // => function(arg1)(arg2)(arg3)
function(arg1,arg2,arg3,arg4)        // => function(arg1)(arg2)(arg3)(arg4)
function(arg1,arg2,…,argn)        // => function(arg1)(arg2)…(argn)
复制代码

而反柯里化就是反过来,增加适用范围,让方法使用场景更大。使用unCurrying, 可以把原生方法借出来,让任何对象拥有原生对象的方法。举个栗子:

obj.func(arg1, arg2)        // => func(obj, arg1, arg2)
复制代码

也可以这样理解: 柯里化是在运算前提前传参,可以传递多个参数; 反柯里化是延迟传参,在运算时把原来已经固定的参数或者this上下文等当作参数延迟到未来传递。


附:

V8引擎中Array.prototype.push方法源码实现:

function ArrayPush() {
    var n = TO_UINT32(this.length);
    var m = %_ArgumentsLength();
    for (var i = 0; i < m; i++) {
        this[i + n] = %_Arguments(i);        // 属性拷贝
        this.length = n + m;                    // 修正length
        return this.length;
    }
}
复制代码

网上的帖子大多深浅不一,甚至有些前后矛盾,在下的文章都是学习过程中的总结,如果发现错误,欢迎留言指出~

参考:

  1. JS 柯里化
  2. 前端开发者进阶之函数反柯里化unCurrying
  3. JavaScript中有趣的反柯里化
  4. js柯里化适用场景,优缺点分别是什么,还有个反柯里化?
  5. JS进阶篇--JS中的反柯里化( uncurrying)

PS:欢迎大家关注我的公众号【前端下午茶】,一起加油吧~

相关文章:

  • 线程池没你想的那么简单
  • 第九周总结
  • 聊聊Dubbo(九):核心源码-服务端启动流程2
  • Nginx和Apache
  • 重学ES6 Set 数据结构(2)
  • webpack treeshking的三个要点
  • 安装固态硬盘,小米笔记本13.3
  • 利用requests库访问360主页20次
  • Linux入门学习
  • 前端之移动页面布局
  • 获取html资源
  • 专为SaaS而生的PaaS平台!
  • 福尔摩斯的约会
  • DRDS 数据恢复重磅发布,全方位保障您的数据安全
  • 系列二、spirngMVC view和controller参数相互传递
  • “Material Design”设计规范在 ComponentOne For WinForm 的全新尝试!
  • Essential Studio for ASP.NET Web Forms 2017 v2,新增自定义树形网格工具栏
  • HashMap剖析之内部结构
  • HTTP中的ETag在移动客户端的应用
  • spring boot 整合mybatis 无法输出sql的问题
  • Spring框架之我见(三)——IOC、AOP
  • VirtualBox 安装过程中出现 Running VMs found 错误的解决过程
  • 关于Flux,Vuex,Redux的思考
  • 规范化安全开发 KOA 手脚架
  • 简单易用的leetcode开发测试工具(npm)
  • 容器化应用: 在阿里云搭建多节点 Openshift 集群
  • 探索 JS 中的模块化
  • 推荐一款sublime text 3 支持JSX和es201x 代码格式化的插件
  • 网页视频流m3u8/ts视频下载
  • Semaphore
  • 测评:对于写作的人来说,Markdown是你最好的朋友 ...
  • #100天计划# 2013年9月29日
  • #13 yum、编译安装与sed命令的使用
  • (13):Silverlight 2 数据与通信之WebRequest
  • (Python) SOAP Web Service (HTTP POST)
  • (Python第六天)文件处理
  • (读书笔记)Javascript高级程序设计---ECMAScript基础
  • (附源码)springboot高校宿舍交电费系统 毕业设计031552
  • (更新)A股上市公司华证ESG评级得分稳健性校验ESG得分年均值中位数(2009-2023年.12)
  • (排序详解之 堆排序)
  • (顺序)容器的好伴侣 --- 容器适配器
  • (转) Android中ViewStub组件使用
  • (转)Linux整合apache和tomcat构建Web服务器
  • (转)大型网站架构演变和知识体系
  • .net core使用RPC方式进行高效的HTTP服务访问
  • .NET Micro Framework初体验
  • .NET 跨平台图形库 SkiaSharp 基础应用
  • .NET 应用启用与禁用自动生成绑定重定向 (bindingRedirect),解决不同版本 dll 的依赖问题
  • .NET 中 GetProcess 相关方法的性能
  • .NET/C# 在 64 位进程中读取 32 位进程重定向后的注册表
  • .NET开发者必备的11款免费工具
  • .NET正则基础之——正则委托
  • @cacheable 是否缓存成功_Spring Cache缓存注解
  • []sim300 GPRS数据收发程序
  • [Angular] 笔记 20:NgContent