side effects 在维基百科上直接翻译为 “函数副作用”。

首先要明确“副作用”这个词没有贬义成分,不是“负面作用”,而是“在满足主要功能(主作用?)的同时,顺便完成了一些其他的副要功能”。有个答主说“附作用”表示附加作用,我觉得很好。

从数学的角度来讲,作为一个函数,例如 f(x) = 2x,这个函数,最重要的目的是什么?就是对于传入的x值,找到它所对应的值,在这个例子里就是 2乘以x。从编程的角度来说,传入的x 就是一个函数的参数,而找到的对应的值,就是函数的输出值,很多语言里用 return 这个关键字。所以 y = f(2),得到了 y = 4,这里y 被赋值了,很开心。函数也完成了它最主要的作用:对每个自变量 x 计算出一个相对应的因变量 y。

从这个角度来说,如果一个函数除了计算最终要返回的结果之外,还做了任何其他事情,都可以成为“副作用”,因为这不是一个函数的首要功能。至于这个“副作用”惊喜不惊喜,意外不意外,与它是不是副作用的定义本身无关。

有些时候,函数的副作用可能会比首要功能更重要。例如有 write() 这个函数,把一些字符写到一个文件里,传回true/false表示是否写成功,甚至不回传。这里明显可以看出,我们叫这个函数,主要还是希望它能把东西写到文件里的。可惜从数学的角度来讲,函数的首要功能永远都是返回值,所以这里“写文件”操作也被定义为“副作用”。

实际编程中,我们不一定需要尽量避免副作用,但是一定要尽量避免“意外的”副作用。比如我们可以通过合适的函数名或注释,提醒调用函数的人,这个函数有副作用。

举个简单例子,假如我们设计一个函数,从一个数组里找出最大的数字,两种写法:第一种,直接遍历数组,找到最大的数字并返回结果,O(n) 代码略。这个函数没有副作用,我们如果给它取名为 findMax() (找最大值)还算妥当。第二种写法,对传入的数组进行从大到小排序,之后返回数组的第一项,O(n log n) 代码略。这个函数会把数组原先的顺序破坏,算是副作用(但不一定是坏事)。不过如果我们依然给函数取名为 findMax() 的话,那别人发现自己的数组顺序被破坏了可能会凌乱。为了提醒大家,比如我们可以给它取名为 sortArrayAndReturnMax() (排序并找最大值) 至少就比 findMax() (找最大值)更清晰些。(举例而已,实际使用中最好还是拆成两个函数吧。)