箭头函数是ES6语法中加入的新特性,而它也是许多开发者对ES6仅有的了解,每当面试里被问到关于“ES6里添加了哪些新特性?”这种问题的时候,几乎总是会拿箭头函数来应付。箭头函数,=>,没有自己的this , arguments , super , new.target ,“书写简便,没有this”在很长一段时间内涵盖了大多数开发者对于箭头函数的全部认知(当然也包括我自己),如果只是为了简化书写,把=>按照function关键字来解析就好了,何必要弄出一个跟普通函数特性不一样的符号呢?答案就是:函数式编程(Functional Programming)。
如果你了解javascript这门语言就知道,它是没有类这个东西的,ES6新加入的Class关键字,也不过是语法糖而已,我们不断被要求使用面向对象编程的思想来使用javascript,定义很多类,用复杂的原型链机制去模拟类,是因为更多的开发者能够习惯这种描述客观世界的方式,《你不知道的javascript》中就明确指出原型链的机制其实只是实现了一种功能委托机制,即便不使用面向对象中的概念去描述它,这也是一种合乎逻辑的语言设计方案,并不会造成巨大的认知障碍。但需要明确的是,面向对象并不是javascript唯一的使用方式。
当然我也是接触到【函数式编程】的思想后才意识到,我并不是说【函数式编程】优于【面向对象】,每一种编程思想都有其适用的范围,但它的确向我展示了另一种对编程的认知方式,而且在流程控制的清晰度上,它的确比面向对象更棒,它甚至让我开始觉得,这才是javascript该有的打开方式。
如果你也曾以为【函数式编程】就是“用箭头函数把函数写的精简一些”,如果你也被各种复杂的this绑定弄的晕头转向,那么就一起来看看这个胖箭头指向的新世界——Functional Programming吧!
二. 更贴近本能的思维方式假如有这样一个题目:
在传统编程中,你的编码过程大约是这样:
let resolveYX = (x) => 3*x*x + 2*x + 1; let resolveZY = (y) => 4*y*y*y + 5*y*y + 6; let resolveRZ = (z) => (2*z*z - 4)/3; let y = resolveYX(2); let z = resolveZY(y); let result = resolveRZ(z);我们大多时候采用的方式是把程序的执行细节用程序语言描述出来。但是如果你把这道题拿给一个不懂编程的学生来做,就会发现大多数时候他们的做法会是下面的样子:
先对方程进行合并和简化,最后再代入数值进行计算得到结果就可以了。有没有发现事实上你自己在不写代码的时候也是这样做的,因为你很清楚那些中间变量对于得到正确的结果来说没有什么意义,而这样解题效率更高,尤其是当前面的环节和后面的环节可以抵消掉某些互逆的运算时,这样合并的好处可想而知。
而今天的主角【函数式编程】,可以看做是这种思维方式在程序设计中的应用,我并不建议非数学专业的作者从范畴论的角度去解释函数式编程,因为术语运用的准确性会造成难以评估的影响,很可能达不到技术交流的目的,反而最终误人子弟。
三. 函数式编程假如对某个需求的实现,需要传入x,然后经历3个步骤后得到一个答案y,你会怎样来实现呢?
3.1 传统代码的实现这样一个需求在传统编程中最容易想到的就是链式调用:
function Task(value){ this.value = value; } Task.prototype.step = function(fn){ let _newValue = fn(this.value); return new Task(_newValue); } y = (new Task(x)).step(fn1).step(fn2).step(fn3);你或许在jQuery中经常见到这样的用法,或者你已经意识到上面的函数实际上就是Promise的简化原型(关于Promise相关的知识可以看《javascript基础修炼(7)——Promise,异步,可靠性》这篇文章),只不过我们把每一步骤包裹在了Task这个容器里,每个动作执行完以后返回一个新的Task容器,里面装着上一个步骤返回的结果。
3.2 函数式代码推演