TypeScript中的可变性和不可变性

TypeScript中的可变性和不可变性

本文将介绍TypeScript中的可变性和不可变性的概念。

YouTube Video

TypeScript中的可变性和不可变性

什么是可变性?

可变性意味着值是可以更改的。像对象和数组这样的引用类型是可变数据结构的典型例子。

可变对象的示例

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 }

在此代码中,person对象的age属性从25更改为26。由于对象是通过引用传递的,因此会修改存储在person变量中的内存地址所表示的内容。

可变数组的示例

1// Mutable Example: Array
2let numbers: number[] = [1, 2, 3];
3numbers.push(4);
4console.log(numbers); // [1, 2, 3, 4]

在此代码中,使用push方法向原数组添加了一个新元素4。这修改了原始数组,因此这是一个可变操作。

函数中的示例

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]

在函数中执行可变操作时,原数组也会被修改。

什么是不可变性?

不可变性意味着值是不能更改的原始类型本质上是不可变的。

不可变原始类型的示例

1// Immutable Example: String
2let str: string = "hello";
3str[0] = "H"; // Error: Index assignment is not allowed
4console.log(str); // "hello"

尝试将字符串str的第一个字符更改为H会失败,因为字符串是不可变的

函数中的示例

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)

由于数字是不可变的,函数中的操作不会影响原始变量。

数组上的不可变操作

数组是可变的,但通过创建一个新数组而不是修改原数组,可以实现不可变操作。

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)

在这里,扩展语法 (...) 被用来创建一个新的数组 newNumbers。由于原始的 numbers 数组保持不变,这是一个不可变操作。

使用不可变数据结构的好处

提高可预测性

不可变数据不会更改,减少了意外修改的可能性,从而降低了产生错误的机会。

与基于不可变性的库的兼容性

ReactRedux 这样的库通常是基于不可变数据设计的,合理使用时可以更轻松地进行状态管理。

使用 Object.freeze 将对象变为不可变

Object.freeze 可用于阻止对对象的修改。

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 }

然而,Object.freeze 只执行 浅冻结,这意味着嵌套对象的属性仍然是可变的。

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)

若要创建完全不可变的对象,需要进行 深冻结

 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)

总结

  • 可变数据是可以修改的,包括对象和数组。
  • 不可变数据是不可修改的,包括字符串和数字等原始类型。
  • 使用 扩展语法或 map 等方法,可以执行不可变数据操作。
  • 可以使用 Object.freezedeepFreeze 来阻止对对象的修改。
  • 使用不可变数据有助于编写更可预测且更少错误的代码。

采用不可变设计可以提高代码的安全性和可读性,因此请充分利用它!

您可以在我们的YouTube频道上使用Visual Studio Code跟随上述文章进行学习。 请也查看我们的YouTube频道。

YouTube Video