標題說明了一切。讓我們來談談 JavaScript 最被低估的功能之一: Object.freeze() 。這種強大的不變性不僅僅是另一種方法 - 它是您編寫更安全、更可預測的程式碼的秘密武器✨。
老實說,當我第一次發現 Object.freeze() 時,我幾乎忽略了它。 「只是不要改變你的物體,」我想。但隨著我的應用程式變得越來越複雜,我開始看到它的真正價值。現在,它是我工具包的重要組成部分。
讓我向您展示為什麼 Object.freeze() 絕對必要,以及它如何提升您的 JavaScript 遊戲水平。讓我們深入研究一些現實世界的例子! 🥶
我們都經歷過這種情況——建立「常量」物件,但最終會在我們的程式碼庫中的某個地方發生變異。
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;
現在你的配置其實是不可變的。不再除錯神秘的配置更改。
這是狀態管理的遊戲規則改變者,特別是在 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';
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
預設情況下,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";
曾經因為事件物件在事件發生後被修改而出現錯誤嗎?不再!
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);
}
}
保持 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;
雖然我們現在有了帶有 # 的私有字段,但 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() 功能強大,但了解其效能影響也很重要:
凍結操作本身是有成本的,尤其是使用 DeepFreeze 時
凍結物件的讀取速度可能會稍慢(但差異通常可以忽略不計)
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
});
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