网络科技

    今日:866| 主题:245806
收藏本版
互联网、科技极客的综合动态。

[其他] JavaScript函数式真正的浅析

[复制链接]
曾经的年少轻狂 发表于 2016-10-17 03:30:44
84 6

立即注册CoLaBug.com会员,免费获得投稿人的专业资料,享用更多功能,玩转个人品牌!

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
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. // Array.splice 会修改xs, 所以是不纯的, 所以相同的输入不会有相同的输出!
  7. var xs.splice(0,3);
  8. //=> [1,2,3]
  9. xs.splice(0,3);
  10. //=> [4,5]
  11. xs.splice(0,3);
  12. //=> []
复制代码
纯函数的好处: 不会去修改外部变量就不会产生线程安全问题.可以极大的减少系统复杂程度
  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. // 函数incrementAll不是point free 风格
  5. // 因为这里提到了numbers参数, 需要给出一个命名.
  6. // 这样定义函数会导致我们需要多命名一个变量. 麻烦!
  7. let incrementAll = (numbers) => map(add(1))(numbers);
  8. // Point free风格的定义方法
  9. // 假设add被柯里化过了
  10. 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. Container.prototype.map = function(f){
  6.   return Container.of(f(this.__value))
  7. }
  8. Container.of(3).map(x => x+1).map(x => x*5)
  9. // of用来构建容器, map用来变换容器
  10. // 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. var Left = function(x) {
  8.   this.__value = x;
  9. }
  10. var Right = function(x) {
  11.   this.__value = x;
  12. }
  13. // 完全一样
  14. Left.of = function(x) {
  15.   return new Left(x);
  16. }
  17. Right.of = function(x) {
  18.   return new Right(x);
  19. }
  20. // 这里不同!!!
  21. Left.prototype.map = function(f) {
  22.   return this;
  23. }
  24. Right.prototype.map = function(f) {
  25.   return Right.of(f(this.__value));
  26. }
  27. // 应用:
  28. var getAge = user => user.age ? Right.of(user.age) : Left.of("ERROR!")
  29. getAge({name: 'stark', age: '21'}).map(age => 'Age is ' + age);
  30. //=> Right('Age is 21')
  31. getAge({name: 'stark'}).map(age => 'Age is ' + age);
  32. //=> 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. IO.prototype.map = function(f) {
  9.     return new IO(compose(f, this.__value))
  10. };
复制代码
权威解答: 这里的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. // 调用 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('我')('家里')0
复制代码
redux中的reducer

  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('我')('家里')1
复制代码
0x10 总结一下

  确实是这样, 不总结的话就不像是一篇文章了, 还是总结下吧:
  
       
  •       纯函数的概念以及函数柯里化和函数的组合
       
  •       容器概念, Container和Maybe, Either的派生Left,Right, IO作用.
       
  •       函数式编程的应用
      
友荐云推荐




上一篇:使用sklearn进行数据挖掘
下一篇:Android 中的转场动画及兼容处理
酷辣虫提示酷辣虫禁止发表任何与中华人民共和国法律有抵触的内容!所有内容由用户发布,并不代表酷辣虫的观点,酷辣虫无法对用户发布内容真实性提供任何的保证,请自行验证并承担风险与后果。如您有版权、违规等问题,请通过"联系我们"或"违规举报"告知我们处理。

如波 发表于 2016-10-17 03:44:07
别用你的脾气来挑战我的个性!
回复 支持 反对

使用道具 举报

一朵奇葩向阳开 发表于 2016-10-17 03:51:41
不回帖,臣妾做不到啊!
回复 支持 反对

使用道具 举报

刘述平 发表于 2016-10-24 13:59:21
我们都知道恶虎架不住群狼。说明:”团队很重要!”
回复 支持 反对

使用道具 举报

空心印末夕情 发表于 2016-10-29 02:01:19
就算是一坨屎,也有遇见屎壳郎的那天。所以你大可不必为今天的自己有太多担忧。
回复 支持 反对

使用道具 举报

liluo1991 发表于 2016-11-4 23:13:41
这次必须是沙发!
回复 支持 反对

使用道具 举报

nobo110 发表于 2016-11-9 09:29:12
想念你,真的不需要理由,那只是一种感觉,随时乘我不备,就会窜入我的脑海,直达我的心脏。
回复 支持 反对

使用道具 举报

*滑动验证:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

我要投稿

推荐阅读

扫码访问 @iTTTTT瑞翔 的微博
回页顶回复上一篇下一篇回列表手机版
手机版/CoLaBug.com ( 粤ICP备05003221号 | 文网文[2010]257号 )|网站地图 酷辣虫

© 2001-2016 Comsenz Inc. Design: Dean. DiscuzFans.

返回顶部 返回列表