JavaScript内核与高级编程之：`JavaScript`的`State Machine`：其在状态管理中的应用。

admin
Mon, 25 Aug 2025 08:57:10 +0000
Javascript, javascript, machine, state

嘿，各位靓仔靓女，晚上好啊！今天咱来聊点儿刺激的，啊不，是烧脑的——JavaScript 的状态机！别怕，听起来高大上，其实就是个纸老虎。保证各位听完，腰不酸了，腿不疼了，一口气能写十个 Bug…呃，是十个状态机！ 状态机是啥玩意儿？ 想象一下，你正在玩一个超级玛丽的游戏。玛丽有几个状态：站立、跳跃、行走、死亡。当按下“跳跃”键，玛丽就从“站立”状态切换到“跳跃”状态。这就是一个简单的状态机。 简单来说，状态机就是描述一个事物在不同条件下，如何从一种状态转换到另一种状态的模型。 它包含以下几个核心要素： 状态 (State): 事物可能处于的不同情况。比如上面例子中的“站立”、“跳跃”。 事件 (Event): 触发状态转换的动作。比如“按下跳跃键”。 转换 (Transition): 从一个状态到另一个状态的改变。 动作 (Action): 状态转换时执行的副作用，比如播放跳跃动画。 JavaScript 中的状态机：为什么要用它？ 你可能会说，用 if-else 或者 switch 也能实现状态切换啊，干嘛要用状态机这么麻烦的东西？ 问得好！ 代码可读性更高： 状态机将状态逻辑和转 …

继续阅读“JavaScript内核与高级编程之：`JavaScript`的`State Machine`：其在状态管理中的应用。”

The post JavaScript内核与高级编程之：`JavaScript`的`State Machine`：其在状态管理中的应用。 first appeared on 智猿学院-前后端，数据库，人工智能，云计算等领域前沿技术讲座.

嘿，各位靓仔靓女，晚上好啊！今天咱来聊点儿刺激的，啊不，是烧脑的——JavaScript 的状态机！别怕，听起来高大上，其实就是个纸老虎。保证各位听完，腰不酸了，腿不疼了，一口气能写十个 Bug…呃，是十个状态机！

状态机是啥玩意儿？

想象一下，你正在玩一个超级玛丽的游戏。玛丽有几个状态：站立、跳跃、行走、死亡。当按下“跳跃”键，玛丽就从“站立”状态切换到“跳跃”状态。这就是一个简单的状态机。

简单来说，状态机就是描述一个事物在不同条件下，如何从一种状态转换到另一种状态的模型。 它包含以下几个核心要素：

状态 (State): 事物可能处于的不同情况。比如上面例子中的“站立”、“跳跃”。

事件 (Event): 触发状态转换的动作。比如“按下跳跃键”。

转换 (Transition): 从一个状态到另一个状态的改变。

动作 (Action): 状态转换时执行的副作用，比如播放跳跃动画。

JavaScript 中的状态机：为什么要用它？

你可能会说，用 if-else 或者 switch 也能实现状态切换啊，干嘛要用状态机这么麻烦的东西？ 问得好！

代码可读性更高： 状态机将状态逻辑和转换规则集中管理，让代码结构更清晰，易于理解和维护。想想看，几十个 if-else 嵌套在一起，谁看了不头大？

减少 Bug： 状态机可以帮助你明确定义所有可能的状态和转换，减少遗漏或错误的判断，从而降低 Bug 产生的概率。

更容易扩展： 当需要添加新的状态或转换时，状态机可以让你更容易地进行扩展，而不需要修改大量的代码。

更容易测试： 状态机可以让你更容易地编写单元测试，验证状态转换的正确性。

状态机在状态管理中的应用

现在，让我们看看状态机在前端状态管理中的一些实际应用。

1. UI 组件的状态管理： 比如一个按钮，它可以有 disabled、enabled、loading 等状态。 使用状态机，可以清晰地定义这些状态之间的转换规则。

2. 表单验证： 表单的不同字段可能处于 validating、valid、invalid 等状态。 状态机可以帮助你管理这些状态，并根据用户的输入触发相应的验证逻辑。

3. 异步请求： 一个异步请求可能经历 idle、pending、success、failure 等状态。 状态机可以帮助你管理这些状态，并在不同的状态下显示不同的 UI。

手撸一个简单的状态机

理论讲了一堆，不如直接上手撸一个。 我们先来实现一个简单的状态机，用于管理一个灯泡的状态：on 和 off。

class StateMachine {
  constructor(initialState, transitions) {
    this.state = initialState;
    this.transitions = transitions;
  }

  transition(event) {
    if (this.transitions[this.state] && this.transitions[this.state][event]) {
      const nextState = this.transitions[this.state][event];
      this.state = nextState;
      console.log(`状态转换：${event} -> ${this.state}`); // 打印状态转换信息
      return this.state;
    } else {
      console.warn(`无效的转换：${this.state} -> ${event}`);
      return null;
    }
  }

  getState() {
    return this.state;
  }
}

// 定义状态和转换规则
const lightTransitions = {
  off: {
    turnOn: 'on',
  },
  on: {
    turnOff: 'off',
  },
};

// 创建状态机实例
const lightMachine = new StateMachine('off', lightTransitions);

// 测试状态转换
console.log(`当前状态：${lightMachine.getState()}`); // 输出：当前状态：off
lightMachine.transition('turnOn'); // 输出：状态转换：turnOn -> on
console.log(`当前状态：${lightMachine.getState()}`); // 输出：当前状态：on
lightMachine.transition('turnOff'); // 输出：状态转换：turnOff -> off
console.log(`当前状态：${lightMachine.getState()}`); // 输出：当前状态：off
lightMachine.transition('break'); // 输出：无效的转换：off -> break

这段代码定义了一个 StateMachine 类，它接受一个初始状态和一个转换规则对象作为参数。transition 方法用于触发状态转换，getState 方法用于获取当前状态。

lightTransitions 对象定义了灯泡的状态转换规则。例如，当灯泡处于 off 状态时，如果触发 turnOn 事件，则状态转换为 on。

使用 xstate 库

虽然我们可以手撸状态机，但是对于复杂的应用，使用现成的状态机库会更加方便。 xstate 是一个非常流行的 JavaScript 状态机库，它提供了更强大的功能和更灵活的配置。

首先，安装 xstate：

npm install xstate

然后，我们可以使用 xstate 来实现灯泡状态机：

import { createMachine, assign } from 'xstate';

// 定义状态机
const lightMachine = createMachine({
  id: 'light',
  initial: 'off',
  states: {
    off: {
      on: {
        TURN_ON: 'on',
      },
    },
    on: {
      on: {
        TURN_OFF: 'off',
      },
      entry: () => console.log('灯亮了！'), // 进入 on 状态时执行的动作
      exit: () => console.log('灯灭了！'), // 离开 on 状态时执行的动作
    },
  },
});

// 导入服务
import { interpret } from 'xstate';

// 创建服务
const lightService = interpret(lightMachine)
  .onTransition((state) => console.log(state.value))
  .start();

// 发送事件
lightService.send('TURN_ON'); // 输出：on
lightService.send('TURN_OFF'); // 输出：off

createMachine 函数用于创建状态机。 id 属性用于指定状态机的 ID，initial 属性用于指定初始状态，states 属性用于定义状态和转换规则。

on 属性用于定义事件和状态的转换关系。例如，当灯泡处于 off 状态时，如果接收到 TURN_ON 事件，则状态转换为 on。

entry 和 exit 属性用于定义进入和离开状态时执行的动作。

interpret 函数用于创建一个状态机服务，可以驱动状态转换。 send 方法用于发送事件，触发状态转换。

更高级的用法

xstate 还提供了许多高级功能，例如：

Context (上下文): 用于存储状态机的数据。

Guards (守卫): 用于根据上下文的值来决定是否进行状态转换。

Actions (动作): 用于在状态转换时执行副作用，例如更新上下文、发送网络请求等。

Activities (活动): 用于在状态处于某个状态时持续执行的任务，例如轮询服务器、播放动画等。

Parallel States (并行状态): 允许状态机同时处于多个状态。

Hierarchical States (分层状态): 允许状态机拥有嵌套的状态。

下面是一个使用 context 和 actions 的例子，用于管理一个计数器的状态：

import { createMachine, assign } from 'xstate';

const counterMachine = createMachine({
  id: 'counter',
  initial: 'idle',
  context: {
    count: 0,
  },
  states: {
    idle: {
      on: {
        INCREMENT: {
          target: 'idle',
          actions: assign({
            count: (context) => context.count + 1,
          }),
        },
        DECREMENT: {
          target: 'idle',
          actions: assign({
            count: (context) => context.count - 1,
          }),
        },
      },
    },
  },
});

const counterService = interpret(counterMachine)
  .onTransition((state) => console.log(`Count: ${state.context.count}`))
  .start();

counterService.send('INCREMENT'); // 输出：Count: 1
counterService.send('INCREMENT'); // 输出：Count: 2
counterService.send('DECREMENT'); // 输出：Count: 1

在这个例子中，context 用于存储计数器的值。 INCREMENT 和 DECREMENT 事件分别用于增加和减少计数器的值。 assign 函数用于更新 context 的值。

状态机的可视化

xstate 还可以与可视化工具集成，例如 xstate-viz，它可以让你以图形化的方式查看状态机的状态和转换规则。 这对于理解和调试复杂的状态机非常有帮助。

状态机 VS 其他状态管理方案

你可能会想，React 还有 Redux、Vue 还有 Vuex，这些状态管理方案也很流行，状态机和它们有什么区别？

特性 | 状态机 | Redux/Vuex
核心概念 | 状态、事件、转换 | 单一状态树、Action、Reducer
状态转换方式 | 基于事件触发的显式状态转换 | 通过 Action 触发 Reducer 更新状态树
适用场景 | 复杂的状态逻辑和状态转换场景 | 全局状态管理，组件间数据共享
优点 | 清晰的状态定义，易于理解和维护，减少 Bug | 集中管理状态，方便调试，适合大型应用
缺点 | 学习曲线较陡峭，代码量可能稍多 | 需要编写大量的 Action 和 Reducer，容易冗余

简单来说，Redux/Vuex 更适合管理全局状态，而状态机更适合管理单个组件或模块的复杂状态逻辑。 它们可以结合使用，例如使用 Redux/Vuex 管理全局状态，使用状态机管理组件的内部状态。

总结

状态机是一种强大的工具，可以帮助你更好地管理状态和状态转换。 虽然学习曲线可能稍陡峭，但是一旦掌握了它，你就会发现它能大大提高你的代码质量和开发效率。

记住，状态机不是银弹，不要滥用它。 只有在需要管理复杂的状态逻辑时，才应该考虑使用状态机。

好了，今天的讲座就到这里。 希望各位能够从中学到一些东西，并在实际项目中尝试使用状态机。 祝大家早日成为状态机大师！ 拜拜！