为了账号安全,请及时绑定邮箱和手机立即绑定

`Object.freeze()` 让对象冻到骨子里啦!🥶❄️

标题已经说明了一切。让我们来谈谈 JavaScript 中最被低估的特性之一:Object.freeze()。这个强大的方法不仅仅是一个工具,它是你的编写更安全且更可预测代码的秘密武器 ✨。

说实话,当我第一次见到 Object.freeze() 这个方法时,我基本上忽略了它。我只是觉得别去修改那些对象就行了。但随着我的项目变得越来越复杂,我开始认识到它的真正价值。现在,它现在已经是我工具箱里必不可少的工具了。

7 个改变游戏规则的对象冻结用例 🧊

让我展示给你看为什么 Object.freeze() 绝对是必不可少的,以及它如何让你的 JavaScript 技巧更上一层楼。让我们来看看一些实际的例子吧!🥶

1: 实际上这些常量是固定的

我们都经历过这种情况——我们创建了“常量”对象,却发现这些对象在代码里被改了。

    interface Config {
      api: string;
      timeout: number;
      retries: number;
    }

    // 未使用冻结 - 虽然声称是'常量',但可以被修改
    const CONFIG: Config = {
      api: "https://api.example.com",
      timeout: 5000,
      retries: 3
    };

    // 哦豁!即使它是'常量',这个修改也可以生效
    CONFIG.timeout = 1000;

    // 使用冻结 - 真正不可改变
    const FROZEN_CONFIG: Config = Object.freeze({
      api: "https://api.example.com",
      timeout: 5000,
      retries: 3
    });

    // 在严格模式下这会导致错误!🎉
    FROZEN_CONFIG.timeout = 1000;

切换到全屏模式,退出全屏

现在你的配置实际上是不能更改的,再也不用担心那些神秘的配置变化了。这样就不会再出现调试的问题了。

保护默认设置

这对状态管理是个颠覆,尤其是在像Redux这样的解决方案中。

    interface AppState {
      user: {
        name: string;
        preferences: {
          theme: 'light' | 'dark';
          notifications: boolean;
        };
      };
      settings: {
        language: string;
      };
    }

    const initialState: AppState = Object.freeze({
      user: {
        name: '',
        preferences: {
          theme: 'light',
          notifications: true
        }
      },
      settings: {
        language: 'en'
      }
    });

    // 现在你再也不能意外地修改初始状态了!
    // 在严格模式下,这会报错
    initialState.user.preferences.theme = 'dark';

全屏,退出全屏

3: 类似枚举的不可修改的对象

JavaScript 并没有真正的枚举类型,但是我们可以利用 Object.freeze() 达到类似的效果:

    const HttpStatus = Object.freeze({
      OK: 200,
      CREATED: 201,
      BAD_REQUEST: 400,
      UNAUTHORIZED: 401,
      NOT_FOUND: 404,
      INTERNAL_SERVER_ERROR: 500,
      // TypeScript 小技巧:使用 `as const` 为字面量类型
    } as const);

    // 这里会产生错误!你的状态码是安全的 🔒
    HttpStatus.OK = 999;

    // TypeScript 知道确切的类型
    type StatusCode = typeof HttpStatus[keyof typeof HttpStatus];
    // 类型是明确的:200 | 201 | 400 | 401 | 404 | 500

全屏模式(点击进入,退出全屏)

4: 深度冷冻物体

Object.freeze() 默认只会进行浅层冻结,但我们可以创建一个深层冻结函数来实现深层冻结。

function deepFreeze<T>(obj: T): Readonly<T> {
  // 获取所有属性,包括非可枚举属性
  const propNames = Object.getOwnPropertyNames(obj);

  // 冻结属性后冻结父对象
  propNames.forEach(name => {
    const value = (obj as any)[name];

    if (value && typeof value === 'object') {
      deepFreeze(value);
    }
  });

  return Object.freeze(obj);
}

const complexObject = deepFreeze({
  level1: {
    level2: {
      level3: {
        value: "can't touch this"
      }
    }
  }
});

// 这将会抛出异常!深度冻结,就是这样 🏆
complexObject.level1.level2.level3.value = "试着去触碰这个";

全屏模式 退出全屏

保护事件处理

遇到过这种情况吗?由于事件对象在处理中被修改导致的 bug?不会再有了!

    interface 自定义事件接口 {
      类型: string;
      时间戳: number;
      数据: any;
    }

    class 事件源 {
      private 处理器: { [key: string]: Function[] } = {};

      触发(事件: 自定义事件接口) {
        // 冻结事件对象以防止修改
        const 冻结事件对象 = Object.freeze({ ...事件 });

        const 处理器 = this.处理器[事件.类型] || [];
        处理器.forEach(处理器函数 => 处理器函数(冻结事件对象));
      }

      监听(类型: string, 处理器函数: Function) {
        if (!this.处理器[类型]) {
          this.处理器[类型] = [];
        }
        this.处理器[类型].push(处理器函数);
      }
    }

点击全屏 点击退出全屏

6: 不可变的 API 响应:

保持你的 API 响应不变,以防止意外更改:

    interface ApiResponse<T> {
      data: T;
      metadata: {
        timestamp: number;
        requestId: string;
      };
    }

    async function fetchData<T>(url: string): Promise<ApiResponse<T>> {
      const response = await fetch(url);
      const json = await response.json();

      // 立即冻结响应
      return Object.freeze({
        data: json,
        metadata: {
          timestamp: Date.now(),
          requestId: crypto.randomUUID()
        }
      });
    }

    // 法
    const response = await fetchData('https://api.example.com/data');
    // 这会引发错误!您的 API 响应是安全的,不用担心盾牌符号 🛡️
    response.data = null;

进入全屏,退出全屏

7: 创建真正的私有财产

尽管我们现在有了用 # 表示的私有字段,Object.freeze() 可以帮助在工厂函数中创建真正的私有属性。

    interface User {
      readonly id: string;
      readonly username: string;
      获取信息(): string;
    }

    function createUser(username: string): User {
      const 私有状态 = {
        loginAttempts: 0,
        lastLogin: new Date()
      };

      // 公共接口被冻结
      return Object.freeze({
        id: crypto.randomUUID(),
        username,
        获取信息() {
          return `${username} (最后登录: ${私有状态.lastLogin})`;
        }
      });
    }

    const 用户 = createUser('alice');
    // 这会抛出异常!不能添加属性
    用户.管理员权限 = true;
    // 这会抛出异常!不能修改现有属性
    用户.username = 'bob';

点击全屏 点击退出全屏

性能考虑 🚀

虽然 Object.freeze() 非常强大,但理解其性能考量也很重要。

  1. 冻结过程本身是有成本的,尤其是使用 deepFreeze 时。
  2. 被冻结的对象读取可能会稍微慢一点(但这种差异通常可以忽略不计)。
  3. TypeScript 的 readonly 仅在编译时有效,并且在运行时没有成本。

这里有一个非常注重性能的方法:

    // 开发环境:使用 Object.freeze() 以便更好地捕获错误
    const isDev = process.env.NODE_ENV === 'development';

    function safeFreeze<T>(obj: T): Readonly<T> {
      return isDev ? Object.freeze(obj) : obj;
    }

    // 现在可以到处使用 safeFreeze
    const config = safeFreeze({
      // 你的配置如下
    });

切换到全屏模式 或 结束全屏模式

TypeScript 的魔力 ✨

使用 Object.freeze() 方法与 TypeScript 的类型系统配合得非常好:

    // Object.freeze() 自动使属性变为只读
    const frozen = Object.freeze({ x: 1, y: 2 });
    // TypeScript 错误:不能将 'x' 赋值为只读属性,因为它是只读的
    frozen.x = 2;

    // 使用 'as const' 对字面量类型有效
    const DIRECTIONS = Object.freeze({
      UP: 'UP',
      DOWN: 'DOWN',
      LEFT: 'LEFT',
      RIGHT: 'RIGHT'
    } as const);

    // 类型正是:'UP'、'DOWN'、'LEFT'、'RIGHT'
    type Direction = typeof DIRECTIONS[keyof typeof DIRECTIONS];

全屏 退出全屏

结束思考

Object.freeze()乍一看可能很简单,但其实它是一个非常强大的工具,可以帮助你编写更安全、更易于维护的JavaScript代码。从保护配置对象到确保状态的不可变性,它是现代JavaScript开发中不可或缺的工具。

下次当你想要使用第三方不可变库时,记得 Object.freeze() 可能就足够满足你的需求了! 🥶✨

哦,最后推荐一个东西 😁
如果你想要进行回顾会议或计划扑克游戏,可以试试Kollabe。它是一个功能强大、经过实战验证的平台,不断进步以提供最佳的实时合作体验。

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消