JavaScript에서 forEach나 map을 사용할 때 메서드의 콜백 함수는 인자로 배열의 각 요소에 대한 참조를 받게 되는데, 이는 배열 안의 객체 자체를 가리키는 참조를 의미합니다.
그럼 그 인자를 재할당 하면 원본 배열의 요소가 바뀔까요?
let array = [{ a: 1 }, { b: 2 }];
array.forEach(item => {
item = { c: 3 }; // 새 객체로 재할당
});
결론부터 말하자면 바뀌지 않습니다.
이유
console.log(array); // [{ a: 1 }, { b: 2 }]
item 이라는 지역 변수가 참조(주소)값을 가지고 있어서 원본을 변경 할 수 있다고 착각할 수 있지만 이 재할당은 지역 변수의 값을 변경할 뿐, 원본 배열에 영향을 주지 않습니다.
const array = [{ a: 1 }, { b: 2 }];
let item = array[0];
item = { c: 3 };
console.log(item); // { c: 3 }
item이 array의 0번 인덱스 값을 할당 받는 순간 item이 가지고 있는건 해당 인덱스의 주소값입니다.
즉 array[0]가 가리키는 메모리 주소에 접근해서 해당 값을 변경하는게 아니라 item이라는 변수가 가지고 있는 주소값만 다른 값으로 변경하는거죠.
원본 배열의 요소를 변경하려면?
일반적으로 이뮤터블하게 함수를 쓰는게 선호되고 중첩 객체의 경우 객체 속성에 접근하면 원본도 수정되기 때문에 원본 배열의 요소 자체를 변경할 일은 많지 않죠.
그래도 변경 할 일이 있다면 2번째 인자인 index와, 3번째 인자인 원본 배열을 활용할 수 있습니다.
let numbers = [1, 2, 3];
numbers.forEach((item, index, arr) => {
arr[index] = item * 2; // 배열의 각 요소를 2배로 변경
});
console.log(numbers); // [2, 4, 6]
결론
forEach
와 같은 메소드를 사용할 때는 JavaScript의 참조 타입 동작 방식을 이해하는 것이 중요합니다. 변수에 새 객체를 재할당하는 것은 원본 객체의 참조를 변경하지 않으며, 원본 배열에 영향을 미치지 않습니다.
원본 객체를 수정하는 것도 사이드 이펙트를 부를 수 있어서 선호되지 않을 뿐 의도했다면 나쁜 방법은 아닙니다.