Object.freeze() 变得困难🥶❄️

2025-06-07

Object.freeze() 变得困难🥶❄️

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

说实话,当我第一次发现 Object.freeze() 时,我几乎忽略了它。我当时想:“别改变你的对象就行了。” 但随着我的应用程序变得越来越复杂,我开始意识到它的真正价值。现在,它已经成为我工具包中不可或缺的一部分。

Object.freeze() 的 7 个颠覆性用例

让我来告诉你为什么 Object.freeze() 至关重要,以及它如何提升你的 JavaScript 水平。让我们深入研究一些实际的例子!🥶

1:实际恒定的常数

我们都经历过这种情况——创建“常量”对象,但这些对象最终会在代码库的某个地方发生变异。

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

// Without freeze - supposedly "constant" but can be modified
const CONFIG: Config = {
  api: "https://api.example.com",
  timeout: 5000,
  retries: 3
};

// Oops! This works even though it's "constant"
CONFIG.timeout = 1000;

// With freeze - truly immutable
const FROZEN_CONFIG: Config = Object.freeze({
  api: "https://api.example.com",
  timeout: 5000,
  retries: 3
});

// This throws an error in strict mode! 🎉
FROZEN_CONFIG.timeout = 1000;
Enter fullscreen mode Exit fullscreen mode

现在你的配置实际上是不可变的。无需再调试那些神秘的配置变更。

2:保护默认状态

这对于状态管理来说是一个改变,特别是在 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'
  }
});

// Now you can't accidentally modify your initial state!
// This will throw in strict mode
initialState.user.preferences.theme = 'dark';
Enter fullscreen mode Exit fullscreen mode

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 bonus: as const for literal types
} as const);

// This will throw! Your status codes are safe 🔒
HttpStatus.OK = 999;

// TypeScript knows the exact types
type StatusCode = typeof HttpStatus[keyof typeof HttpStatus];
// Type is exactly: 200 | 201 | 400 | 401 | 404 | 500
Enter fullscreen mode Exit fullscreen mode

4:深度冷冻物体

Object.freeze() 默认是浅冻结,但我们可以创建一个深度冻结实用程序:

function deepFreeze<T>(obj: T): Readonly<T> {
  // Get all properties, including non-enumerable ones
  const propNames = Object.getOwnPropertyNames(obj);

  // Freeze properties before freezing parent
  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"
      }
    }
  }
});

// This will throw! Deep freezing for the win 🏆
complexObject.level1.level2.level3.value = "try to touch this";
Enter fullscreen mode Exit fullscreen mode

5:保护事件处理程序

您是否曾经遇到过由于事件对象在事件发生后被修改而导致的 bug?再也不会了!

interface CustomEvent {
  type: string;
  timestamp: number;
  data: any;
}

class EventEmitter {
  private handlers: { [key: string]: Function[] } = {};

  emit(event: CustomEvent) {
    // Freeze the event object to prevent modifications
    const frozenEvent = Object.freeze({ ...event });

    const handlers = this.handlers[event.type] || [];
    handlers.forEach(handler => handler(frozenEvent));
  }

  on(type: string, handler: Function) {
    if (!this.handlers[type]) {
      this.handlers[type] = [];
    }
    this.handlers[type].push(handler);
  }
}
Enter fullscreen mode Exit fullscreen mode

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();

  // Freeze the response immediately
  return Object.freeze({
    data: json,
    metadata: {
      timestamp: Date.now(),
      requestId: crypto.randomUUID()
    }
  });
}

// Usage
const response = await fetchData('https://api.example.com/data');
// This will throw! Your API response is safe 🛡️
response.data = null;
Enter fullscreen mode Exit fullscreen mode

7:创建真正的私有属性

虽然我们现在有了带有#的私有字段,但是 Object.freeze() 可以帮助在工厂函数中创建真正的私有属性:

interface User {
  readonly id: string;
  readonly username: string;
  getInfo(): string;
}

function createUser(username: string): User {
  const privateState = {
    loginAttempts: 0,
    lastLogin: new Date()
  };

  // Public interface is frozen
  return Object.freeze({
    id: crypto.randomUUID(),
    username,
    getInfo() {
      return `${username} (Last login: ${privateState.lastLogin})`;
    }
  });
}

const user = createUser('alice');
// This throws! Can't add properties
user.admin = true;
// This throws! Can't modify existing ones
user.username = 'bob';
Enter fullscreen mode Exit fullscreen mode

性能考虑

虽然 Object.freeze() 功能强大,但了解其性能影响也很重要:

  1. 冷冻操作本身是有成本的,尤其是使用 deepFreeze
  2. 读取冻结对象的速度可能会稍微慢一些(但差异通常可以忽略不计)
  3. TypeScript 的 readonly 仅在编译时有效,运行时成本为零

这是一种注重性能的方法:

// Development: Use Object.freeze() for better error catching
const isDev = process.env.NODE_ENV === 'development';

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

// Now use safeFreeze everywhere
const config = safeFreeze({
  // your config here
});
Enter fullscreen mode Exit fullscreen mode

TypeScript 魔法✨

Object.freeze() 与 TypeScript 的类型系统完美配合:

// Object.freeze() automatically makes properties readonly
const frozen = Object.freeze({ x: 1, y: 2 });
// TypeScript error: Cannot assign to 'x' because it is a readonly property
frozen.x = 2;

// Works with 'as const' for literal types
const DIRECTIONS = Object.freeze({
  UP: 'UP',
  DOWN: 'DOWN',
  LEFT: 'LEFT',
  RIGHT: 'RIGHT'
} as const);

// Type is exactly: 'UP' | 'DOWN' | 'LEFT' | 'RIGHT'
type Direction = typeof DIRECTIONS[keyof typeof DIRECTIONS];
Enter fullscreen mode Exit fullscreen mode

结论

Object.freeze() 乍一看可能很简单,但它是一个非常强大的工具,可以编写更安全、更易于维护的 JavaScript。从保护配置对象到确保不可变状态,它是现代 JavaScript 开发的重要组成部分。

下次你发现自己需要第三方不可变库时,请记住 Object.freeze() 可能就是你所需要的!🥶✨

哦,还有最后一个无耻的插件😁

科拉贝回顾展

如果您想举办回顾会或策划扑克牌会议,不妨看看Kollabe。它是一个功能强大、经过生产测试的平台,并且不断发展,以创造最佳的实时协作体验。

文章来源:https://dev.to/mattlewandowski93/objectfreeze-goes-hard-5cn1
PREV
如何使用 PHP Traits
NEXT
修复 Node 应用中的内存泄漏