Mamy tablicę [„javascript”, „jest”, „fajny”, 1, 2, 3]. Zobaczmy co się stanie, jeśli będziemy chcieli ją skopiować i przypisać do zmiennej, a następnie zmodyfikować tę skopiowaną tablicę:
let arr1 = ["javascript", "jest", "fajny", 1, 2, 3];
let arr2 = arr1;
arr2[2] = "super";
console.log(arr1); // ["javascript", "jest", "super", 1, 2, 3]
console.log(arr2); // ["javascript", "jest", "super", 1, 2, 3]
Okazuje się, że zmianie uległy dane w obu tablicach! Dlaczego przy kopiowaniu tablic użycie znaku = nie zadziała?
W javascript wyróżniamy typy danych: proste i referencyjne.
Typy proste to:
- string
- number
- boolean
- null
- undefined
Typy referencyjne to np:
- obiekt
- tablica
- funkcja
Typy proste służą do zapisywania „prostych” danych, jak liczba, string albo inna z wyżej wymienionych danych. Jeśli więc przypiszemy do zmiennej number liczbę 5 (let number = 5), to w pamięci zapisana zostanie liczba 5.
Jednak w przypadku typów referencyjnych do zmiennych nie są przypisywane ich wartości lecz referencje. Oznacza to, że do zmiennej let arr = [1, 2, 3] nie jest przypisana wartość [1, 2, 3], ale adres do miejsca w pamięci, w którym ta wartość jest zapisana. Taki zabieg ma na celu zaoszczędzenie pamięci, ponieważ wartość dla arr zapisana jest tylko jeden raz, a odwoływać się do niej można z kilku miejsc w kodzie.
Kiedy więc zmiennej arr2 przypiszemy zmienną arr1, to kopiujemy referencję do obiektu, a nie sam obiekt (w naszym przypadku obiektem tym jest tablica). Teraz dwie zmienne – arr1 i arr2 odwołują się do tego samej tablicy, więc jeśli zmienimy wartość którejkolwiek z nich, zmieni się też wartość drugiej. Jak temu zaradzić?
Jak skopiować tablicę w javascript?
Wiemy już, że nie możemy uzyć znaku = do skopiowania wartości tablicy. Ale możemy wykorzystać inne sposoby:
1. slice();
let arr1 = ["javascript", "jest", "fajny", 1, 2, 3];
let arr2 = arr1.slice();
arr2[2] = "super";
console.log(arr1); // ["javascript", "jest", "fajny", 1, 2, 3]
console.log(arr2); // ["javascript", "jest", "super", 1, 2, 3]
2. spread operator
ES 6 wprowadziło nowy sposób na kopowanie tablic, jest nim operator spread, czyli trzy kropki stawiane przed podaniem wartości, jakie chce się iterować do miejsca docelowego. Oznacza to, że pisząc …arr1 „wrzucasz” po kolei wszystkie wartości z arr1 do miejsca, w które chcesz je skopiować:
let arr1 = ["javascript", "jest", "fajny", 1, 2, 3];
let arr2 = [...arr1];
arr2[2] = "super";
console.log(arr1); // ["javascript", "jest", "fajny", 1, 2, 3]
console.log(arr2); // ["javascript", "jest", "super", 1, 2, 3]
3. concat();
Można też użyć funkcji łączenia tablic i pustą tablicę połączyć z już istniejącą, tworząc w ten sposób nową tablicę.
let arr1 = ["javascript", "jest", "fajny", 1, 2, 3];
let arr2 = [].concat(arr1);
arr2[2] = "super";
console.log(arr1); // ["javascript", "jest", "fajny", 1, 2, 3]
console.log(arr2); // ["javascript", "jest", "super", 1, 2, 3]
4. Array.from();
Kolejną opcją kopiowania tablic jest utworzenie nowej tablicy z istniejącej tablicy. Do tego posłuży funkcja Array.from();
let arr1 = ["javascript", "jest", "fajny", 1, 2, 3];
let arr2 = Array.from(arr1);
arr2[2] = "super";
console.log(arr1); // ["javascript", "jest", "fajny", 1, 2, 3]
console.log(arr2); // ["javascript", "jest", "super", 1, 2, 3]
Tablice wielowymiarowe
Wszystkie z wymienianych wyżej sposobów mają jednak wadę – mogą skopiować tablicę tylko na jednym poziomie (tzw. shallow copy – płytka kopia), więc NIE nadają się do kopiowania tablic wielowymiarowych.
let arr1 = [[1, 2, 3]];
let arr2 = arr1.slice();
// or let arr2 = […arr1];
// or let arr2 = [].concat(arr1);
// or let arr2 = Array.from(arr1);
arr2[0][1] = "super";
console.log(arr1); // [[1, "super", 3]]
console.log(arr2); // [[1, "super", 3]]
Kopiowanie tablic wielowymiarowych w javascript
Jeśli chcemy skopiować tablicę wielowymiarową, musimy napisać własną funkcję. Pomoże nam tu funkcja rekurencyjna – taka, która wywołuje samą siebie. Ilekroć nasza funkcja natrafi na zagnieżdżoną tablicę, wywoła się ponownie, aby skopiować każdy jej elemet. Do zwrócenia nowej tablicy użyjemy natomiast funkcji map();
let arr1 = [1, 2, 3, [4, 5, 6, [7, 8, 9, [10, 11, 12]]]];
function arrCopy(arr) {
let arr2 = arr.map( val => {
if (Array.isArray(val)) {
return arrCopy(val)
} else {
return val
}
})
return arr2;
}
let arr2 = arrCopy(arr1);
arr2[3][3][3][0] = "super";
console.log(arr1); // [1, 2, 3, [4, 5, 6, [7, 8, 9, [10, 11, 12]]]]
console.log(arr2); // [1, 2, 3, [4, 5, 6, [7, 8, 9, ["super", 11, 12]]]]
Teraz możemy dowolnie zmieniać zagnieżdżone wartości w skopiowanej tablicy bez obawy, że pierwotna tablica ulegnie zmianie 🙂
const arr2 = JSON.parse(JSON.stringify(arr1)) – dużo prościej