0

0

JS如何实现状态模式

畫卷琴夢

畫卷琴夢

发布时间:2025-08-25 13:19:01

|

221人浏览过

|

来源于php中文网

原创

答案:JavaScript中实现状态模式可通过封装不同状态行为于独立对象中,避免冗余条件判断。示例中MediaPlayer作为上下文持有当前状态引用,并将播放、暂停、停止操作委托给具体状态对象处理;每个状态类(如PlayingState、PausedState、StoppedState)实现对应行为并可改变上下文状态,从而实现行为随状态变化而变化,提升代码可维护性与扩展性。

js如何实现状态模式

在JavaScript中实现状态模式,核心在于让一个对象的行为在其内部状态改变时也随之改变,并且这些状态相关的行为被封装在独立的状态对象中。这样能有效避免大量的条件判断语句,让代码更清晰、更易于维护和扩展。

解决方案

实现状态模式,通常会涉及一个“上下文”(Context)对象和多个“具体状态”(Concrete State)对象。上下文对象持有当前状态的引用,并将请求委托给当前状态对象处理。每个具体状态对象则负责实现特定状态下的行为,并可以根据需要改变上下文的状态。

以下是一个简单的JavaScript实现示例,以一个媒体播放器为例,它有“播放中”、“暂停”和“停止”三种状态:

// 抽象状态或状态接口 (在JS中通常通过定义一组共同方法来模拟)
class PlayerState {
    play() { throw new Error("This method must be overridden!"); }
    pause() { throw new Error("This method must be overridden!"); }
    stop() { throw new Error("This method must be overridden!"); }
}

// 具体状态:播放中
class PlayingState extends PlayerState {
    constructor(player) {
        super();
        this.player = player;
    }

    play() {
        console.log("已经在播放了,无需重复操作。");
    }

    pause() {
        console.log("暂停播放。");
        this.player.setState(this.player.pausedState);
    }

    stop() {
        console.log("停止播放。");
        this.player.setState(this.player.stoppedState);
    }
}

// 具体状态:暂停
class PausedState extends PlayerState {
    constructor(player) {
        super();
        this.player = player;
    }

    play() {
        console.log("恢复播放。");
        this.player.setState(this.player.playingState);
    }

    pause() {
        console.log("已经暂停了。");
    }

    stop() {
        console.log("停止播放。");
        this.player.setState(this.player.stoppedState);
    }
}

// 具体状态:停止
class StoppedState extends PlayerState {
    constructor(player) {
        super();
        this.player = player;
    }

    play() {
        console.log("开始播放。");
        this.player.setState(this.player.playingState);
    }

    pause() {
        console.log("当前已停止,无法暂停。");
    }

    stop() {
        console.log("已经停止了。");
    }
}

// 上下文:媒体播放器
class MediaPlayer {
    constructor() {
        this.playingState = new PlayingState(this);
        this.pausedState = new PausedState(this);
        this.stoppedState = new StoppedState(this);

        // 初始状态
        this.currentState = this.stoppedState;
        console.log("播放器初始化,当前状态:停止。");
    }

    setState(state) {
        this.currentState = state;
        // 可以在这里添加一些状态切换的日志或副作用
        console.log(`状态已切换到:${state.constructor.name}`);
    }

    play() {
        this.currentState.play();
    }

    pause() {
        this.currentState.pause();
    }

    stop() {
        this.currentState.stop();
    }
}

// 使用示例
const player = new MediaPlayer();
player.play();   // 开始播放
player.pause();  // 暂停
player.play();   // 恢复播放
player.stop();   // 停止
player.pause();  // 无法暂停(已停止)
player.stop();   // 已经停止

在这个例子里,

MediaPlayer
是上下文,它不直接处理播放、暂停、停止的逻辑,而是将这些行为委托给
currentState
。每个状态类(
PlayingState
,
PausedState
,
StoppedState
)封装了在该状态下,这些操作的具体行为,并且能够决定在特定操作后,播放器应该切换到哪个新状态。

为什么要在JavaScript中使用状态模式?

我个人觉得,最头疼的就是那些随着对象状态变化而变得臃肿不堪的条件判断。想象一下,一个复杂的订单系统,有“待支付”、“已支付”、“已发货”、“已取消”等状态,每个状态下,订单可以执行的操作(如修改地址、退款、确认收货)都可能不同。如果用大量的

if/else if
switch
语句来判断当前状态并执行对应逻辑,那代码很快就会变成一团乱麻,维护起来简直是噩梦。

状态模式的魅力就在于它能把这些散落在各处的、依赖于状态的逻辑,清晰地封装到各自独立的状态类中。这不仅让代码结构变得异常整洁,每个状态类只关心自己的行为和可能的转换,大大提升了可读性和可维护性。想新增一个状态?只需要添加一个新的状态类,并调整相关状态的转换逻辑,而无需修改大量现有代码。这简直是软件“开闭原则”的典范应用,对大型、复杂且状态变化频繁的系统来说,它能显著降低维护成本和引入新功能的风险。

状态模式与策略模式有何不同?

我常常会把这俩搞混,后来发现,关键在于那个“谁在变”。状态模式和策略模式确实有很多相似之处:它们都使用了组合(Composition)而非继承,都通过委托(Delegation)来改变对象的行为,并且都将算法或行为封装在独立的类中。但它们的核心意图和行为改变的驱动力是不同的。

状态模式中,是“上下文对象”的内部状态在改变,而上下文对象的行为也随之改变。你可以把状态对象看作是上下文对象在特定时刻的“人格”或“模式”。例如,我的播放器在“播放中”和“暂停”时,点击“播放”按钮的行为是完全不同的。这种行为的切换是内部状态驱动的,上下文对象会主动改变它当前持有的状态对象。

奥硕企业网站管理系统3.0.2
奥硕企业网站管理系统3.0.2

临沂奥硕软件有限公司拥有国内一流的企业网站管理系统,奥硕企业网站管理系统真正会打字就会建站的管理系统,其强大的扩展性可以满足企业网站实现各种功能(唯一集成3O多套模版的企业建站系统)奥硕企业网站管理系统具有一下特色功能1、双语双模(中英文采用单独模板设计,可制作中英文不同样式的网站)2、在线编辑JS动态菜单支持下拉效果,同时生成中文,英文,静态3个JS菜单3、在线制作并调用FLASH展示动画4、自

下载

策略模式则不同,它关注的是“算法族”的封装。上下文对象(或客户端)会选择一个具体的策略来执行某个任务,但上下文对象本身的“状态”并没有改变。比如,一个排序器可以选择“冒泡排序”策略或“快速排序”策略来对数据进行排序。排序器本身还是排序器,只是它执行排序的方式变了,这种改变通常是由外部(客户端)来选择或配置的。

简而言之,状态模式是“我在什么状态下,就做什么事”,状态是内在的、动态变化的;策略模式是“我选择哪种方式来做这件事”,策略是可替换的、通常由外部选择的。

实现状态模式时常见的陷阱与考量?

说实话,刚开始用状态模式的时候,我确实踩过不少坑。最常见的就是,明明一个

if
就能搞定的事,非要硬套模式,结果代码反而更复杂了。不是所有状态相关的逻辑都非得用状态模式,对于只有两三个状态,且状态转换逻辑非常简单的场景,过度设计反而会增加不必要的复杂性和样板代码。这就像你为了钉个小图钉,非得搬出一套重型电钻一样,完全没必要。

另一个让我纠结的问题是:状态转换的逻辑,到底是应该放在上下文对象里,还是放在各个具体的状态对象里?在上面的播放器例子中,我把状态转换逻辑(

this.player.setState(...)
)放在了具体的状态对象内部。这样做的好处是,每个状态对象完全掌控了自己在接收到某个操作后,应该如何响应以及如何转换到下一个状态,这让状态的封装性变得非常好。但缺点是,状态对象需要持有上下文对象的引用,这可能会引入循环依赖,并且如果状态转换逻辑非常复杂,状态对象本身也会变得比较臃肿。

反之,如果把所有状态转换逻辑都集中在上下文对象中,上下文对象会变得相对复杂,因为它需要知道所有状态及其转换规则。这又回到了我们最初想避免的“大

switch
语句”问题。

我个人的经验是,对于大多数情况,让状态对象自己负责状态转换是更优雅的选择,它符合“单一职责原则”:每个状态对象只关心自己在当前状态下的行为以及如何根据操作进入下一个状态。但如果状态转换逻辑异常复杂,或者有大量的状态间共享的转换规则,那么在上下文对象中集中管理一部分转换逻辑也未尝不可,关键在于权衡和取舍,找到最适合当前场景的平衡点。

最后,调试也可能变得稍微复杂一些。因为行为被分散到多个状态对象中,当出现问题时,你可能需要追踪当前上下文处于哪个状态,以及这个状态是如何被改变的。因此,在实现时,适当地添加日志(比如示例中的

console.log
)来追踪状态变化,会非常有帮助。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
if什么意思
if什么意思

if的意思是“如果”的条件。它是一个用于引导条件语句的关键词,用于根据特定条件的真假情况来执行不同的代码块。本专题提供if什么意思的相关文章,供大家免费阅读。

783

2023.08.22

switch语句用法
switch语句用法

switch语句用法:1、Switch语句只能用于整数类型,枚举类型和String类型,不能用于浮点数类型和布尔类型;2、每个case语句后面必须跟着一个break语句,以防止执行其他case的代码块,没有break语句,将会继续执行下一个case的代码块;3、可以在一个case语句中匹配多个值,使用逗号分隔;4、Switch语句中的default代码块是可选的等等。

543

2023.09.21

Java switch的用法
Java switch的用法

Java中的switch语句用于根据不同的条件执行不同的代码块。想了解更多switch的相关内容,可以阅读本专题下面的文章。

424

2024.03.13

js正则表达式
js正则表达式

php中文网为大家提供各种js正则表达式语法大全以及各种js正则表达式使用的方法,还有更多js正则表达式的相关文章、相关下载、相关课程,供大家免费下载体验。

515

2023.06.20

js获取当前时间
js获取当前时间

JS全称JavaScript,是一种具有函数优先的轻量级,解释型或即时编译型的编程语言;它是一种属于网络的高级脚本语言,主要用于Web,常用来为网页添加各式各样的动态功能。js怎么获取当前时间呢?php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

245

2023.07.28

js 字符串转数组
js 字符串转数组

js字符串转数组的方法:1、使用“split()”方法;2、使用“Array.from()”方法;3、使用for循环遍历;4、使用“Array.split()”方法。本专题为大家提供js字符串转数组的相关的文章、下载、课程内容,供大家免费下载体验。

340

2023.08.03

js是什么意思
js是什么意思

JS是JavaScript的缩写,它是一种广泛应用于网页开发的脚本语言。JavaScript是一种解释性的、基于对象和事件驱动的编程语言,通常用于为网页增加交互性和动态性。它可以在网页上实现复杂的功能和效果,如表单验证、页面元素操作、动画效果、数据交互等。

5334

2023.08.17

js删除节点的方法
js删除节点的方法

js删除节点的方法有:1、removeChild()方法,用于从父节点中移除指定的子节点,它需要两个参数,第一个参数是要删除的子节点,第二个参数是父节点;2、parentNode.removeChild()方法,可以直接通过父节点调用来删除子节点;3、remove()方法,可以直接删除节点,而无需指定父节点;4、innerHTML属性,用于删除节点的内容。

483

2023.09.01

2026赚钱平台入口大全
2026赚钱平台入口大全

2026年最新赚钱平台入口汇总,涵盖任务众包、内容创作、电商运营、技能变现等多类正规渠道,助你轻松开启副业增收之路。阅读专题下面的文章了解更多详细内容。

54

2026.01.31

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
PHP基础入门课程
PHP基础入门课程

共33课时 | 2万人学习

前端系列快速入门课程
前端系列快速入门课程

共4课时 | 0.4万人学习

Git 教程
Git 教程

共21课时 | 3.2万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号