Muterbara och Oföränderliga i TypeScript
Den här artikeln förklarar begreppen muterbarhet och oföränderlighet i TypeScript.
YouTube Video
Muterbara och Oföränderliga i TypeScript
Vad är Muterbart?
Muterbart betyder att ett värde kan ändras. Referenstyper som objekt och arrayer är typiska exempel på muterbara datastrukturer.
Exempel på ett Muterbart Objekt
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 }
I denna kod ändras age
-egenskapen hos person
-objektet från 25
till 26
. Eftersom objekt passeras som referens, ändras innehållet vid minnesadressen som lagras i variabeln person
.
Exempel på en Muterbar Array
1// Mutable Example: Array
2let numbers: number[] = [1, 2, 3];
3numbers.push(4);
4console.log(numbers); // [1, 2, 3, 4]
I denna kod används push
-metoden för att lägga till ett nytt element 4
i den ursprungliga arrayen. Detta ändrar den ursprungliga arrayen, vilket gör det till en muterbar operation.
Exempel i en Funktion
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]
När muterbara operationer utförs i en funktion, ändras också den ursprungliga arrayen.
Vad är Oföränderligt?
Oföränderligt betyder att ett värde inte kan ändras. Primitiva typer är i grunden oföränderliga.
Exempel på en Oföränderlig Primitiv Typ
1// Immutable Example: String
2let str: string = "hello";
3str[0] = "H"; // Error: Index assignment is not allowed
4console.log(str); // "hello"
Att försöka ändra den första bokstaven i strängen str
till H
misslyckas eftersom strängar är oföränderliga.
Exempel i en Funktion
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)
Eftersom siffror är oföränderliga påverkar inte operationer inuti en funktion den ursprungliga variabeln.
Oföränderliga Operationer på Arrayer
Arrayer är muterbara, men genom att skapa en ny array istället för att ändra den ursprungliga, kan oföränderliga operationer uppnås.
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)
Här används spridningssyntaxen (...
) för att skapa en ny array newNumbers
. Eftersom den ursprungliga arrayen numbers
förblir oförändrad, är detta en oföränderlig operation.
Fördelar med att använda oföränderliga datastrukturer
Förbättrad förutsägbarhet
Oföränderliga data ändras inte, vilket gör oväntade ändringar mindre sannolika och minskar risken för buggar.
Kompatibilitet med bibliotek som bygger på oföränderlighet
Bibliotek som React
och Redux
är ofta designade utifrån oföränderliga data, vilket gör tillståndshantering enklare när de används på rätt sätt.
Gör objekt oföränderliga med Object.freeze
Object.freeze
kan användas för att förhindra ändringar i ett objekt.
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 }
Men Object.freeze
utför endast en ytlig frysning, vilket innebär att egenskaper hos nästlade objekt förblir ändringsbara.
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)
För att skapa ett helt oföränderligt objekt krävs en djupfrysning.
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)
Sammanfattning
- Ändringsbara data kan modifieras och inkluderar objekt och arrayer.
- Oföränderliga data kan inte modifieras och inkluderar primitiva typer som strängar och nummer.
- Med hjälp av spridningssyntax eller metoder som
map
kan operationer på oföränderliga data utföras. Object.freeze
ochdeepFreeze
kan användas för att förhindra ändringar i objekt.- Att använda oföränderliga data hjälper till att skriva mer förutsägbar och mindre felbenägen kod.
Att anta en oföränderlig design förbättrar kodsäkerhet och läsbarhet, så dra full nytta av det!
Du kan följa med i artikeln ovan med hjälp av Visual Studio Code på vår YouTube-kanal. Vänligen kolla även in YouTube-kanalen.