JavaScript函数式真正的浅析
0x00 入门的导语(废话)
最近两年你要说函数式编程不火的话, 那是不可能的, 是人都知道函数式编程很火.为什么函数式编程会火呢, 在于它的思想, 很强大, 很强势!尤其是前端的redux更是在reducer上完全使用纯函数, 函数式的好处渐渐被发掘出来, 笔者最近看了一些函数式方面的东东, 现在发出来给大家学习学习, 顺便我也学习学习怎么写文章... :P
常用的函数式库:
ramda 设计很棒的一个库 lodash 比较常用的一个库 underscore 应该也不错的一个库0x01 纯函数
定义: 相同输入一定得到相同输出且运行过程中不修改,不读取外部环境的变量的函数
说出来肯定不好理解, 还是要看看代码. 就好像你不看国足比赛永远不知道国足为什么会输给月薪几百块的叙利亚.
// Array.slice 对于固定输入一定是固定输出, 且不依赖外部变量, 啥? 依赖了arr变量吗? // 其实这种写法和Array.prototype.slice(arr, 0, 3); 是一样的. 这样就理解了, // 你还学到一个东西 Array.slice是不会修改原数组滴! var arr = [1,2,3,4,5]; arr.slice(0,3); // Array.splice 会修改xs, 所以是不纯的, 所以相同的输入不会有相同的输出! var xs.splice(0,3); //= [1,2,3] xs.splice(0,3); //= [4,5] xs.splice(0,3); //= []
纯函数的好处: 不会去修改外部变量就不会产生线程安全问题.可以极大的减少系统复杂程度
0x02 函数的柯里化
看! 代码!
// 调用 doWht(我, 家里, 饭); let doWhat = (who, where, what) = { return who + 在 + where + 做 + what // 柯里化后的等价效果 // 调用 doWhat(我)(家里)(饭) let doWhat = who = where = what = { return who + 在 + where + 做 + what // 假设现在知道是我在家, 至于做什么是不知道的 // tmp函数就已经帮我们保存了值, 这样是非常灵活的. let doWhatCurry = doWhat(我)(家里)
上面提到的库里都有一个叫curry的函数会将一个普通的函数柯里化.
0x03 函数的组合
函数组合是将函数组合在一起, 生成一个新的函数
// h(g(f(x))) 这是以前调用函数的方式 var add1 = x = x + 1 var mul5 = x = x * 5 // compose会生成一个新的函数, 接收的参数全部传给add1, 然后add1的返回值传给mul5(注意注意!, mul5的参数个数只能有一个!!!), 然后compose生成的新的函数的返回值就是mul5的返回值. compose(mul5, add1)(2)
函数组合非常强大, 能够通过组合的方式来生成新的函数, 这是非常爽的. 如果你运用灵活, 会极大的减少你的代码量(如果不能减少别喷我啊), compose的实现在上面提到的三个库中都有实现.
0x04 声明式与命令式风格
命令式的风格让我们通过代码引导机器, 让机器一步一步完成我们要的任务; 而声明式则是直接告诉机器我要做啥, 更直观.
//命令式 var persons = [...] for (var i = 0; persons.length; ++i) { persons[i] = persons[i].toUppercase() //声明式 var persons = [...] persons.map(person = person.toUppercase())
0x05 Point free风格
// 假定如果 let map = fn = list = list.map(fn); let add = (a, b) = a + b; // 函数incrementAll不是point free 风格 // 因为这里提到了numbers参数, 需要给出一个命名. // 这样定义函数会导致我们需要多命名一个变量. 麻烦! let incrementAll = (numbers) = map(add(1))(numbers); // Point free风格的定义方法 // 假设add被柯里化过了 let incrementAll = map(add(1))
现在是推荐使用point free风格的代码(定义函数时), 这会减少我们不必要的命名. 多用这种风格哦!
0x06 容器(Functor)
容器代表了一个值, 一个任意值. 他就好像是函数式编程里的变量,函数的一个铠甲.可以让你的变量,函数在工程的战场中所向披靡!
var Container = function(x) { this.__value = x; Container.of = x = new Container(x); Container.prototype.map = function(f){ return Container.of(f(this.__value)) Container.of(3).map(x = x+1).map(x = x*5) // of用来构建容器, map用来变换容器 // Functor可以做很多很多事情, 具体的? 往下介绍.
// Maybe就是在普通容器上新增了一个检查空值的行为. var Maybe = function(x) { this.__value = x; Maybe.of = function(x) { return new Maybe(x); Maybe.prototype.map = function(f) { return this.isNothing() ? Maybe.of(null) : Maybe.of(f(this.__value)); Maybe.prototype.isNothing = function() { return (this.__value === null || this.__value === undefined); // 例子, 如果name是空的话就会输出空了 var functor = Maybe.of({name: ‘mrcode}) functor .map(value = value.age) .map(String.prototype.upperCase) .map(value = console.log(value))
这个Maybe到底有啥用呢? 就是空值检测, 看上面的例子, 如果不进行判空的话, 第二个map就会调用String.prototype.upperCase函数, 会抛出异常的, 怕了吧? :P, 而且, 现在很多语言,swift等都添加了类似的支持. optional
Maybe只能判空, 但是Either才是真正的处理错误的容器, Either有两个子类, Left和Right.
// Promise是通过catch方法来接收错误的 如: doSomething() .then(async1) .then(async2) .catch(e = console.log(e)); // 完全一样 var Left = function(x) { this.__value = x; var Right = function(x) { this.__value = x; // 完全一样 Left.of = function(x) { return new Left(x); Right.of = function(x) { return new Right(x); // 这里不同!!! Left.prototype.map = function(f) { return this; Right.prototype.map = function(f) { return Right.of(f(this.__value)); // 应用: var getAge = user = user.age ? Right.of(user.age) : Left.of("ERROR!") getAge({name: stark, age: 21}).map(age = Age is + age); //= Right(Age is 21) getAge({name: stark}).map(age = Age is + age); //= Left(ERROR!)
Left会跳过所有执行过程, 直达结果, 这就好像Right是流程图里一个又一个指向下一个任务的箭头, 而Left直接指向了结果, 是错误的结果.
0x07 IO
诶, 函数式编程里, 涉及到IO总是让人尴尬的, 蓝瘦的很..幸好, 有一种叫做IO的东西专门处理IO这种东西(别嫌绕哈), 看代码,
// 没毛病 var IO = function(f) { this.__value = f; // ??? 看不懂, 待会解释.. IO.of = x = new IO(_ = // ??? 这是啥子鬼???? IO.prototype.map = function(f) { return new IO(compose(f, this.__value))
权威解答: 这里的IO里存的是一个函数, 包裹了外部环境变量的函数, 我们传入了一个函数, 这个函数里包含了实际的值,会进行IO操作. 我们把不纯的IO操作放到了这个函数里, 总体上看, 我们的IO对象, 是不会执行这些不纯的操作的. 它依然是纯的, 因为IO操作压根就没执行内部包含的函数, 这个函数是外部调用者去执行的. 也就是说, 不纯的操作是外部的人干的, 和我们的IO对象一丢丢关系都木有!(干得漂亮!) 看一个例子.
var io_document = new IO(_ = window.document); io_document.map(function(doc){ return doc.title }); // 得到IO(documen.title)
科普: 这里你没有得到document.title, 你得到的仅仅是一个会返回document.title的一个函数, 这个函数是不纯的, 但是执行不是由上面的代码执行的, 锅在调用函数的人身上! 上面的代码依然是纯的!
0x08 Monad
看这个部分的时候建议看一下IO的实现, 好好理解一下, 我知道有点烧脑, 但是看一下没坏处!玩过Promise的都知道, Promise.then传进去的函数可以返回一个新的Promise. Promise就是Monad.
0x09 函数式编程的应用
react中的纯组件
// 固定的输入得到固定的输出 纯组件极大的增加了react的灵活程度 // app 的状态交给一些状态机管理 比如redux var Text = props = ( div >redux中的reducer
// 输入当前状态和action, 输出nowState reducer(currentState, action) = newState
0x10 总结一下
确实是这样, 不总结的话就不像是一篇文章了, 还是总结下吧:
纯函数的概念以及函数柯里化和函数的组合 容器概念, Container和Maybe, Either的派生Left,Right, IO作用. 函数式编程的应用 作者:mrcode来源:51CTO
JavaScript 函数式编程技巧 - 反柯里化 柯里化,是固定部分参数,返回一个接受剩余参数的函数,也称为部分计算函数,目的是为了缩小适用范围,创建一个针对性更强的函数。核心思想是把多参数传入的函数拆成单参数(或部分)函数,内部再返回调用下一个单参数(或部分)函数,依次处理剩余的参数。 而反柯里化,从字面讲,意义和用法跟函数柯里化相比正好相反,扩大适用范围,创建一个应用范围更广的函数。使本来只有特定对象才适用的方法,扩展到更多的对象。
JavaScript 函数式编程技巧 - 柯里化 柯里化(Currying),又称部分求值(Partial Evaluation),是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。 核心思想是把多参数传入的函数拆成单参数(或部分)函数,内部再返回调用下一个单参数(或部分)函数,依次处理剩余的参数。 按照Stoyan Stefanov --《JavaScript Pattern》作者 的说法,所谓“柯里化”就是使函数理解并处理部分应用
Fundebug经授权转载,版权归原作者所有。 在长时间学习和使用面向对象编程之后,咱们退一步来考虑系统复杂性。 在做了一些研究之后,我发现了函数式编程的概念,比如不变性和纯函数。
大神是怎样用函数式JavaScript计算数组平均值的 函数式编程中用于操作数组的方法就像“毒品”一样,它让很多人爱上函数式编程。因为它们真的十分常用而且又超级简单。 .map() 和 .filter()都仅需一个参数,该参数定义操作数组每一个元素的函数即可。
JavaScript函数式编程之pointfree与声明式编程 更多相关内容见博客 github.com/zhuanyongxi… 函数式编程中的pointfree的意思就是“无参”或“无值”,pointfree style是一种编程范式,也作tacit programming,就是“无参编程”的意思了。
函数式 JavaScript 教程(一) 本文讲的是函数式 JavaScript 教程(一),本文是介绍 JavaScript 函数式编程的四部分之首篇。在这篇文章里,我们来看一下那些让 JavaScript 适合作为函数式编程语言的组成部分,并探讨为什么函数式编程可能是有用的。
译者按: 当从业20的JavaScript老司机学会函数式编程时,他扔掉了90%的特性,也不用面向对象了,最后发现了真爱啊!!! 原文: How I rediscovered my love for JavaScript after throwing 90% of it in the trash. 译者: Fundebug
轻量函数式 JavaScript:十一、综合应用 现在,你拥有了为了理解轻量函数式 JavaScript 所需的一切。再没有新的概念要介绍了。 在这最后的一章中,我们的目标是凝聚这些概念。我们将看到将这本书中的许多主题融合在一起的代码 —— 应用我们学到的东西。
相关文章
- [译]JavaScript:函数的作用域链
- javascript日期相减,求时间差
- 【学习笔记13】JavaScript的判断语法
- 前端开发面试题—JavaScript回调函数与异步编程
- 25dwr - DWR中的JavaScript(查找回调函数)
- 前端基础 - JavaScript定时器
- JavaScript 11. 函数
- JavaScript - 获取字符串字节数(函数封装)
- 精读JavaScript模式(六),Memoization模式与函数柯里化的应用
- JavaScript - 数组
- 【JavaScript】理解与使用Javascript中的回调函数
- javascript加载顺序
- JavaScript 高阶函数
- JavaScript进阶系列02,函数作为参数以及在数组中的应用
- JavaScript设计模式经典-面向对象中六大原则
- 第60节:Java中的JavaScript技术
- JavaScript 回调函数中的 return false 问题
- javascript 指定绑定函数名称,并且传递参数
- 《第三方JavaScript编程》——1.4 第三方开发的挑战
- 《21天学通HTML+CSS+JavaScript Web开发(第7版)》——1.6 小结
- ArcGIS JavaScript API4.8 底图选择的几种方案
- 什么情况下要加上 javascript:
- javascript基础,对象和函数(4)
- JavaScript函数式编程究竟是什么?
- JavaScript深入浅出第2课:函数是一等公民是什么意思呢?
- JavaScript正则表达式进阶指南
- 如何实现JavaScript的Map和Filter函数?
- 华为OD机试 - 航天器(JavaScript) | 机试题+算法思路+考点+代码解析 【2023】
- 华为OD机试 - 最少数量线段覆盖(JavaScript) | 机试题+算法思路+考点+代码解析 【2023】
- 华为OD机试 - Excel单元格数值统计(JavaScript) | 机试题+算法思路+考点+代码解析 【2023】
- JavaScript定时调用函数(SetInterval与setTimeout)
- JavaScript中的闭包
- 深入理解JavaScript系列(4):立即调用的函数表达式
- JavaScript 中常用和必备的一些工具类函数