在 JavaScript 中, 函数是"一等公民"(First-class Citizen). 这可不是随便给的称号,它意味着:
1. 函数可以赋值给变量
2. 函数可以作为参数传递
3. 函数可以作为其他函数的返回值
// 1. 赋值给变量const greet = function(name) {return `Hello, ${name}!`;};console.log(greet('Alice')); // "Hello, Alice!"// 2. 作为参数传递function sayHello(greetingFn, name) {console.log(greetingFn(name));}sayHello(greet, 'Bob'); // "Hello, Bob!"// 3. 作为返回值function createGreeter(greeting) {return function(name) {return `${greeting}, ${name}!`;};}const sayHi = createGreeter('Hi');console.log(sayHi('Charlie')); // "Hi, Charlie!"
二、高阶函数
高阶函数(Higher-order Function)是指能够操作其他函数的函数, 它要么接受函数作为参数, 要么返回一个函数, 或者两者兼有. 2.1 接受函数作为参数:最常见的例子就是数组的方法了:
2.2 返回函数的函数:这种模式在创建特定功能的函数时特别有用:const numbers = [1, 2, 3, 4, 5];// map 是一个高阶函数,它接受一个函数作为参数const doubled = numbers.map(function(num) {return num * 2;});console.log(doubled); // [2, 4, 6, 8, 10]// 用箭头函数更简洁const squared = numbers.map(num => num * num);console.log(squared); // [1, 4, 9, 16, 25]
2.3 创建验证器(应用示例):function multiplyBy(factor) {return function(number) {return number * factor;};}const triple = multiplyBy(3);console.log(triple(5)); // 15const double = multiplyBy(2);console.log(double(5)); // 10
function createValidator(testFn, errorMsg) {return function(value) {if (!testFn(value)) {throw new Error(errorMsg);}return true;};}const isNumber = createValidator(val => typeof val === 'number','必须是一个数字');const isPositive = createValidator(val => val > 0,'必须是正数');try {isNumber('abc'); // 报错:必须是一个数字} catch (e) {console.error(e.message);}try {isPositive(-5); // 报错:必须是正数} catch (e) {console.error(e.message);}
三、闭包
闭包(Closure)是 JavaScript 中一个超级重要的概念. 简单说,闭包让函数可以"记住"创建时的环境.
这里的神奇之处在于,function createCounter() {let count = 0; // 这个变量被"封闭"在返回的函数中return function() {count += 1;return count;};}const counter = createCounter();console.log(counter()); // 1console.log(counter()); // 2console.log(counter()); // 3
count 变量本该在 createCounter 执行完后就被销毁,但由于返回的函数引用了它, 它就活下来了!闭包的实际应用:
function createBankAccount(initialBalance) {let balance = initialBalance;return {deposit: function(amount) {balance += amount;return balance;},withdraw: function(amount) {if (amount > balance) {throw new Error('余额不足');}balance -= amount;return balance;},getBalance: function() {return balance;}};}const account = createBankAccount(100);console.log(account.getBalance()); // 100account.deposit(50);console.log(account.getBalance()); // 150account.withdraw(75);console.log(account.getBalance()); // 75// account.balance; // 无法直接访问,实现了真正的私有变量
四、函数柯里化
柯里化(Currying)是把接受多个参数的函数变换成接受单一参数(最初函数的第一个参数)的函数, 并且返回接受余下参数的新函数的技术.听起来有点绕, 我们来看一个例子:
手动写柯里化函数太麻烦, 我们可以写一个通用的柯里化工具函数:// 普通函数function add(a, b, c) {return a + b + c;}// 柯里化版本function curriedAdd(a) {return function(b) {return function(c) {return a + b + c;};};}console.log(add(1, 2, 3)); // 6console.log(curriedAdd(1)(2)(3)); // 6
柯里化的实际应用:function curry(fn) {return function curried(...args) {if (args.length >= fn.length) {return fn.apply(this, args);} else {return function(...args2) {return curried.apply(this, args.concat(args2));};}};}// 使用const curriedAdd = curry(add);console.log(curriedAdd(1)(2)(3)); // 6console.log(curriedAdd(1, 2)(3)); // 6console.log(curriedAdd(1)(2, 3)); // 6
// 日志函数function log(date, importance, message) {console.log(`[${date.getHours()}:${date.getMinutes()}] [${importance}] ${message}`);}// 柯里化版本const curriedLog = curry(log);// 创建特定时间的日志函数const logNow = curriedLog(new Date());// 创建重要级别的日志函数const logNowWarning = logNow('WARNING');// 使用logNowWarning('内存泄漏检测'); // [当前时间] [WARNING] 内存泄漏检测
柯里化的好处是我们可以创建一些基础函数, 然后通过部分应用参数来生成更具体的函数.
五、函数组合
函数组合(Function Composition)是把多个函数组合成一个新函数的过程. 基本组合示例:
多函数组合示例:function compose(f, g) {return function(x) {return f(g(x));};}function double(x) {return x * 2;}function square(x) {return x * x;}const doubleThenSquare = compose(square, double);console.log(doubleThenSquare(5)); // 100 (先double得到10,再square得到100)const squareThenDouble = compose(double, square);console.log(squareThenDouble(5)); // 50 (先square得到25,再double得到50)
function compose(...fns) {return function(x) {return fns.reduceRight((acc, fn) => fn(acc), x);};}function add1(x) {return x + 1;}function mul2(x) {return x * 2;}function sub3(x) {return x - 3;}const transform = compose(sub3, mul2, add1);console.log(transform(5)); // 9// 计算过程:// 1. add1(5) => 6// 2. mul2(6) => 12// 3. sub3(12) => 9
六、偏函数应用
偏函数应用(Partial Application)是指固定一个函数的一些参数, 然后产生另一个更小元的函数. 看一个示例:
// 简单的偏函数实现function partial(fn, ...presetArgs) {return function(...laterArgs) {return fn.apply(this, [...presetArgs, ...laterArgs]);};}// 使用function greet(greeting, name, punctuation) {return `${greeting}, ${name}${punctuation}`;}const sayHello = partial(greet, 'Hello');const sayHelloToJohn = partial(greet, 'Hello', 'John');console.log(sayHello('Alice', '!')); // "Hello, Alice!"console.log(sayHelloToJohn('!!!')); // "Hello, John!!!"
柯里化: 每次只接受一个参数,返回一个新函数, 直到所有参数都收集完.
偏函数: 一次可以接受多个参数,固定这些参数, 返回一个需要剩余参数的函数.
七、省略不必要的参数, Point-Free风格
Point-Free 风格是一种编程风格,其中函数定义不显式地提到它们所操作的数据(即没有"点").我们来看个示例:
// 非 Point-Freefunction isOdd(x) {return x % 2 === 1;}const numbers = [1, 2, 3, 4, 5];console.log(numbers.filter(x => isOdd(x)));// Point-Freeconst isOdd = x => x % 2 === 1;console.log(numbers.filter(isOdd));
八、函子
函子是一个实现了 map 方法的对象, 它允许你在不直接操作值的情况下对值进行转换. 我们来看个简单的示例:
const plus1 = x => x + 1;[
class Box {constructor(value) {this.value = value;}map(fn) {return new Box(fn(this.value));}toString() {return `Box(${this.value})`;}}const result = new Box(5).map(x => x * 2).map(x => x + 1);console.log(result.toString()); // "Box(11)"
![]() |
Austin Liu 刘恒辉
Project Manager and Software Designer E-Mail:lzhdim@163.com Blog:https://lzhdim.cnblogs.com 欢迎收藏和转载此博客中的博文,但是请注明出处,给笔者一个与大家交流的空间。谢谢大家。 |




浙公网安备 33010602011771号