随着大前端时代的到来,在产品开发过程中,前端所占业务比重越来越大、交互越来越重。传统的老夫拿起 JQuery 就是一把梭应付当下重交互页面已经十分乏力。于是乎有了 Angular,React,vue 这些现代框架。
但随之而来的还有大量的新知识新名词,如 MVC,MVVM,Flux 这些设计模式就弄得很多同学傻傻分不清。这时候又见到别人讨论什么函数式编程,更是一脸懵逼了。
我们大多听过面向对象编程,面向过程编程,那啥又是函数式编程呢?在我们前端开发中又有哪些应用场景?我抱着这个疑惑,初步的学习了下。 (此文仅是学习,无甚干货)。
函数式编程(Functional Programming,后面简称 FP),维基百科的定义是:
是一种编程范型,它将电脑运算视为数学上的函数计算,并且避免使用程序状态以及易变对象。函数编程语言最重要的基础是λ演算(lambda calculus)。而且λ演算的函数可以接受函数当作输入(引数)和输出(传出值)。比起命令式编程,函数式编程更加强调程序执行的结果而非执行的过程,倡导利用若干简单的执行单元让计算结果不断渐进,逐层推导复杂的运算,而不是设计一个复杂的执行过程。
我来尝试理解下这个定义,好像就是说,在敲代码的时候,我要把过程逻辑写成函数,定义好输入参数,只关心它的输出结果。而且可以把函数作为输入输出。感觉好像平常写 js 时,就是这样的嘛!
网上 FP 的定义与特性琳琅满目。各种百科、博客、一些老师的网站上都有大同小异的介绍。为了方便阅读,我列下几个好像比较重要的特性,并附上我的第一眼理解。
还有一些衍生的特性,如柯里化与组合,三言两语说不清,就不阐述了,有兴趣的同学可以自己再了解了解。
React 就是典型的 FP。它不同于 Vue 这样的 MVVM 框架,它仅仅是个 View 层。
ReactView = render(data) 它只关心你的输入,最终给你返回相应视图。所以你休想在 react 组件中去修改父组件的状态,更没有与 dom 的双向绑定。
这个是框架上的应用,那么在我们平常书写 JavaScript 时有哪些应用呢?换句话说,平常书写 js 时候,遇到什么情况,我们采用 FP 会更好。
从最常见的入手吧,如典型的操作数组:
- // 从users中筛选出年龄大于15岁的人的名字
- const users = [{
- age: 10,
- name: '张三',
- },
- {
- age: 20,
- name: '李四'
- },
- {
- age: 30,
- name: '王五'
- }];
- // 过程式
- const names = [];
- for (let i = 0; i < users.length; i++) {
- if (users[i].age > 15) {
- names.push(users[i].name);
- }
- }
- // 函数式
- const names = users.filter(u = >u.age > 15).map(u = >u.name);
嗯,代码精简了很多,但是貌似带来了更大的开销。如果是非常大的数据,非常多的筛选工作,那就会循环多次。
这里得想到刚刚的惰性计算。按照惰性求值的要求,应该是要最后返回结果时,才真正去筛选年纪并得到姓名数组。
然而 JavaScript 的数组并不支持惰性求值。这时候我们得上一些工具库,如 Lodash 。可以看下它文档中的例子: _.chain 。
好像也没好到哪里去啊,不就是把多行代码变一行嘛?说的那么玄乎,还多了性能开销,然后又跟我说得上个工具库。。。
说的好像很有道理,但是 for 循环是有个弊端的,它产生了变量 i,而这个变量又是不可控的,如果业务逻辑一复杂,谁知道它循环到什么时候 i 有没有发生变化,然后导致循环出问题呢?
我们再看一个与 DOM 交互的场景:
假如页面有一个按钮 button ,我们需要求出用户点击了几次,但是一秒钟内重复点击的不算。传统方法会这么写。
- var count = 0;
- var rate = 1000;
- var lastClick = Date.now() - rate;
- var button = document.querySelector('button');
- button.addEventListener('click', () => {
- if (Date.now() - lastClick >= rate) {
- console.log(`Clicked ${++count} times`);
- lastClick = Date.now();
- }
- });
妥,完全没问题。但是发现多了很多状态,count,rate,lastClick,还得对比来对比去。那如果用 FP 会是怎么样的呢?
抱歉。。。没法写。。。除非很强大的编程能力,自己封装好方法去处理。所以在这里,我们可以上个工具 --- Rx.js ,上述的例子就是 rxjs 中引用的,我们看它是如何优雅地处理的。
- var button = document.querySelector('button');
- Rx.Observable.fromEvent(button, 'click')
- .throttleTime(1000) // 每隔1000毫秒才能触发事件
- .scan(count => count + 1, 0) // 求值,默认值是0
- .subscribe(count => console.log(`Clicked ${count} times`)); // 订阅结果、输出值
巧夺天工!再也不用去管理状态了,不需要声明一堆变量,修改来修改去,判断来判断去,简直完美。
平常我们有很多需要更新 dom 的异步操作,如搜索行为:用户连续输入查询值,如果停顿半秒就执行搜索,如果搜索了多次,发起了多次请求,那只返回最终输入的那次搜索结果。
闭上眼想想,你之前是怎么实现的。反正我都是设置开始时间,结束时间,上次时间,等等变量。繁琐,而且不可控。
当我们以 FP 的思想去实现时,就会想方设法的减少变量,来优雅程序。最常见的方法就是用下别人的工具库来实现它。当然有些简单的场景也可以自己实现,最主要的还是要有这个意识。
其实我们平常已经写了一些 FP 了,只是我们没意识到,或者没怎么写好。就好比闭包,很多人都不了解闭包的概念,但实际上已经写了很多闭包代码。其实闭包本身也是函数式编程的一个应用。
鉴于我自己理解也不深,没法多阐述 FP 的应用,大家如果有兴趣,可以多了解了解。
总结一下 FP 的优劣,以便于我们在实际开发中,能更好的抉择是否采用 FP。
个人觉得,FP 还是好的。对于开发而言,确确实实能优化我们的代码,熟悉之后,也能提高编程效率。对于编程本身而言,也能拓展我们的思维,不局限在过程式的编程代码。
在编写 JS 中,可以尽量的运用 FP 的思维,如不可变量、纯函数、惰性求值。但也不必教条式的遵循函数式编程,一定要怎样怎样。比如我们看下知乎大 V 某温的一个回答: 传送门 。
唉,做个页面仔不容易啊。但是不想当大牛的页面仔不是好页面仔!
来源: http://www.open-open.com/lib/view/open1498025847699.html