前端进阶之旅前端进阶之旅
  • 基础篇HTML/CSS/JS 打底
  • 进阶篇原理与工程化
  • 高频篇面试最常问的那批
  • 精选篇按模块收敛的总结
  • 手写篇常考代码手写实现
  • 面经篇真实面试问题复盘
  • AI 篇NEWAI 时代的前端考点
  • 历年面经NEW按年份追踪真实考点
  • 每日一题每天一道,攒手感
  • 专项自测100 题快速查漏
  • 小程序题库小程序专项刷题
  • 算法题库NEW在线编码即时判题
  • 知识卡片NEW碎片时间过考点
  • 面试题大全常见问题解析
  • AI 答疑NEW随时提问,即时解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • AI 定制路线NEW按你的简历现排
  • AI 知识地图NEW串起全站知识点
  • 原理篇React / Vue 源码拆解
  • HTTP从报文一路讲到 HTTPS
  • 浏览器渲染、事件循环、进程
  • 计算机基础Linux、网络、操作系统
  • 设计模式23 种模式怎么用
  • Node学习指南从环境搭建到服务端
  • NPM工作流script、依赖与发布
  • Docker容器化部署上手
  • Canvas图形与动画实战
  • 前端系统进阶学习大型项目工程化
  • 前端综合文章长期沉淀的实践文
  • 思维导图知识点全景图
  • 学习路线按图索骥不跑偏
  • AI 热点NEWAI 每日动态
  • 公众号动态公众号历史文章
  • 博客动态站长的技术博客
  • 开发者导航常用工具与文档站
  • 基础篇HTML/CSS/JS 打底
  • 进阶篇原理与工程化
  • 高频篇面试最常问的那批
  • 精选篇按模块收敛的总结
  • 手写篇常考代码手写实现
  • 面经篇真实面试问题复盘
  • AI 篇NEWAI 时代的前端考点
  • 历年面经NEW按年份追踪真实考点
  • 每日一题每天一道,攒手感
  • 专项自测100 题快速查漏
  • 小程序题库小程序专项刷题
  • 算法题库NEW在线编码即时判题
  • 知识卡片NEW碎片时间过考点
  • 面试题大全常见问题解析
  • AI 答疑NEW随时提问,即时解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • AI 定制路线NEW按你的简历现排
  • AI 知识地图NEW串起全站知识点
  • 原理篇React / Vue 源码拆解
  • HTTP从报文一路讲到 HTTPS
  • 浏览器渲染、事件循环、进程
  • 计算机基础Linux、网络、操作系统
  • 设计模式23 种模式怎么用
  • Node学习指南从环境搭建到服务端
  • NPM工作流script、依赖与发布
  • Docker容器化部署上手
  • Canvas图形与动画实战
  • 前端系统进阶学习大型项目工程化
  • 前端综合文章长期沉淀的实践文
  • 思维导图知识点全景图
  • 学习路线按图索骥不跑偏
  • AI 热点NEWAI 每日动态
  • 公众号动态公众号历史文章
  • 博客动态站长的技术博客
  • 开发者导航常用工具与文档站
首页程序员面试题库JavaScript 函数 策略模式 取舍
设计设计模式可维护代码

在 JavaScript 中直接传递函数与定义策略类实现策略模式各有什么取舍?

在JavaScript中,策略模式可简化为直接传函数,但需判断是否需要共享状态、类型安全及完整策略结构。本文从机制、场景和边界给出取舍准则。

前端进阶之旅 · 一题精讲更新于 2026.09.05
设计模式#可维护代码
先看核心答案读代码示例
理解线索

策略的本质是注入行为

  1. 一等函数函数可作为值传递,替代单方法对象
  2. 策略类样板定义接口和类,增加层级但稳定命名
  3. 收敛原则策略复杂度低时用函数,高时用类

如果策略只包含一个方法且无其他依赖,直接传函数消除样板;否则保留类以容纳更多契约。

核心回答

先记住这个答案

JavaScript的一等函数让策略模式常可退化为函数参数,省去策略类样板。当策略仅是一个算法且无额外状态时,直接传函数更简洁。但当策略需要维护内部状态、需要命名空间或需与策略模式其他特性(如运行时替换、组合)结合时,定义策略类仍必要。取舍关键看策略的复杂度和变化维度。

  • 单个无状态算法适合直接传函数
  • 策略需要状态或命名空间时用类
  • 判断标准是变化维度和上下文需求

函数参数如何取代策略类

传统策略模式在Java等语言中要求定义策略接口和多个实现类,客户端依赖接口。JavaScript函数是一等公民,一个函数既能携带行为也能被传递,因此一个函数参数就相当于一个隐式策略接口。例如比较器可传函数而非Comparator对象。

但函数作为策略只暴露单一入口,若策略需要多个方法或可配置状态,函数式表达需依赖闭包,而闭包封装的状态与函数一起传递。当策略需要公开展示其属性和方法时,类结构更明确。机制不同导致取舍:函数式更轻,类式更结构化。

折扣计算:何时该用函数还是类

场景是电商价格计算,支持多种折扣策略如无折扣、固定折扣、满减。直接传函数:new PriceCalculator(product => product.price * 0.9) 简洁。但另一种场景是折扣策略需要记录调用次数、有效期等状态,且需要校验配置,此时函数闭包维护的状态不直观。

我采用混合:纯计算规则用箭头函数传递,有状态或需要多方法(如isApplicable和calculate)的折扣实现为策略类实例。这样保持调用处的简洁,同时尊重复杂策略的封装。实践中评估策略是否独立可命名和可能演进。

直接函数与策略类对比JavaScript
// 直接传递函数(无状态)
function applyDiscount(price, discountFn) {
  return discountFn(price);
}
const tenPercent = p => p * 0.9;
console.log(applyDiscount(100, tenPercent)); // 90

// 策略类需要维护状态
class VipStrategy {
  constructor() {
    this.calls = 0;
  }
  apply(price) {
    this.calls++;
    return price * 0.8;
  }
  getCalls() { return this.calls; }
}
const vip = new VipStrategy();
console.log(vip.apply(100), vip.getCalls()); // 80 1
查看输出与解释
90
80 1

演示函数参数适合单次计算,而策略类携带状态。注意VipStrategy实例方法被调用后状态变化。

失效条件:函数式策略的局限

直接传函数失效于策略需要多个协作方法时。例如一个支付策略需要validate、pay和refund,只传一个函数会迫使各逻辑分散。另外,函数没有显式类型,在TypeScript中用函数接口仍可能需要类型断言,而类可提供反射或元数据。

如果策略集可能变化且希望运行时动态增删,类是更好的容器。但过度设计也会出现:为一个简单回调创建类层次。判断准则:策略是否只有一个关注点?是否无需保存状态?若是,函数即可;否则考虑类。当上下文需要统一调用接口而函数签名不统一,可借助适配器函数。

回答前,多想一步

容易答错的地方

认为函数能完全替代策略类
错误。当策略需要多个操作或内部状态时,单个函数无法承担,需用对象。JS中函数本身可用属性,但这会污染函数语义。正确评估:单方法策略可用函数;多方法或状态用类。
盲目为所有策略建立类结构
过度使用类会引入样板和冗长。例如只有一行逻辑的验证器写成类实例导致代码碎片化。应优先考虑函数参数,仅在策略逻辑足够复杂或需要独立测试多个辅助方法时才引入类。
试着用自己的话回答

面试官还会怎么问?

TypeScript中直接传函数与策略类的类型差异?

TS中用函数类型可精确描述,如(price:number)=>number。策略类需定义接口,但能统一接口并支持同名方法。若策略函数参数和返回值不同,需联合类型或泛型。类更易实现函数重载和私有方法。

何时应该从函数升级为策略类?

当函数的闭包状态增多、函数长度超过一个屏幕且需要多阶段处理时。另一个信号:多个函数共享一组私有逻辑,可提取为类的内联私有方法。升级也发生在需要生命周期钩子(如初始化、销毁)时。

策略模式中直接传函数是否影响测试?

不影响,纯函数更易测,用桩断言调用即可。策略类则需实例化并验证状态。但若策略函数引用外部变量,测试需控制环境。函数式策略更简单但难以测试其内部行为,可导出纯函数辅助测试。

从一道题,走向一组知识

把知识连起来

可维护代码

JavaScript 中观察者模式忘记取消订阅会造成什么内存问题,如何诊断?

同属「可维护代码」专题,接着看 观察者模式 内存泄漏 取消订阅 在具体场景中的处理方式。

可维护代码

什么信号表明一段对象创建逻辑应该收敛为工厂而不是散落在各处 new?

同属「可维护代码」专题,接着看 工厂模式 何时使用 对象创建 在具体场景中的处理方式。

参考资料

  • Functions

示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。

本题目录
  1. 先记住这个答案
  2. 函数参数如何取代策略类
  3. 折扣计算:何时该用函数还是类
  4. 失效条件:函数式策略的局限
  5. 容易答错的地方
  6. 面试官还会怎么问
  7. 把知识连起来
读懂,再试着讲出来

先看核心答案,再读代码。最后展开追问,检查自己有没有遗漏边界。

试着回答追问
浏览全部面试题理解原理,也关注真实的使用场景。回到顶部 ↑