Mutowalność i niemutowalność w JavaScript

Mutowalność i niemutowalność w JavaScript

Ten artykuł wyjaśnia pojęcia mutowalności i niemutowalności w JavaScript.

YouTube Video

Mutowalność i niemutowalność w JavaScript

Czym jest mutowalność?

Mutowalne oznacza, że wartość może zostać zmieniona. Obiekty i tablice, które są typami referencyjnymi, są typowymi przykładami mutowalnych struktur danych.

Przykład mutowalnego obiektu

1let person = { name: "Alice", age: 25 };
2person.age = 26;
3console.log(person); // { name: "Alice", age: 26 }

W tym kodzie właściwość age obiektu person została zmieniona z 25 na 26. Ponieważ obiekty są przekazywane przez referencję, zawartość pod adresem pamięci przechowywanym w zmiennej person zostaje zmodyfikowana.

Przykład mutowalnej tablicy

1let numbers = [1, 2, 3];
2numbers.push(4);
3console.log(numbers); // [1, 2, 3, 4]

W tym kodzie metoda push jest używana do dodania nowego elementu 4 do oryginalnej tablicy. To modyfikuje oryginalną tablicę, co czyni tę operację mutowalną.

Przykład w funkcji

 1// Function to append a value to an array
 2function append(arr, value) {
 3    arr.push(value); // Modify the original array
 4    console.log(arr);
 5}
 6
 7let numbers = [1, 2, 3];
 8append(numbers, 4);
 9
10console.log(numbers); // [1, 2, 3, 4] (original array is modified)

Podczas wykonywania operacji mutowalnych wewnątrz funkcji, oryginalna tablica również zostaje zmodyfikowana.

Czym jest niemutowalność?

Niemutowalne oznacza, że wartość nie może zostać zmieniona. Typy prymitywne są z natury niemutowalne.

Przykład niemutowalnego typu prymitywnego

1let str = "hello";
2str[0] = "H";
3console.log(str); // "hello"

Próba zmiany pierwszego znaku w ciągu str na H nie powiedzie się, ponieważ ciągi są niemutowalne.

Przykład w funkcji

 1// Function to increment a number
 2function increment(num) {
 3    num++; // This modifies only the local copy of num
 4    console.log(num);
 5}
 6
 7let number = 10;
 8increment(number);
 9
10console.log(number); // 10 (original number remains unchanged)

Ponieważ liczby są niemutowalne, operacje wykonane wewnątrz funkcji nie wpływają na oryginalną zmienną.

Niemutowalne operacje na tablicach

Tablice są mutowalne, ale utworzenie nowej tablicy zamiast modyfikacji oryginalnej pozwala na niemutowalne operacje.

1// Create an array of numbers
2let numbers = [1, 2, 3];
3
4// Create a new array by spreading the original and adding a new element
5let newNumbers = [...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)

Tutaj składnia rozprzestrzeniania (...) jest używana do utworzenia nowej tablicy newNumbers. Ponieważ oryginalna tablica numbers nie jest modyfikowana, jest to operacja niemutowalna.

Zalety korzystania z niemutowalnych struktur danych

Zwiększona przewidywalność

Ponieważ danych niemutowalnych nie można zmieniać, nieoczekiwane modyfikacje są mniej prawdopodobne, co zmniejsza ryzyko błędów.

Kompatybilność z bibliotekami opartymi na niemutowalności

Biblioteki takie jak React i Redux są często zaprojektowane z myślą o danych niemutowalnych, co ułatwia zarządzanie stanem przy prawidłowym użytkowaniu.

Uczynienie obiektów niemutowalnymi za pomocą Object.freeze

Funkcja Object.freeze może być użyta, aby zapobiec modyfikacjom obiektu.

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 }

Jednak Object.freeze wykonuje płytkie zamrożenie, co oznacza, że właściwości zagnieżdżonych obiektów pozostają mutowalne.

1// Create a frozen object with a nested object
2const user = Object.freeze({ profile: { name: "Bob" } });
3
4// Attempt to modify a nested property (this works because Object.freeze() is shallow)
5user.profile.name = "Charlie";
6
7console.log(user.profile.name); // "Charlie" (nested object is still mutable)

Aby utworzyć w pełni niemutowalny obiekt, wymagane jest głębokie zamrożenie.

 1// Function to deeply freeze an object, making all nested objects immutable
 2function deepFreeze(obj) {
 3  Object.keys(obj).forEach(key => {
 4    if (typeof obj[key] === "object" && obj[key] !== null) {
 5      deepFreeze(obj[key]); // Recursively freeze nested objects
 6    }
 7  });
 8  return Object.freeze(obj); // Freeze the top-level object
 9}
10
11// Create a deeply frozen object
12const user = deepFreeze({ profile: { name: "Bob" } });
13
14// Attempt to modify a nested property (ignored)
15user.profile.name = "Charlie";
16
17console.log(user.profile.name); // "Bob" (unchanged due to deep freeze)

Podsumowanie

  • Dane mutowalne mogą być modyfikowane, w tym obiekty i tablice.
  • Dane niemutowalne nie mogą być modyfikowane, w tym typy prymitywne, takie jak ciągi znaków i liczby.
  • Używanie składni rozprzestrzeniania lub map umożliwia operacje z niemutowalnymi danymi..
  • Funkcje Object.freeze i deepFreeze mogą być używane, aby zapobiec modyfikacjom obiektów.
  • Korzystanie z danych niemutowalnych pozwala na bardziej przewidywalny i mniej podatny na błędy kod.

Projektowanie w oparciu o niemutowalność zwiększa bezpieczeństwo i czytelność kodu, więc warto z tego korzystać!

Możesz śledzić ten artykuł, korzystając z Visual Studio Code na naszym kanale YouTube. Proszę również sprawdzić nasz kanał YouTube.

YouTube Video