Изменяемое и Неизменяемое в 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 }
В этом коде свойство age
объекта person
изменяется с 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
остается неизменным, это является неизменяемой операцией.
Преимущества использования неизменяемых структур данных
Повышенная предсказуемость
Неизменяемые данные не изменяются, что делает неожиданные изменения менее вероятными и снижает вероятность ошибок.
Совместимость с библиотеками, основанными на неизменяемости
Библиотеки, такие как React
и Redux
, часто проектируются на основе неизменяемых данных, что упрощает управление состоянием при правильном использовании.
Создание неизменяемых объектов с помощью 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.freeze
иdeepFreeze
могут использоваться для предотвращения изменений в объектах.- Использование неизменяемых данных помогает писать более предсказуемый и менее подверженный ошибкам код.
Применение дизайна с неизменяемостью повышает безопасность и читаемость кода, поэтому используйте его в полной мере!
Вы можете следовать этой статье, используя Visual Studio Code на нашем YouTube-канале. Пожалуйста, также посмотрите наш YouTube-канал.