- 简述
- 无副作用(No Side Effects)
- 高阶函数(High-Order Function)
- 科里化(Currying)
- 闭包(Closure)
- 不可变(Immutable)
- 惰性计算(Lazy Evaluation)
- Monad
一等公民
高阶函数(High-Order Function)是函数式编程思维中的重要条件,而满足该条件的编程语言则需要将函数作为该语言的一等公民来看待。符合一等公民的条件是:
- 函数可以作为一种数据类型的值,赋值于一个变量;
- 函数可以作为参数,在其他函数中进行传递;
- 函数可以作为返回值,在其他函数中返回;
将函数视作一等公民的语言有:JavaScript、Golang、Python、Scala、Lua、Lisp、Scheme等。同时,有着越来越多的其他语言看上了函数式编程的出彩之处,以其特有的方式实现着符合自身编程方式的高阶函数。
函数类型
在 JavaScript
中,函数是数据类型 object
的子类型——即是指一种对象类型。我们可以通过 typeof
操作符检测一个值是否是一个函数类型:
// 一个函数声明
function foo(x){
return x + 10;
}
typeof foo; // 返回值:'function'
然而,实际上在 JavaScript
中,函数并非是基本的数据类型,函数隶属于对象类型。能够使用 typeof
操作符进行检测仅仅是语言提供的便利:
// 声明一个 number 类型的变量
const n = 13;
// 一个函数声明
function foo(x){
return x + 10;
}
typeof foo; // 结果:'function'
typeof n; // 结果:'number'
// 检测 n 所持有的值是否是对象
n instanceof Object; // 结果:false
// 检测 foo 所持有的值是否是对象
foo instanceof Function; // 结果:true
Function instanceof Object; // 结果:true
foo instanceof Object; // 结果:true
可见,函数是一个隶属于 Function
的对象,而 Function
本身又隶属于顶层 Object
,是它的子对象。因此,一个函数的实例,也隶属于 Object
,他们之间拥有间接的继承关系。
很多有 面向对象
经验的同学可能会想,实例化对象不是通过 new
关键字调用类的构造函数来进行的吗?这个问题很简单:拿 Java
例举,Java
中拥有字面量形式的对象声明方式 String str = "I like Java!";
,声明变量 str
的过程中实际上也构建了一个字符串对象,并没有显式的使用关键字 new
。
在 JavaScript
中,以字面量的方式构建对象有很多种,比如:
// 数组字面量
const arr = [1, 2, 3];
// 对象字面量
const obj = {id : 'xx001'};
// 函数字面量
function foo(){} // 函数声明
const bar = function(){} // 函数表达式
const baz = (x) => x + 3; // ES6提供的lambda表达式函数
函数入参
由于函数也是一个对象,因此函数也可以作为其他函数的参数,作为入参:
// 一个函数声明
const add2 = (x) => x + 2; // 参数x基础上加2的函数
const sub2 = (x) => x - 2; // 参数x基础上减2的函数
const result = (y, f) => y * f(y); // 参数y基础上乘以 函数参数f 的运算结果
result(4, add2); // 结果: 24
result(4, sub2); // 结果: 8
在 JavaScript
内置对象中,也有非常多的函数入参实例,比如 Array.prototype.map
函数的简单实现:
// 一个高仿的 Array.prototype.map 函数
const map = function(arr, f){
const t = [];
for(let i=0;i<arr.length; t.push(f(arr[i],i++,arr)));
return t;
}
// 声明一个数组
const a1 = [1,2,3,4];
// map函数调用:参数 f 所示为每一个值乘以3,并且返回一个新数组
const a2 = map(a1, (e) => e*3); // 结果:[3, 6, 9, 12]
函数返回值
函数可以作为参数,当然也可以作为返回值。作为返回值的函数,常用于缓存上一个函数的执行状态:
// 一个函数声明
const add2 = (x) => x + 2; // 参数x基础上加 2 的函数
const mul2 = (x) => x * 2; // 参数x基础上乘以 2 的函数
// 该函数用于计算,计算结果不会直接得出,而是缓存了用于计算的两个函数
const calc = function(f1, f2){
return (y) => f1( f2(y) );
}
// 根据缓存顺序不同,生成的新的函数执行过程也不同
const c01 = calc(add2, mul2);
c01(3); // 结果: 8
const c02 = calc(mul2, add2);
c02(3); // 结果:10
有疑问加站长微信联系(非本文作者)