大概几个月前,受题叶影响开始关注函数式编程,在看完一本 js 的函数式之后,对函数式有了点基本的了解。
函数式给我的感觉,最大的一个主旨是是编程中所有过程可控,尤其在 js 这种没有原则的语言中,过程可控制有为重要。
函数式
到底什么是函数式,他和命令式编程和面向对象有什么区别。(知乎上已经有很多讨论了,感兴趣的话,我在结尾的地方贴了一些链接。)
总的来说,在函数式中,函数是一等公民,函数能作为变量的值,函数可以是另一个函数的参数,函数可以返回另一个函数等等。
函数式中有些个人感觉比较有意思的点
纯函数
最喜欢纯函数了,什么是纯函数呢?纯函数有三个重要的点:
- 函数的结果只受函数参数影响。
- 函数内部不使用能被外部函数影响的变量。
- 函数的结果不影响外部变量。
这有什么好处呢?当你有一堆函数对某个数据进行操作的时候,就能轻松定位到哪些函数出了问题,这也就是 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
});
先这样,若有不对的地方请告知,感谢~
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。