W laboratoriach Parku Jurajskiego obowiązuje surowa zasada - nigdy nie modyfikujesz oryginalnej próbki DNA. Każda zmiana tworzy nową kopię. To samo podejście stosujemy w programowaniu funkcyjnym z danymi - oryginalne struktury pozostają nienaruszone, a każda modyfikacja tworzy nowy obiekt.
Operator spread (
...) to podstawowe narzędzie do tworzenia niemutowalnych kopii obiektów i tablic:1// Kopiowanie obiektu z modyfikacja
2const dinosaur = { name: 'Rex', health: 100, zone: 'A' };
3const movedDino = { ...dinosaur, zone: 'B' };
4// dinosaur: { name: 'Rex', health: 100, zone: 'A' } - niezmieniony
5// movedDino: { name: 'Rex', health: 100, zone: 'B' } - nowy obiekt
6
7// Kopiowanie tablicy z dodaniem elementu
8const zones = ['A', 'B', 'C'];
9const extendedZones = [...zones, 'D'];
10// zones: ['A', 'B', 'C'] - niezmieniona
11// extendedZones: ['A', 'B', 'C', 'D']
12
13// Usuwanie elementu z tablicy (bez mutacji)
14const dinosaurs = ['Rex', 'Blue', 'Brachio', 'Stego'];
15const withoutBlue = dinosaurs.filter((d) => d !== 'Blue');
16// dinosaurs: ['Rex', 'Blue', 'Brachio', 'Stego'] - niezmieniona
17// withoutBlue: ['Rex', 'Brachio', 'Stego']Spread operator tworzy płytką kopię - zagnieżdżone obiekty nadal są współdzielone! Musisz spreadować na każdym poziomie:
1const park = {
2 name: 'Jurassic Park',
3 zones: {
4 A: { dinosaurs: ['Rex'], voltage: 10000 },
5 B: { dinosaurs: ['Brachio'], voltage: 5000 },
6 },
7};
8
9// ZLE - plytka kopia, zones jest wspoldzielone
10const badCopy = { ...park };
11badCopy.zones.A.voltage = 0; // ZMIENIA tez oryginal!
12
13// DOBRZE - gleboka kopia na kazdym poziomie
14const updatedPark = {
15 ...park,
16 zones: {
17 ...park.zones,
18 A: {
19 ...park.zones.A,
20 voltage: 15000,
21 },
22 },
23};
24// park.zones.A.voltage: 10000 (niezmieniony)
25// updatedPark.zones.A.voltage: 15000 (nowy obiekt)Object.freeze() zapobiega modyfikacji obiektu w runtime. Próba zmiany jest po cichu ignorowana (lub rzuca błąd w strict mode):1const CONFIG = Object.freeze({
2 maxDinosaurs: 100,
3 maxVoltage: 50000,
4 alertThreshold: 3,
5});
6
7CONFIG.maxDinosaurs = 200; // Ignorowane! (lub Error w strict mode)
8console.log(CONFIG.maxDinosaurs); // nadal 100
9
10// UWAGA: freeze jest plytki!
11const deepConfig = Object.freeze({
12 zones: { A: { limit: 10 } },
13});
14deepConfig.zones.A.limit = 999; // TO ZADZIALA - freeze jest plytkistructuredClone() tworzy głęboką kopię obiektu - wszystkie zagnieżdżone struktury są niezależne:1const original = {
2 name: 'Jurassic Park',
3 zones: [
4 { id: 'A', dinosaurs: [{ name: 'Rex', health: 100 }] },
5 { id: 'B', dinosaurs: [{ name: 'Blue', health: 95 }] },
6 ],
7};
8
9const deepCopy = structuredClone(original);
10deepCopy.zones[0].dinosaurs[0].health = 50;
11
12console.log(original.zones[0].dinosaurs[0].health); // 100 (niezmieniony!)
13console.log(deepCopy.zones[0].dinosaurs[0].health); // 50 (niezalezna kopia)Pełny zestaw operacji na tablicach bez mutacji:
1const dinosaurs = [
2 { id: 1, name: 'Rex', health: 100 },
3 { id: 2, name: 'Blue', health: 95 },
4 { id: 3, name: 'Brachio', health: 88 },
5];
6
7// Dodawanie
8const added = [...dinosaurs, { id: 4, name: 'Stego', health: 100 }];
9
10// Usuwanie
11const removed = dinosaurs.filter((d) => d.id !== 2);
12
13// Aktualizacja
14const updated = dinosaurs.map((d) =>
15 d.id === 1 ? { ...d, health: 80 } : d
16);
17
18// Sortowanie (bez mutacji - tworzymy kopie przed sort)
19const sorted = [...dinosaurs].sort((a, b) => a.health - b.health);