JavaScript Point-free风格
什么是Point-free风格?
Point-free编程(也称为"无值"或"无参数"风格)是函数式编程中一种特殊的代码编写风格,在这种风格中,函数定义不显式地指定其参数。"Point"在这里指的是函数的参数,所以"Point-free"意味着"没有明确提及参数"。
备注
"Point-free"并不意味着函数没有参数,而是在定义函数时不直接引用参数。
这种风格主要通过函数组合来实现,而不是通过指定参数来表达数据流。
传统风格与Point-free风格对比
让我们看一个简单的例子来对比传统编程风格和Point-free风格:
传统风格
// 传统风格 - 明确声明参数
const getFullName = person => {
return person.firstName + ' ' + person.lastName;
};
const getUppercaseName = person => {
return getFullName(person).toUpperCase();
};
// 使用函数
const person = { firstName: 'John', lastName: 'Doe' };
console.log(getUppercaseName(person)); // "JOHN DOE"
Point-free风格
// Point-free风格 - 使用函数组合
const getFirstName = person => person.firstName;
const getLastName = person => person.lastName;
const concat = (a, b) => a + ' ' + b;
const toUpperCase = str => str.toUpperCase();
// 使用函数组合工具
const pipe = (...fns) => x => fns.reduce((v, f) => f(v), x);
const getFullName = pipe(
person => concat(getFirstName(person), getLastName(person))
);
const getUppercaseName = pipe(
getFullName,
toUpperCase
);
// 使用函数
const person = { firstName: 'John', lastName: 'Doe' };
console.log(getUppercaseName(person)); // "JOHN DOE"
在Point-free风格中,getUppercaseName
函数定义中没有直接提及其参数,而是通过函数组合来表达数据流。
Point-free风格的核心概念
1. 函数组合
函数组合是Point-free风格的核心,它允许我们创建新的函数,而不需要明确声明参数:
// 基础函数组合工具
const compose = (f, g) => x => f(g(x));
const pipe = (f, g) => x => g(f(x));
// 更通用的组合工具
const composeMulti = (...fns) => x => fns.reduceRight((acc, fn) => fn(acc), x);
const pipeMulti = (...fns) => x => fns.reduce((acc, fn) => fn(acc), x);
// 使用
const add2 = x => x + 2;
const multiply3 = x => x * 3;
const add2ThenMultiply3 = pipe(add2, multiply3);
console.log(add2ThenMultiply3(5)); // (5 + 2) * 3 = 21
2. 柯里化 (Currying)
柯里化是将一个接受多个参数的函数转换为一系列接受单个参数的函数的过程,这对于实现Point-free风格非常有用:
// 柯里化函数
const curry = (fn) => {
const arity = fn.length;
return function curried(...args) {
if (args.length >= arity) {
return fn.apply(this, args);
}
return (...moreArgs) => {
return curried.apply(this, [...args, ...moreArgs]);
};
};
};
// 示例
const add = curry((a, b, c) => a + b + c);
// 以下调用都会返回6
console.log(add(1, 2, 3));
console.log(add(1)(2, 3));
console.log(add(1, 2)(3));
console.log(add(1)(2)(3));
3. 高阶函数
高阶函数是接受函数作为参数 和/或返回函数的函数,它们是实现Point-free风格的重要工具:
// 高阶函数示例 - 从数组中过滤偶数
const numbers = [1, 2, 3, 4, 5, 6];
// 传统方式
const evenNumbers1 = numbers.filter(function(n) {
return n % 2 === 0;
});
// Point-free方式
const isEven = n => n % 2 === 0;
const evenNumbers2 = numbers.filter(isEven);
console.log(evenNumbers2); // [2, 4, 6]
实现Point-free风格的常用工具函数
要有效地使用Point-free风格,我们需要一些辅助函数:
// prop函数 - 获取对象的属性
const prop = curry((key, obj) => obj[key]);
// map函数 - 对数组的每个元素应用函数
const map = curry((fn, arr) => arr.map(fn));
// filter函数 - 过滤数组
const filter = curry((predicate, arr) => arr.filter(predicate));
// reduce函数 - 规约数组
const reduce = curry((fn, initial, arr) => arr.reduce(fn, initial));