• 微信公众号:美女很有趣。 工作之余,放松一下,关注即送10G+美女照片!

纯函数与副作用

互联网 diligentman 2周前 (02-20) 6次浏览

现代的JS有许多概念或者说一些编程技巧,这些概念有来自于其他语言、一些设计模式或者js语言特有的概念等。下面就来说一下与函数式编程密切相关的两个概念:纯函数副作用

副作用

在计算机科学中,函数副作用指当调用函数时,除了返回函数值之外,还对主调用函数产生附加的影响。例如修改全局变量(函数外的变量)或修改参数。 — 维基百科

例如在javascript的内置的一些函数是有副作用的:

[1, 2, 3].pop() // 每次执行pop函数,原数组都会减少一个元素 [1, 2, 3].splice(1, 1) // 会删除原数组里面的元素 ...

除了这些内置的函数会存在副作用,我们平时写的一些操作Dom、修改登录信息和弹出一个弹窗等,这些例子都太多了。

无副作用 & 纯函数

  1. 函数与外界的只有唯一一个渠道进行沟通,通过传入参数和返回值进行沟通。
  2. 相同的传入参数永远只会有相同的返回值。

最简单的例子:

function sum(a, b) { return a + b }

这两个概念是相当简单的,我们接下来看一下,函数式的出现给JS带来了什么解决了什么样的问题存在哪些问题

带来了什么

纯函数函数式有一些优点:

  • 可缓存性(Cacheable)
  • 可移植性/自文档化(Portable / Self-Documenting)
  • 可测试性(Testable)
  • 合理性(Reasonable)
  • 并行代码(Parallel Code)

单从这些有点来看,确实是一些软件工程上所提倡的几个观点,在足够灵活的情况下还能保证稳定性。想想一些场景,当你使用第三方库的函数,传入了你的比较重要的一个对象,结果被修改了,导致了整个程序的错了,关键是这样的问题还不好定位到。

let importantObj = { a: 1 } someFunc(a) console.log(importantObj ) // ??? 不知道被修改成什么样子了。。。埋下了坑

比如,ReactlodashRedux等就巧妙应用了js的函数式编程,使得暴露出来的接口十分稳定,接口结构清晰。但是这些还只是用到了函数式的方式和技巧,还并不是完全的函数范式,真正的函数范式的限制更加严格,但是我认为不能一味的去追求这些严格的理论,而是适合的才是最好的。其中的一些函数式编程模型,比如:闭包(Closure)、高阶函数、柯里化(Currying)和组合(Composing)等,让更多的框架借鉴和使用,函数式编程这些特性给前端带来了更多的活力。

解决了什么样的问题

函数式编程关心数据的映射,命令式编程关心解决问题的步骤。如今前端应用功能越来越多,用户交互越来越多,所有需要处理的用户数据也是越来越复杂,管理这些数据是现在大家比较关心的事情和寻求突破的事情。

函数范式来处理这些数据,就可以使得数据的每一次修改都可以变得可以追溯,可以很容易的定位到问题的所在。因为无副作用的特性,减少了影响原始数据噪音,使得每次修改数据都比较稳定。

存在哪些问题

相对于声明式和命令式,函数式的编程是比较不容易理解的,有些接口的用法有时候可能不好理解。下面我引用一下《重新思考 Redux》(rematch 作者 Shawn McKay 写的一篇干货软文)里面有些相关段落进行举例。


以下内容来着精读《重新思考 Redux》 博客,里面文章很有深度,推荐阅读。简化初始化redux 初始化代码涉及的概念比较多,比如 compose thunk 等等,同时将 reducerinitialStatemiddlewares 这三个重要概念拆分成了函数方式调用,而不是更容易接受的配置方式:

const store = preloadedState => { return createStore( rootReducer, preloadedState, compose(applyMiddleware(thunk, api), DevTools.instrument()) ); };

如果换成配置方式,理解成本会降低不少:

const store = new Redux.Store({ instialState: {}, reducers: { count }, middlewares: [api, devTools] });

笔者注:redux 的初始化方式非常函数式,而下面的配置方式就更面向对象一些。相比之下,还是面向对象的方式更好理解,毕竟 store 是一个对象。instialState 也存在同样问题,相比显示申明,将 preloadedState 作为函数入参就比较抽象了,同时 redux 对初始 state 的赋值也比较隐蔽,createStore 时统一赋值比较别扭,因为 reducers 是分散的,如果在 reducers 中赋值,要利用 es 的默认参数特性,看起来更像业务思考,而不是 redux 提供的能力。


通过上面博客的,可以看到,函数式的一些代码理解起来并不是那么容易。

因为函数式的处理方式,每次都是产生一个新的数据,所有相对来说,需要的内存和占的资源是较高的。

总结

经过上面的分析,我们可以看到函数式的一些模型早已在如今的前端各种库实行开来,并且效果都还很不错,了解函数式的这个概念可谓是非常有必要的,但是是否一定需要函数式需要多多考虑。还是记住只有适合的技术。

参考文章

  • 我眼中的 JavaScript 函数式编程
  • 精读《重新思考 Redux》
  • JavaScript 函数式编程(一)

原文链接: https://github.com/zachrey/zblog/issues/3


程序员灯塔
转载请注明原文链接:纯函数与副作用
喜欢 (0)