JavaScript函数式真正的浅析

JS函数式浅析

0x00 入门的导语(废话)

最近两年你要说函数式编程不火的话, 那是不可能的, 是人都知道函数式编程很火.为什么函数式编程会火呢, 在于它的思想, 很强大,
很强势!尤其是前端的redux更是在reducer上完全使用纯函数, 函数式的好处渐渐被发掘出来, 笔者最近看了一些函数式方面的东东,
现在发出来给大家学习学习, 顺便我也学习学习怎么写文章... :P

常用的函数式库:

  • ramda 设计很棒的一个库
  • lodash 比较常用的一个库
  • underscore 应该也不错的一个库

0x01 纯函数

定义: 相同输入一定得到相同输出且运行过程中不修改,不读取外部环境的变量的函数

说出来肯定不好理解, 还是要看看代码. 就好像你不看国足比赛永远不知道国足为什么会输给月薪几百块的叙利亚.


  1. // Array.slice 对于固定输入一定是固定输出, 且不依赖外部变量, 啥? 依赖了arr变量吗? 
  2. // 其实这种写法和Array.prototype.slice(arr, 0, 3); 是一样的. 这样就理解了, 
  3. // 你还学到一个东西 Array.slice是不会修改原数组滴! 
  4. var arr = [1,2,3,4,5]; 
  5. arr.slice(0,3); 
  6.  
  7.  // Array.splice 会修改xs, 所以是不纯的, 所以相同的输入不会有相同的输出! 
  8. var xs.splice(0,3); 
  9. //=> [1,2,3] 
  10. xs.splice(0,3); 
  11. //=> [4,5] 
  12. xs.splice(0,3); 
  13. //=> []  

纯函数的好处: 不会去修改外部变量就不会产生线程安全问题.可以极大的减少系统复杂程度

0x02 函数的柯里化

看! 代码!


  1. // 调用 doWht('我', '家里', '饭'); 
  2. let doWhat = (who, where, what) => { 
  3.   return who + '在' + where + '做' + what 
  4.  
  5. // 柯里化后的等价效果 
  6. // 调用 doWhat('我')('家里')('饭') 
  7. let doWhat = who => where => what => { 
  8.   return who + '在' + where + '做' + what 
  9.  
  10. // 假设现在知道是'我'在'家', 至于做什么是不知道的 
  11. // tmp函数就已经帮我们保存了值, 这样是非常灵活的. 
  12. let doWhatCurry = doWhat('我')('家里')  

上面提到的库里都有一个叫curry的函数会将一个普通的函数柯里化.

0x03 函数的组合

函数组合是将函数组合在一起, 生成一个新的函数


  1. // h(g(f(x))) 这是以前调用函数的方式 
  2. var add1 = x => x + 1 
  3. var mul5 = x => x * 5 
  4. // compose会生成一个新的函数, 接收的参数全部传给add1, 然后add1的返回值传给mul5(注意注意!, mul5的参数个数只能有一个!!!), 然后compose生成的新的函数的返回值就是mul5的返回值. 
  5. compose(mul5, add1)(2)  

函数组合非常强大, 能够通过组合的方式来生成新的函数, 这是非常爽的. 如果你运用灵活, 会极大的减少你的代码量(如果不能减少别喷我啊), compose的实现在上面提到的三个库中都有实现.

0x04 声明式与命令式风格

命令式的风格让我们通过代码引导机器, 让机器一步一步完成我们要的任务; 而声明式则是直接告诉机器我要做啥, 更直观.


  1. //命令式 
  2. var persons = [...] 
  3. for (var i = 0; persons.length; ++i) { 
  4.   persons[i] = persons[i].toUppercase() 
  5.  
  6. //声明式 
  7. var persons = [...] 
  8. persons.map(person => person.toUppercase())  

0x05 Point free风格


  1. // 假定如果  
  2. let map = fn => list => list.map(fn); 
  3. let add = (a, b) => a + b; 
  4.  
  5. // 函数incrementAll不是point free 风格 
  6. // 因为这里提到了numbers参数, 需要给出一个命名. 
  7. // 这样定义函数会导致我们需要多命名一个变量. 麻烦! 
  8. let incrementAll = (numbers) => map(add(1))(numbers); 
  9.  
  10. // Point free风格的定义方法 
  11. // 假设add被柯里化过了 
  12. let incrementAll = map(add(1))  

现在是推荐使用point free风格的代码(定义函数时), 这会减少我们不必要的命名. 多用这种风格哦!

0x06 容器(Functor)

容器代表了一个值, 一个任意值. 他就好像是函数式编程里的变量,函数的一个铠甲.可以让你的变量,函数在工程的战场中所向披靡!


  1. var Container = function(x) { 
  2.   this.__value = x; 
  3.  
  4. Container.of = x => new Container(x); 
  5.  
  6. Container.prototype.map = function(f){ 
  7.   return Container.of(f(this.__value)) 
  8.  
  9. Container.of(3).map(x => x+1).map(x => x*5) 
  10. // of用来构建容器, map用来变换容器 
  11. // Functor可以做很多很多事情, 具体的? 往下介绍.  

  1. // Maybe就是在普通容器上新增了一个检查空值的行为.  
  2. var Maybe = function(x) { 
  3.   this.__value = x; 
  4.  
  5. Maybe.of = function(x) { 
  6.   return new Maybe(x); 
  7.  
  8. Maybe.prototype.map = function(f) { 
  9.   return this.isNothing() ? Maybe.of(null) : Maybe.of(f(this.__value)); 
  10.  
  11. Maybe.prototype.isNothing = function() { 
  12.   return (this.__value === null || this.__value === undefined); 
  13.  
  14. // 例子, 如果name是空的话就会输出空了 
  15. var functor = Maybe.of({name: ‘mrcode'}) 
  16. functor 
  17.     .map(value => value.age) 
  18.     .map(String.prototype.upperCase) 
  19.     .map(value => console.log(value))  

这个Maybe到底有啥用呢? 就是空值检测, 看上面的例子, 如果不进行判空的话,
第二个map就会调用String.prototype.upperCase函数, 会抛出异常的, 怕了吧? :P, 而且,
现在很多语言,swift等都添加了类似的支持. optional

Maybe只能判空, 但是Either才是真正的处理错误的容器, Either有两个子类, Left和Right.


  1. // Promise是通过catch方法来接收错误的 如: 
  2. doSomething() 
  3.     .then(async1) 
  4.     .then(async2) 
  5.     .catch(e => console.log(e)); 
  6.  
  7. // 完全一样     
  8. var Left = function(x) { 
  9.   this.__value = x; 
  10. var Right = function(x) { 
  11.   this.__value = x; 
  12.  
  13. // 完全一样 
  14. Left.of = function(x) { 
  15.   return new Left(x); 
  16. Right.of = function(x) { 
  17.   return new Right(x); 
  18.  
  19. // 这里不同!!! 
  20. Left.prototype.map = function(f) { 
  21.   return this; 
  22. Right.prototype.map = function(f) { 
  23.   return Right.of(f(this.__value)); 
  24.  
  25. // 应用: 
  26. var getAge = user => user.age ? Right.of(user.age) : Left.of("ERROR!") 
  27. getAge({name: 'stark', age: '21'}).map(age => 'Age is ' + age); 
  28. //=> Right('Age is 21') 
  29.  
  30. getAge({name: 'stark'}).map(age => 'Age is ' + age); 
  31. //=> Left('ERROR!')  

Left会跳过所有执行过程, 直达结果, 这就好像Right是流程图里一个又一个指向下一个任务的箭头, 而Left直接指向了结果, 是错误的结果.

0x07 IO

诶, 函数式编程里, 涉及到IO总是让人尴尬的, 蓝瘦的很..幸好, 有一种叫做IO的东西专门处理IO这种东西(别嫌绕哈), 看代码,


  1. // 没毛病 
  2. var IO = function(f) { 
  3.     this.__value = f; 
  4.  
  5. // ??? 看不懂, 待会解释.. 
  6. IO.of = x => new IO(_ => x); 
  7.  
  8. // ??? 这是啥子鬼???? 
  9. IO.prototype.map = function(f) { 
  10.     return new IO(compose(f, this.__value)) 
  11. };  

权威解答: 这里的IO里存的是一个函数, 包裹了外部环境变量的函数, 我们传入了一个函数, 这个函数里包含了实际的值,会进行IO操作.
我们把不纯的IO操作放到了这个函数里, 总体上看, 我们的IO对象, 是不会执行这些不纯的操作的. 它依然是纯的,
因为IO操作压根就没执行内部包含的函数, 这个函数是外部调用者去执行的. 也就是说, 不纯的操作是外部的人干的,
和我们的IO对象一丢丢关系都木有!(干得漂亮!) 看一个例子.


  1. var io_document = new IO(_ => window.document); 
  2. io_document.map(function(doc){ return doc.title }); 
  3. // 得到IO(documen.title)  

科普: 这里你没有得到document.title, 你得到的仅仅是一个会返回document.title的一个函数, 这个函数是不纯的, 但是执行不是由上面的代码执行的, 锅在调用函数的人身上! 上面的代码依然是'纯'的!

0x08 Monad

看这个部分的时候建议看一下IO的实现, 好好理解一下, 我知道有点烧脑, 但是看一下没坏处!玩过Promise的都知道, Promise.then传进去的函数可以返回一个新的Promise. Promise就是Monad.

0x09 函数式编程的应用

react中的纯组件


  1. // 固定的输入得到固定的输出 纯组件极大的增加了react的灵活程度 
  2. // app 的状态交给一些状态机管理 比如redux 
  3. var Text = props => ( 
  4.     <div style={props.style}>{props.text}</div> 
  5. )  

redux中的reducer


  1. // 输入当前状态和action, 输出nowState 
  2. reducer(currentState, action) => newState  

0x10 总结一下

确实是这样, 不总结的话就不像是一篇文章了, 还是总结下吧:

  • 纯函数的概念以及函数柯里化和函数的组合
  • 容器概念, Container和Maybe, Either的派生Left,Right, IO作用.
  • 函数式编程的应用

作者:mrcode

来源:51CTO

时间: 2024-10-21 11:52:48

JavaScript函数式真正的浅析的相关文章

javascript函数式编程实例分析

  这篇文章主要介绍了javascript函数式编程,实例分析了javascript函数式编程的相关使用技巧,非常具有实用价值,需要的朋友可以参考下 本文实例讲述了javascript函数式编程.分享给大家供大家参考.具体分析如下: js像其他动态语言一样是可以写高阶函数的,所谓高阶函数是可以操作函数的函数.因为在js中函数是一个彻彻底底的对象,属于第一类公民,这提供了函数式编程的先决条件. 下面给出一个例子代码,出自一本js教程,功能是计算数组元素的平均值和标准差,先列出非函数式编程的一种写法

JavaScript中闭包之浅析解读(必看篇)_javascript技巧

JavaScript中的闭包真心是一个老生常谈的问题了,最近面试也是一直问到,我自己的表述能力又不能完全支撑起来,真是抓狂.在回来的路上,我突然想到了一个很简单的事情,其实我们在做项目时候,其实就经常用到闭包的,可是面试问的时候,回答又往往是我们经常搜到的答案,唉 不管是应付面试 还是真的想学点东西 ,我也用自己的理解跟大家分享一下,书面化就避免不了了的. 1.闭包是什么? 红宝书中曰:"是指有权访问另外一个函数作用域中的变量的函数." 简单的说,JavaScript允许使用内部函数-

Javascript函数式编程语言_javascript技巧

函数式编程语言 函数式编程语言是那些方便于使用函数式编程范式的语言.简单来说,如果具备函数式编程所需的特征, 它就可以被称为函数式语言.在多数情况下,编程的风格实际上决定了一个程序是否是函数式的. 是什么让一个语言具有函数式特征? 函数式编程无法用C语言来实现.函数式编程也无法用Java来实现(不包括那些通过大量变通手段实现的近似函数式编程). 这些语言不包含支持函数式编程的结构.他们是纯面向对象的.严格非函数式的语言. 同时,纯函数语言也无法使用面向对象编程,比如Scheme.Haskell以

javascript函数式编程实例分析_javascript技巧

本文实例讲述了javascript函数式编程.分享给大家供大家参考.具体分析如下: js像其他动态语言一样是可以写高阶函数的,所谓高阶函数是可以操作函数的函数.因为在js中函数是一个彻彻底底的对象,属于第一类公民,这提供了函数式编程的先决条件. 下面给出一个例子代码,出自一本js教程,功能是计算数组元素的平均值和标准差,先列出非函数式编程的一种写法: var data = [1,1,3,5,5]; var total = 0; for(var i = 0;i < data.length;i++)

Javascript函数式编程简单介绍_javascript技巧

几十年来,函数式编程一直是计算机科学狂热者的至爱,由于数学的纯洁性和谜一般的本质, 它被埋藏在计算机实验室,只有数据学家和有希望获得博士学位的人士使用.但是现在,它正经历一场复兴, 这要感谢一些现代语言比如Python,Julia,Ruby,Clojure以及--但不是最后一个--Javascript. 你是说Javascript?这个WEB脚本语言?没错! Javascript已经被证明是一项长期以来都没有消失的重要的技术.这主要是由于它扩展的一些框架和库而使其具有重生的能力, 比如backb

浅谈javascript函数式编程_javascript技巧

函数式编程,属于编程范式的一种 1 函数是第一公民,可以返回值,也可以作为其他函数的参数 //console是一个函数 function con(v){ console.log(v) } // execute 也是一个函数 function execute(fn){ fn(1) } //将con函数作为参数传进execute函数 execute(con) // 1 2 接近自然语言的写法   晓池吃完饭然后就去洗澡 可以表现为eat().bathe() // 吃饭函数 function eat(

探究JavaScript函数式编程的乐趣_javascript技巧

编程范式 编程范式是一个由思考问题以及实现问题愿景的工具组成的框架.很多现代语言都是聚范式(或者说多重范式): 他们支持很多不同的编程范式,比如面向对象,元程序设计,泛函,面向过程,等等. 函数式编程范式 函数式编程就像一辆氢燃料驱动的汽车--先进的未来派,但是还没有被广泛推广.与命令式编程相反,他由一系列语句组成,这些语句用于更新执行时的全局状态.函数式编程将计算转化作表达式求值.这些表达式全由纯数学函数组成,这些数学函数都是一流的(可以被当做一般值来运用和处理),并且没有副作用. 函数式编程

我眼中的JavaScript函数式编程

JavaScript 函数式编程是一个存在了很久的话题,但似乎从 2016 年开始,它变得越来越火热.这可能是因为 ES6 语法对于函数式编程更为友好,也可能是因为诸如 RxJS (ReactiveX) 等函数式框架的流行. 看过许多关于函数式编程的讲解,但是其中大部分是停留在理论层面,还有一些是仅针对 Haskell 等纯函数式编程语言的.而本文旨在聊一聊我眼中的函数式编程在 JavaScript 中的具体实践,之所以是 "我眼中的" 即我所说的仅代表个人观点,可能和部分 严格概念 

《JavaScript函数式编程》读后感_javascript技巧

本文章记录本人在学习 函数式 中理解到的一些东西,加深记忆和并且整理记录下来,方便之后的复习. 在近期看到了<JavaScript函数式编程>这本书预售的时候就定了下来.主要目的是个人目前还是不理解什么是函数式编程.在自己学习的过程中一直听到身边的人说面向过程编程和面向对象编程,而函数式就非常少.为了自己不要落后于其他同学的脚步,故想以写笔记的方式去分享和记录自己阅读中所汲取的知识. js 和函数式编程 书中用了一句简单的话来回答了什么是函数式编程: 函数式编程通过使用函数来将值转换为抽象单元