阿川私房教材:
學 JavaScript 前端,帶作品集去面試!

63 個專案實戰,寫出作品集,讓面試官眼前一亮!

立即開始免費試讀!

標題說明了一切。讓我們來談談 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;

現在你的配置其實是不可變的。不再除錯神秘的配置更改。

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';

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

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";

5:保護事件處理程序

曾經因為事件物件在事件發生後被修改而出現錯誤嗎?不再!

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

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;

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';

性能考慮因素🚀

雖然 Object.freeze() 功能強大,但了解其效能影響也很重要:

  1. 凍結操作本身是有成本的,尤其是使用 DeepFreeze 時

  2. 凍結物件的讀取速度可能會稍慢(但差異通常可以忽略不計)

  3. TypeScript 的唯讀僅在編譯時執行,執行階段成本為零

這是一種注重性能的方法:

// 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
});

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];

結論

Object.freeze() 乍看之下似乎很簡單,但它是編寫更安全、更易於維護的 JavaScript 的一個非常強大的工具。從保護配置物件到確保不可變狀態,它是現代 JavaScript 開發的重要組成部分。

下次當您發現自己需要第三方不變性函式庫時,請記住 Object.freeze() 可能就是您所需要的! 🥶✨

哦,還有最後一個無恥的插頭😁

科拉貝回顧展

如果您想舉辦回顧或計劃撲克會議,請查看Kollabe 。它是一個經過生產測試的強大平台,不斷發展以創造最佳的即時協作體驗。


原文出處:https://dev.to/mattlewandowski93/objectfreeze-goes-hard-5cn1


共有 0 則留言


精選技術文章翻譯,幫助開發者持續吸收新知。

阿川私房教材:
學 JavaScript 前端,帶作品集去面試!

63 個專案實戰,寫出作品集,讓面試官眼前一亮!

立即開始免費試讀!