Mutable og Immutable i TypeScript

Mutable og Immutable i TypeScript

Denne artikel forklarer mutable og immutable begreber i TypeScript.

YouTube Video

Mutable og Immutable i TypeScript

Hvad er Mutable?

Mutable betyder, at en værdi kan ændres. Referencetyper som objekter og arrays er typiske eksempler på mutable datastrukturer.

Eksempel på et Mutable 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 denne kode ændres age-egenskaben for person-objektet fra 25 til 26. Da objekter videregives som reference, ændres indholdet på hukommelsesadressen gemt i person-variablen.

Eksempel på et Mutable Array

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

I denne kode bruges push-metoden til at tilføje et nyt element 4 til det originale array. Dette ændrer det originale array, hvilket gør det til en mutable operation.

Eksempel 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 mutable operationer udføres inde i en funktion, ændres det originale array også.

Hvad er Immutable?

Immutable betyder, at en værdi ikke kan ændres. Primitive typer er grundlæggende immutable.

Eksempel på en Immutabel Primitiv Type

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

Forsøg på at ændre det første tegn i strengen str til H mislykkes, fordi strenge er immutable.

Eksempel 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)

Da tal er immutable, påvirker operationer inde i en funktion ikke den originale variabel.

Immutable Operationer på Arrays

Arrays er mutable, men ved at oprette et nyt array i stedet for at ændre det originale, kan immutable operationer opnå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)

Her bruges spredningssyntaksen (...) til at oprette et nyt array newNumbers. Da det originale numbers array forbliver uændret, er dette en uforanderlig operation.

Fordele ved at bruge uforanderlige datastrukturer

Forbedret forudsigelighed

Uforanderlige data ændrer sig ikke, hvilket gør uventede ændringer mindre sandsynlige og reducerer risikoen for fejl.

Kompatibilitet med biblioteker baseret på uforanderlighed

Biblioteker som React og Redux er ofte designet baseret på uforanderlige data, hvilket gør tilstandshåndtering lettere, når de bruges korrekt.

Gør objekter uforanderlige med Object.freeze

Object.freeze kan bruges til at forhindre ændringer i et 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 }

Dog udfører Object.freeze kun en overfladisk fryser, hvilket betyder, at egenskaberne i indlejrede objekter forbliver foranderlige.

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)

For at skabe et helt uforanderligt objekt kræves en dyb fryser.

 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)

Sammendrag

  • Foranderlige data kan ændres og inkluderer objekter og arrays.
  • Uforanderlige data kan ikke ændres og inkluderer primitive typer såsom strenge og tal.
  • Ved at bruge spredningssyntaks eller metoder som map kan uforanderlige dataoperationer udføres.
  • Object.freeze og deepFreeze kan bruges til at forhindre ændringer i objekter.
  • Brug af uforanderlige data hjælper med at skrive mere forudsigelig og mindre fejlsikker kode.

Ved at adoptere et uforanderligt design forbedres kodesikkerhed og læsbarhed, så udnyt det fuldt ud!

Du kan følge med i ovenstående artikel ved hjælp af Visual Studio Code på vores YouTube-kanal. Husk også at tjekke YouTube-kanalen.

YouTube Video