JavaScript 函数式编程挑战精解:从基础到高阶技巧
2025-07-08 07:51:23作者:胡唯隽
1. 计算器接口设计
这个挑战要求我们设计一个计算器接口,能够对两个数字执行加、减、乘、除运算。解决方案采用了揭示模块模式,这是一种常见的JavaScript设计模式。
function Calulator(num1, num2) {
function sum() {
return num1 + num2;
}
function difference() {
return num1 - num2;
}
function product() {
return num1 * num2;
}
function dividend() {
return Math.floor(num1 / num2);
}
return { sum, difference, product, dividend };
}
技术要点:
- 利用闭包保持对初始参数num1和num2的访问
- 返回一个对象,暴露四个计算方法作为接口
- 除法使用Math.floor确保结果为整数
2. 私有计数器实现
这个挑战展示了如何使用闭包创建私有状态,这是JavaScript中实现数据封装的重要技术。
function privateCounter(){
var count = 0;
return {
increment: function(val = 1){
count += val;
},
retrieve: function(){
return count;
}
}
}
技术要点:
- 使用IIFE(立即调用函数表达式)创建私有作用域
- count变量对外部完全不可见,实现了真正的私有性
- 默认参数val=1提供了灵活性
3. 实现bind方法的polyfill
bind方法是函数式编程中的重要工具,这个polyfill展示了它的基本原理。
if (!Function.prototype.bind) {
Function.prototype.bind = function (context) {
var fn = this;
var fnArgs = Array.prototype.slice.call(arguments, 1);
return function () {
var allArgs = fnArgs.concat(Array.prototype.slice.call(arguments));
fn.apply(context, allArgs);
};
};
}
技术要点:
- 使用apply方法设置this上下文
- 通过arguments对象处理参数
- 实现了函数柯里化的基本形式
4. 软绑定(Soft Binding)实现
软绑定是比硬绑定更灵活的一种绑定方式,允许在调用时覆盖绑定上下文。
function softBind(fn, context) {
var fnArgs = Array.prototype.slice.call(arguments, 2);
return function () {
var allArgs = fnArgs.concat(Array.prototype.slice.call(arguments));
var finalContext = !this || this === window ? context : this;
fn.apply(finalContext, allArgs);
};
}
应用场景:
- 当需要默认绑定但允许覆盖时
- 创建更灵活的API设计
5. 函数柯里化:乘法函数
这个简单的例子展示了函数柯里化的基本概念。
function multiply(num1) {
return function (num2) {
return num1 * num2;
};
}
柯里化优势:
- 参数复用
- 延迟执行
- 提高函数组合性
6. 通用柯里化函数
这个更高级的实现可以柯里化任何函数,展示了函数式编程的强大之处。
function curryFunc(fn) {
return function curry(...args) {
if(args.length < fn.length) {
return curry.bind(this, ...args);
}
return fn.apply(this, args);
}
}
技术要点:
- 递归处理参数
- 使用函数length属性判断参数是否足够
- 支持部分应用(partial application)
7. 防抖(debounce)实现
防抖是性能优化的重要技术,特别适用于高频事件如滚动、输入等。
function debounce(fn, delay, context) {
let timer;
return function (...args) {
if (timer) clearTimeout(timer);
context = this ?? context;
timer = setTimeout(() => {
fn.apply(context, args);
}, delay);
};
}
应用场景:
- 搜索框输入建议
- 窗口大小调整事件
- 防止重复提交
8. 节流(throttle)实现
节流是另一种性能优化技术,确保函数在一定时间内只执行一次。
function throttle(fn, delay, context) {
let timer;
let lastArgs;
return function (...args) {
lastArgs = args;
context = this ?? context;
if (timer) return;
timer = setTimeout(() => {
fn.apply(context, lastArgs);
clearTimeout(timer);
}, delay);
};
}
与防抖的区别:
- 防抖:在事件停止触发后执行
- 节流:按照固定频率执行
9. 采样(sampler)函数实现
采样函数限制函数在N次调用中只执行一次,是另一种流量控制技术。
function sampler(fn, count, context) {
let counter = 0;
return function (...args) {
lastArgs = args;
context = this ?? context;
if (++counter !== count) return;
fn.apply(context, args);
counter = 0;
};
}
应用场景:
- 高频事件中获取代表性样本
- 日志记录时减少数据量
10. 单例模式实现
单例是常见的设计模式,确保一个类只有一个实例。
var Singleton = (function () {
var instance;
function createInstance() {
var object = new Object("I am the instance");
return object;
}
return {
getInstance: function () {
if (!instance) {
instance = createInstance();
}
return instance;
},
};
})();
技术要点:
- 使用IIFE创建闭包
- 延迟初始化(lazy initialization)
- 全局访问点
11. 切换(toggle)函数实现
这个函数在多个值之间循环切换,展示了闭包的实用价值。
function toggle(...values) {
let state = -1;
const length = values.length;
return function () {
state = (state + 1) % length;
return values[state];
};
}
应用场景:
- UI状态切换
- 循环播放内容
- 多主题切换
12. 范围(range)函数实现
这个函数生成数字范围数组,支持两种调用方式,展示了函数重载的技术。
function range(start, end) {
if (end === undefined) {
return function (end) {
return range(start, end);
};
}
const arr = [];
for (let i = start; i <= end; i++) {
arr.push(i);
}
return arr;
}
技术要点:
- 柯里化支持
- 参数检查
- 数组生成
13. 记忆化(memoization)实现
记忆化是优化重复计算的强大技术,特别适用于递归函数。
function memoize(fn) {
const cache = new Map();
return function () {
const key = JSON.stringify(arguments);
if (cache.has(key)) {
return cache.get(key);
} else {
cache.set(key, fn(...arguments));
return cache.get(key);
}
};
}
应用场景:
- 斐波那契数列计算
- 复杂数学运算
- API响应缓存
14. 多形态求和函数
这个高级函数支持多种调用方式,展示了JavaScript的灵活性。
function sum(a, b, c) {
if (a !== undefined && b !== undefined && c !== undefined) {
return a + b + c;
}
if (a !== undefined && b !== undefined) {
return function (c) {
return sum(a, b, c);
};
}
return function (b, c) {
if (b !== undefined && c !== undefined) {
return sum(a, b, c);
}
return function (c) {
return sum(a, b, c);
};
};
}
技术要点:
- 参数检查
- 条件返回
- 递归调用
15. 链式累加求和
这个函数支持无限链式调用,直到无参数时返回结果。
function sum(a) {
return function (b) {
if (b === undefined) {
return a;
}
return sum(a + b);
};
}
技术要点:
- 闭包保持状态
- 终止条件检查
- 递归返回
16. 使用Proxy实现函数调用日志
Proxy是ES6的强大特性,可以拦截对象操作。
function generateSecretObject(key, value) {
return { [key]: value };
}
generateSecretObject = new Proxy(generateSecretObject, {
apply(target, context, args) {
console.log(`${target.name} function is accessed at ${new Date()}`);
return target.apply(context, args);
},
});
Proxy的其他用途:
- 数据验证
- 性能监控
- 自动填充
- 观察者模式
17. 发布-订阅模式实现
发布-订阅是消息传递的重要模式,广泛应用于事件处理。
function pubSub() {
const subscribers = [];
function publish(data) {
subscribers.forEach((subscriber) => subscriber(data));
}
function subscribe(fn) {
subscribers.push(fn);
}
return {
publish,
subscribe,
};
}
模式特点:
- 松耦合
- 一对多通知
- 异步处理支持
总结
这些JavaScript函数式编程挑战涵盖了从基础到高级的各种技术,包括:
- 闭包和作用域控制
- 高阶函数和函数组合
- 柯里化和部分应用
- 流量控制技术(防抖、节流)
- 设计模式实现(单例、发布订阅)
- 元编程技术(Proxy)
掌握这些技术可以显著提升JavaScript编程能力,写出更简洁、更可维护、性能更好的代码。