闭包与纯函数
和纯函数相反的就是“不纯函数”(Impure Function),一个函数之所以不纯,可能做了下面这些事情:
·改变全局变量的值。
·改变输入参数引用的对象,就像上面不是纯函数的arrayPush实现。
·读取用户输入,比如调用了alert或者confirm函数。
·抛出一个异常。
·网络输入/输出操作,比如通过AJAX调用一个服务器的API。
·操作浏览器的DOM。
上面还只是不纯函数的一部分表现,其实,有一个很简单的判断函数纯不纯的方法,就是假设将一个函数调用替换为一个预期返回的常数,程序运行结果是否一样。
数据不可变(Immutable)是函数式编程非常重要的一个概念,对于刚刚接触这个概念的朋友,可能会觉得莫名其妙,因为众所周知程序就是用代码指令在操作数据,如果数据不能变化,那一个程序又能够干什么有用的事情?
程序要好发挥作用当然是要产生变化的数据,但是并不意味着必须要去修改现有数据,替换方法是通过产生新的数据,来实现这种“变化”,也就是说,当我们需要数据状态发生改变时,保持原有数据不变,产生一个新的数据来体现这种变化。
不可改变的数据就是Immutable数据,它一旦产生,我们就可以肯定它的值永远不会变,这非常有利于代码的理解。
其实,你可能已经体会到了Immutable数据类型的好处,在JavaScript中,字符串类型、数字类型就是不可改变的数据,使用这两种类型的数据给你带来的麻烦比较少。相反,JavaScript中大部分对象都是可变的,比如JavaScript自带的原生数组类型,数组的push、pop、sort函数都会改变一个数组的内容,由此引发的bug可不少。这些不纯的函数导致JavaScript天生不是一个纯粹意义上的函数式编程语言。
闭包简单说,就是引用了自由变量的函数。这里的关键是“自由变量”,其实这个自由变量,扮演的作用是为这个函数调用提供了一个“上下文”,而上下文的不同,将对入参相同的函数调用造成不同的影响,它包括:
- 函数的行为不同,即函数调用改变其上下文中的其它变量,如例子中的 setName();
- 函数的返回值不同,如例子中的 getName()。
和闭包相对的,是一种称为“纯函数”(Pure Function)的东西,即函数不允许引用任何自由变量。因此,和上面两条“影响”对应,纯函数的调用必须满足如下特性:
- 函数的调用不允许改变其所属的上下文;
- 相同入参的函数调用一定能得到相同的返回值。
- 闭包的调用是不安全的,因为它可能改变对象的内部属性(闭包的上下文);同时它也不是幂等的,因为一次调用和多次调用可能产生不同的结果。
- 纯函数的调用是安全的,也是幂等的。
于是,我们又一次发现,技术是相通,是可以联想和类比的。本质上,它们围绕的都是一个方法(函数)是否引用和改变外部状态的问题。闭包本身是一个很简单的机制,但是,它可以带来丰富的语言高级功能特性,比如高阶函数。