Libon

Functional programming and Pure Function

2023/06/08 何为 “一等公民”?纯函数又是什么? #JavaScript

ToC

“一等公民”是什么?

根据维基百科,编程语言中一等公民的概念是由英国计算机学家Christopher Strachey提出来的,时间则早在上个世纪 60 年代,那个时候还没有个人电脑,没有互联网,没有浏览器,也没有 JavaScript。关于一等公民,我找到一个权威的定义,来自于一本书《Programming Language Pragmatics》,这本书是很多大学的程序语言设计的教材。

其实不止我们现实社会中有三六九等的概念,在JavaScript世界中也存在着“一等公民”这类说法,而在 JS 世界中“一等公民”则是指 JS 函数。

In general, a value in a programming language is said to have first-class status if it can be passed as a parameter, returned from a subroutine, or assigned into a variable.

为什么函数是“一等公民”?

“一等公民”在英文中是 “First-class Function” ,而为什么JS会是一等公民还得从JS的历史开始说起。

JS的作者 Brendan Eich 在1995年4月入职了网景公司,Brendan Eich的主要方向和兴趣是函数式编程,网景公司招聘他的目的,是研究将Scheme语言作为网页脚本语言的可能性。Brendan Eich本人也是这样想的,以为进入新公司后,会主要与Scheme语言打交道。仅仅一个月之后,1995年5月,网景公司做出决策,未来的网页脚本语言必须”看上去与Java足够相似”,但是比Java简单,使得非专业的网页作者也能很快上手。这个决策实际上将Perl、Python、Tcl、Scheme等非面向对象编程的语言都排除在外了。Brendan Eich被指定为这种”简化版Java语言”的设计师。但是,他对Java一点兴趣也没有。为了应付公司安排的任务,他只用10天时间就把Javascript设计出来了。总的来说,他的设计思路是这样的:

  1. 借鉴C语言的基本语法;
  2. 借鉴Java语言的数据类型和内存管理;
  3. 借鉴Scheme语言,将函数提升到”第一等公民”(first class)的地位;
  4. 借鉴Self语言,使用基于原型(prototype)的继承机制。

根据上述引用来说,在编程语言中,一等公民可以作为函数参数,可以作为函数返回值,也可以赋值给变量。例如,字符串在几乎所有编程语言中都是一等公民,字符串可以做为函数参数,字符串可以作为函数返回值,字符串也可以赋值给变量。对于各种编程语言来说,函数就不一定是一等公民了,比如Java 8 之前的版本。对于 JavaScript 来说,函数可以赋值给变量,也可以作为函数参数,还可以作为函数返回值,除此之外,还可以作为构造函数、类实例来运行,同时还可以作为立即执行函数,因此可以认定 JavaScript 中函数是一等公民。

JS的编程特点?

JS的编程特点是即可以按照面向对象的方式来组织系统架构,也可以使用面向过程的方式来完成功能(在JS中的class类是function函数的一个语法糖,具体实现还是依靠的function函数),又或者是两种方式结合起来形成一种“混编”语言的风格,这使得JS本身的可扩展性非常强。

因为其扩展性强的特点,使得JS语言本身非常灵活,再加上它是一门弱类型语言,这也更加笃定了它易学的特点,但缺点是它将变得不稳定更不可控,而函数式编程则是为了解决其不可控的问题。

First-class functions are a necessity for the functional programming style, in which the use of higher-order functions is a standard practice.

函数作为一等公民是函数式编程的必要条件。higher-order functions ,即高阶函数,就是使用函数作为参数的函数,它在函数式编程中很常见。而通俗点来讲,JavaScript 的函数也是对象,可以有属性,可以赋值给一个变量,可以放在数组里作为元素,可以作为其他对象的属性,什么都可以做,别的对象能做的它能做,别的对象不能做的它也能做,函数式编程还解决了面向对象中this指向的问题,这已经是将一等公民的身份焊死在身上了。

函数式编程有什么好处?

对比面向对象编程,函数式编程最大的特点就是纯函数,无“函数副作用”。这里引出了一个新的概念:“函数副作用”。引用百科的解释:

函数副作用是指函数在正常工作任务之外对外部环境所施加的影响。具体地说,函数副作用是指函数被调用,完成了函数既定的计算任务,但同时因为访问了外部数据,尤其是因为对外部数据进行了写操作,从而一定程度地改变了系统环境。函数的副作用也有可能是发生在函数运行期间,由于对外部数据的改变,导致了同步运行的外部函数受到影响。

纯函数的特点:

  1. 相同的输入结果返回相同的输入。即同样的参数不管调用多少次,返回的结果都是一致的;
  2. 函数内部的执行、调用不依赖于外部上下文。即便把这个纯函数放到其他地方,设置正确的参数,那么这个函数依旧可以正常运行;
  3. 函数的引用透明。函数内部的运行不会修改外部的变量,内部运行所用到的参数都由函数调用时传入;

简单来讲就是,函数内部的运行不会影响外部环境的其他变量或属性。这个特性使得函数式编程也能达到类似面向对象那样的层面,因为函数内部的运行不会依赖于外部的上下文环境,极大地提升了开发效率与代码的复用率,同时因为没有副作用,还能避免多个函数对统一变量的引用的原因,导致同时运行时产生竞争态(多个函数同时引用了一个对象,对象的值不能确保最后会被修改)的问题。

最后,这里还有一个误区,假设一个函数放在另一个对象上面,作为某个对象的方法再去调用它,这种形式就不能算作为函数是编程,比如这样:

var obj = {
	add() {}
}

obj.add() // !此时 add() 方法的调用依赖于 obj,所以这不能算作函数式编程,它必须独立存在

以上。

CD ..