Có thể thay đổi và không thể thay đổi trong TypeScript
Bài viết này giải thích các khái niệm về có thể thay đổi và không thể thay đổi trong TypeScript.
YouTube Video
Có thể thay đổi và không thể thay đổi trong TypeScript
Cái gì là có thể thay đổi?
Có thể thay đổi nghĩa là một giá trị có thể được thay đổi. Các kiểu tham chiếu như đối tượng và mảng là những ví dụ điển hình của cấu trúc dữ liệu có thể thay đổi.
Ví dụ về một đối tượng có thể thay đổi
1type Person = { name: string; age: number };
2
3// Mutable Example: Object
4let person: Person = { name: "Alice", age: 25 };
5person.age = 26;
6console.log(person); // { name: "Alice", age: 26 }
Trong đoạn mã này, thuộc tính age
của đối tượng person
được thay đổi từ 25
thành 26
. Bởi vì các đối tượng được truyền bằng tham chiếu, nội dung tại địa chỉ bộ nhớ lưu trữ trong biến person
bị thay đổi.
Ví dụ về một mảng có thể thay đổi
1// Mutable Example: Array
2let numbers: number[] = [1, 2, 3];
3numbers.push(4);
4console.log(numbers); // [1, 2, 3, 4]
Trong đoạn mã này, phương thức push
được sử dụng để thêm phần tử mới 4
vào mảng ban đầu. Điều này thay đổi mảng ban đầu, khiến nó trở thành một thao tác có thể thay đổi.
Ví dụ trong một hàm
1// Mutable Example: Function
2function append(arr: number[], value: number): void {
3 arr.push(value); // Modify the original array
4 console.log(arr);
5}
6
7let nums: number[] = [1, 2, 3];
8append(nums, 4);
9console.log(nums); // [1, 2, 3, 4]
Khi thực hiện các thao tác có thể thay đổi bên trong một hàm, mảng ban đầu cũng bị thay đổi.
Cái gì là không thể thay đổi?
Không thể thay đổi nghĩa là một giá trị không thể được thay đổi. Các kiểu nguyên thủy về cơ bản là không thể thay đổi.
Ví dụ về một kiểu nguyên thủy không thể thay đổi
1// Immutable Example: String
2let str: string = "hello";
3str[0] = "H"; // Error: Index assignment is not allowed
4console.log(str); // "hello"
Cố gắng thay đổi ký tự đầu tiên của chuỗi str
thành H
sẽ thất bại vì các chuỗi là không thể thay đổi.
Ví dụ trong một hàm
1// Immutable Example: Function
2function increment(num: number): number {
3 num++; // This modifies only the local copy of num
4 return num;
5}
6
7let number: number = 10;
8console.log(increment(number)); // 11
9console.log(number); // 10 (original number remains unchanged)
Vì các số là không thể thay đổi, các thao tác bên trong một hàm sẽ không ảnh hưởng đến biến ban đầu.
Các thao tác không thể thay đổi trên mảng
Các mảng là có thể thay đổi, nhưng bằng cách tạo một mảng mới thay vì thay đổi mảng ban đầu, có thể thực hiện các thao tác không thể thay đổi.
1// Create an array of numbers
2let numbers: number[] = [1, 2, 3];
3
4// Immutable Example: Creating a new array
5let newNumbers: number[] = [...numbers, 4];
6
7console.log(numbers); // [1, 2, 3] (original array is unchanged)
8console.log(newNumbers); // [1, 2, 3, 4] (new array with an added element)
Ở đây, cú pháp spread (...
) được sử dụng để tạo một mảng mới newNumbers
. Vì mảng gốc numbers
không thay đổi, đây là một thao tác bất biến.
Lợi ích của việc sử dụng cấu trúc dữ liệu bất biến
Cải thiện tính dự đoán
Dữ liệu bất biến không thay đổi, làm giảm khả năng xảy ra các sửa đổi bất ngờ và giảm nguy cơ lỗi.
Tương thích với các thư viện dựa trên tính bất biến
Các thư viện như React
và Redux
thường được thiết kế dựa trên dữ liệu bất biến, giúp việc quản lý trạng thái trở nên dễ dàng hơn khi được sử dụng một cách hợp lý.
Làm cho các đối tượng trở nên bất biến với Object.freeze
Object.freeze
có thể được sử dụng để ngăn chặn các sửa đổi đối với một đối tượng.
1// Create a frozen object (properties cannot be modified)
2const person = Object.freeze({ name: "Alice", age: 25 });
3
4// Attempt to modify a property (ignored in non-strict mode, error in strict mode)
5person.age = 26;
6
7console.log(person); // { name: "Alice", age: 25 }
Tuy nhiên, Object.freeze
chỉ thực hiện việc đóng băng nông, có nghĩa là các thuộc tính của các đối tượng lồng nhau vẫn có thể thay đổi.
1// Create a frozen object with a nested object
2const user: Readonly<{ profile: { name: string } }> = Object.freeze({
3 profile: { name: "Bob" }
4});
5
6// Attempt to modify a nested property (this works because Object.freeze() is shallow)
7user.profile.name = "Charlie"; // No TypeScript error, but still mutable
8
9console.log(user.profile.name); // "Charlie" (nested object is still mutable)
Để tạo ra một đối tượng hoàn toàn bất biến, cần thực hiện một đóng băng sâu.
1// Function to deeply freeze an object, making all nested objects immutable
2function deepFreeze<T>(obj: T): Readonly<T> {
3 Object.freeze(obj);
4 Object.getOwnPropertyNames(obj).forEach(prop => {
5 const value = (obj as any)[prop];
6 if (typeof value === "object" && value !== null) {
7 deepFreeze(value);
8 }
9 });
10 return obj;
11}
12
13// Create a deeply frozen object
14const user = deepFreeze({
15 profile: { name: "Bob" }
16});
17
18// Attempt to modify a nested property
19// (this will now throw an error in strict mode)
20user.profile.name = "Charlie"; // No TypeScript error, but modification is not allowed at runtime
21
22console.log(user.profile.name); // "Bob" (unchanged due to deep freeze)
Tóm tắt
- Dữ liệu có thể thay đổi có thể được sửa đổi và bao gồm các đối tượng và mảng.
- Dữ liệu bất biến không thể thay đổi và bao gồm các kiểu dữ liệu nguyên thủy như chuỗi và số.
- Bằng cách sử dụng cú pháp spread hoặc các phương thức như
map
, có thể thực hiện các thao tác bất biến trên dữ liệu. Object.freeze
vàdeepFreeze
có thể được sử dụng để ngăn các sửa đổi đối với các đối tượng.- Sử dụng dữ liệu bất biến giúp viết mã dễ dự đoán hơn và ít dễ xảy ra lỗi hơn.
Áp dụng thiết kế bất biến cải thiện độ an toàn và dễ đọc của mã, vì vậy hãy tận dụng triệt để nó!
Bạn có thể làm theo bài viết trên bằng cách sử dụng Visual Studio Code trên kênh YouTube của chúng tôi. Vui lòng ghé thăm kênh YouTube.