首页
/ JavaScript 函数式编程挑战精解:从基础到高阶技巧

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函数式编程挑战涵盖了从基础到高级的各种技术,包括:

  1. 闭包和作用域控制
  2. 高阶函数和函数组合
  3. 柯里化和部分应用
  4. 流量控制技术(防抖、节流)
  5. 设计模式实现(单例、发布订阅)
  6. 元编程技术(Proxy)

掌握这些技术可以显著提升JavaScript编程能力,写出更简洁、更可维护、性能更好的代码。