js设计模式-状态模式

一. 概念

状态模式是一种行为型设计模式,通过将对象在不同状态下的行为抽象为独立的对象,形成状态-行为映射。和策略模式有点像。

关键区别点如下:

  1. 状态对象可以自主控制状态转换,而策略对象不会自己更换策略
  2. 状态模式关注对象在不同状态下的行为差异,策略模式注重算法的封装与替换
  3. 状态模式的状态类通常需要引用当前对象(this.light),而策略类一般不需要
  4. 状态模式更强调生命周期内的状态变化,策略模式更强调运行时的算法选择

二. 应用场景举例

1. 举一个打开/关闭开关的案例

1. 常规方案

javascript 复制代码
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <button>按下开关</button>
  <script>
    class Light {
      constructor() {
        this.state = 'off'; // 给电灯设置初始状态 off 
        this.button = null; // 电灯开关按钮
      }
      init () {
        this.button = document.querySelector('button'),
          self = this;
        this.button.onclick = function () {
          self.buttonWasPressed();
        }
      }
      buttonWasPressed () {
        if (this.state === 'off') {
          console.log('开灯');
          this.state = 'on';
        } else if (this.state === 'on') {
          console.log('关灯');
          this.state = 'off';
        }
      }
    }
    const light = new Light();
    light.init();
  </script>
</body>

</html>

2. 利用状态模式

javascript 复制代码
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <script>
    const FSM = {
      off: {
        buttonWasPressed: function () {
          console.log('关灯');
          this.button.innerHTML = '下一次按我是开灯';
          this.currState = FSM.on;
        }
      },
      on: {
        buttonWasPressed: function () {
          console.log('开灯');
          this.button.innerHTML = '下一次按我是关灯';
          this.currState = FSM.off;
        }
      }
    };
    class Light {
      constructor() {
        this.currState = FSM.off; // 设置当前状态
        this.button = null;
      }
      init () {
        var button = document.createElement('button'),
          self = this;
        button.innerHTML = '已关灯';
        this.button = document.body.appendChild(button);
        this.button.onclick = function () {
           self.currState.buttonWasPressed.call(self); // 把请求委托给 FSM 状态机
        }
      }
    }
    const light = new Light();
    light.init(); 
  </script>
</body>

</html>
     

两种方式对比:
方案1:

适合场景: 状态简单、需求固定,且无扩展需求的系统。

优点: 代码简洁,易于快速实现。

缺点: 状态或行为增加时代码膨胀(如需添加 dim 状态需添加 else if)。
方案2:

适合场景: 需要频繁修改或扩展的状态系统(如游戏、设备控制等)。

优点: 扩展性强,维护成本低,逻辑清晰。

缺点: 初期代码量略多,需理解状态机设计模式。

总结:

状态机(方案二) 是模式化、解耦的优雅实现,适合复杂场景。

条件判断(方案一) 是快速实现的简易方案,适合简单需求。

2. 当开关的状态有多种时举例

javascript 复制代码
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <button>按下开关</button>
  <script>
    class OffLightState {
      constructor(light) {
        this.light = light;
      }
      buttonWasPressed () {
        alert('弱光');
        this.light.setState(this.light.weakLightState);
      }
    }

    class WeakLightState {
       constructor(light) {
        this.light = light;
      }
      buttonWasPressed () {
        alert('强光');
        this.light.setState(this.light.strongLightState);
      }
    }

    class StrongLightState {
      constructor(light) {
        this.light = light;
      }
      buttonWasPressed () {
        alert('关灯');
        this.light.setState(this.light.offLightState);
      }
    }

    class Light {
      constructor() {
        this.offLightState = new OffLightState(this);
        this.weakLightState = new WeakLightState(this);
        this.strongLightState = new StrongLightState(this);
        this.currState = this.offLightState;
        this.button = null;
      }

      init () {
        this.button = document.querySelector('button');
        this.button.onclick = () => this.currState.buttonWasPressed();
      }

      setState (newState) {
        this.currState = newState;
      }
    }

    const light = new Light();
    light.init();
  </script>
</body>

</html>

构造Light实例时

javascript 复制代码
Light()
   → 初始化3个状态实例并传入自身(this)
   → 设置初始状态为关灯状态
   → 预留按钮变量

设计优势

  1. 可扩展性:增加新状态仅需添加新状态类,不修改已有代码
  2. 维护简单:状态逻辑与主类解耦,修改某个状态不会影响其他部分
  3. 清晰的状态转移:每个状态明确定义迁移目标,避免条件嵌套
相关推荐
jktantan4 小时前
Vue keep-alive 切标签后滚动位置回顶?因为 onDeactivated 里读到的 scrollTop 是 0
前端·javascript·vue.js
总要学点什么4 小时前
第89篇:三维特效-螺旋圆效果——让螺旋臂卷起一圈圈外扩的涟漪
开发语言·javascript·ecmascript·webgl·cesium·空间分析·1024程序员节
前端精髓5 小时前
调用openrouter的免费API模型
java·前端·javascript
螺丝钉tina11 小时前
[Playwright] [02]常用命令与调试:Debug UI、Mode和 Trace Viewer
开发语言·javascript·测试工具·ui·自动化
开开心心就好12 小时前
竖式除法出题工具,整除余数混合模式
运维·服务器·前端·javascript·ocr·perl·1024程序员节
大江东去浪淘尽千古风流人物12 小时前
【HexVIO】DSP卸载双目惯性VIO:Hexagon向量化前端与单帧融合调度深度解析
前端·状态模式·slam·dsp·vio·hexagon·移动端加速
এ慕ོ冬℘゜12 小时前
JS 事件循环、宏任务微任务:单线程底层原理 + 执行顺序
开发语言·javascript·ecmascript
web打印社区12 小时前
Lodop排障总纲-常见报错一张表
开发语言·前端·javascript·chrome·websocket
程序员大阳13 小时前
副队长JavaScript教程(56)-DOM之表单操作
javascript·dom·表单操作
Dxy123931021613 小时前
JS AES加密详解
开发语言·javascript·ecmascript