17

大概几个月前,受题叶影响开始关注函数式编程,在看完一本 js 的函数式之后,对函数式有了点基本的了解。

函数式给我的感觉,最大的一个主旨是是编程中所有过程可控,尤其在 js 这种没有原则的语言中,过程可控制有为重要。

函数式

到底什么是函数式,他和命令式编程和面向对象有什么区别。(知乎上已经有很多讨论了,感兴趣的话,我在结尾的地方贴了一些链接。)
总的来说,在函数式中,函数是一等公民,函数能作为变量的值,函数可以是另一个函数的参数,函数可以返回另一个函数等等。

函数式中有些个人感觉比较有意思的点

纯函数

最喜欢纯函数了,什么是纯函数呢?纯函数有三个重要的点:

  1. 函数的结果只受函数参数影响。
  2. 函数内部不使用能被外部函数影响的变量。
  3. 函数的结果不影响外部变量。

这有什么好处呢?当你有一堆函数对某个数据进行操作的时候,就能轻松定位到哪些函数出了问题,这也就是 Redux 的中心思想,控制状态的 Reducer 就是一个纯函数。

不可变

说到纯函数,不得不聊聊不可变。

FB 在推出 React 之后, immutable.js 也跟着火起来了。不可变,顾名思义,就是变量或者结构在定义之后不能再发生值的变动,所有操作只是产生新的值而不是去覆盖之前的变量。这样去控制数据,能够让数据流动更加可控。

在 jQuery 大行其道的代码中,最为人称赞的莫过于其链式操作了,但不知道有没有人跟我一样遇到过一些问题,比如我对一个节点进行操作,搞着搞着当前节点不是这个节点了,当我需要对这个节点再进行一次操作的时候,只能新开一个链式。

在函数中,流永远只是对当前的那个初始的数据进行操作,而且当加上惰性链,所有过程更加容易控制。

什么是惰性链?

回想一下 jq 的链式操作,是不是每次一个链接加上去就立马生效,当你写完一堆链式之后,发现不对,如果不能一眼看出是哪步错了,只能一次次去删最后的操作来找原因。而惰性链是在你写完链式时候并不会执行,而是在最后跟上一个执行用的函数,才会去执行前面的所有函数。

mixins

打个比方,当我们需要类型判断的时候,红皮书里一般都是各种 if,不知道平时写的时候也是这样,用个 utils 写各种类型判断的函数,然后挨个 if。

用 mixins 思路的话,大概可以这么去做:

dispach(
function(s){ return isString(s) ? s : undefined },
function(s){ return isArray(s) ? stringifyArray(s) : undefined },
function(s){ return isObject(s) ? stringfyObject(s) : undefined },
function(s){ return s.toString() }
)

这样只要有个函数返回的肯定是字符串了。

函数响应式编程

响应式编程(反应式编程)

响应式编程是一种面向数据流和变化传播的编程范式,数据更新是相关联的。比如很多时候,在写界面的时候,我们需要对事件做处理,伴随着前端事件的增多,对于事件的处理愈发需要更加方便的处理。

设想一下,平时在处理事件的时候,一单上了复杂度,比如输入的时候,需要停止输入的时候才进行,这个时候又只能输入长度大于2才进行事件,当还是之前的数据的话不进行事件,可以考虑一下这个情况下如何去写。

拿 RxJs 中的一端例子举例

var keyup = Rx.Observable.fromEvent($input, 'keyup')
      .map(function (e) {
        return e.target.value; 
      })
      .filter(function (text) {
        return text.length > 2; 
      })
      .debounce(750)
      .distinctUntilChanged(); 

是否十分简洁。

把函数范式里的一套思路和响应式编程合起来就是函数响应式编程。

不知道有没有发现其实 Promise 也是响应式编程的一种。

举个寒冬大大的代码,在一个按钮上绑定两个事件,一个是 5s 后触发,一个是用户点击。

function wait(duration){
    return new Promise(function(resolve, reject) {
        setTimeout(resolve,duration);
    })
}

function waitFor(element,event,useCapture){
    return new Promise(function(resolve, reject) {
        element.addEventListener(event,function listener(event){
            resolve(event)
            this.removeEventListener(event, listener, useCapture);
        },useCapture)
    })
}

var btn = document.getElementById('button');
Promise.race([wait(5000), waitFor(btn, click)]).then(function(){
    console.log('run!')
})

把两个事件绑定到按钮上,这两个事件函数同时也是 promise,这样只要有一个触发了就会执行,相比普通代码

btn.addEventListener(event, eventHandler, false);
setTimeout(eventHandler, 5000);
function eventHandler(){
  console.log('run!');
}

当需要增加更多事件组合的时候,更加容易拓展。

刚才的代码,用 Rx.js 实现会更加简洁

var btn = document.getElementById('button');
var logRun = Rx.Observable.fromEvent(btn, 'click')
             .merge(Rx.Observable.timer(3000))
             .subscribe(e => {
               console.log('run!');
               logRun.dispose(); // 如果是一次性的就移除observable
             });

先这样,若有不对的地方请告知,感谢~


Fakefish
4.3k 声望241 粉丝

引用和评论

0 条评论