자바스크립트에서 원시값은 값에 의한 전달, 객체는 참조에 의한 전달 방식으로 동작한다.
함수의 매개변수
또한 함수 몸체 내부에서 변수와 동일하게 취급되므로
타입
에 따라 값에 의한 전달
, 참조에 의한 전달
방식을 그대로 따르게 된다.
// 매개변수 primitive는 원시 값을 전달받고, 매개변수 obj는 객체를 전달받는다.
function changeVal(primitive, obj) {
primitive += 100;
obj.name = 'Kim';
}
// 외부 상태
var num = 100;
var person = { name: 'Lee' };
console.log(num); // 100
console.log(person); // {name: 'Lee'}
// 원시 값은 값 자체가 복사되어 전달되고 객체는 참조 값이 복사되어 전달된다.
changeVal(num, person);
// 원시 값은 원본이 훼손되지 않는다.
console.log(num); // 100
// 객체는 원본이 훼손된다.
console.log(person); // {name: 'Kim'}
⛔️ 여기서 문제는 참조(객체)에 의한 전달인 경우 원본이 훼손된다는 것이다.
옵저버 패턴
등을 통해 참조를 공유하는 모든 객체를 관리해야 한다.자바스크립트에서 객체를 불변 객체로 만들기 위해서는 객체를 복제하고,
해당 복제본을 수정하여 원래 객체가 변경되지 않도록 해야 한다.
이를 위해서는 다음과 같은 방법을 사용할 수 있다.
Object.assign() 메서드를 사용하여 빈 객체에 원래 객체의 프로퍼티를 복사한 다음,
이를 수정하여 불변 객체를 만들 수 있다.
const originalObj = { a: 1, b: 2 };
const immutableObj = Object.assign({}, originalObj, { c: 3 });
Spread 연산자를 사용하여 객체의 프로퍼티를 빈 객체에 복사한 다음,
이를 수정하여 불변 객체를 만들 수 있다.
const originalObj = { a: 1, b: 2 };
const immutableObj = { ...originalObj, c: 3 };
Immutable.js는 자바스크립트의 불변성을 보장하는 라이브러리다.
이를 사용하여 객체를 불변 객체로 만들 수 있다.
import { Map } from 'immutable';
const originalObj = Map({ a: 1, b: 2 });
const immutableObj = originalObj.set('c', 3);
이러한 방법을 사용하여 객체를 불변 객체로 만들면,
객체를 변경하려는 시도가 발생할 경우 에러가 발생하여 코드의 안정성을 보장할 수 있다.
⛔️ 다만, 얕은 복사에서도 객체 내부의 값들은 참조로 복제하여 새로운 객체를 생성한다.
const originalObj = { a: { b: 1 } };
const shallowCopyObj = Object.assign({}, originalObj);
shallowCopyObj.a.b = 2;
console.log(originalObj.a.b); // 2
console.log(shallowCopyObj.a.b); // 2
참조
로 가져오기에 원본 훼손의 결과를 초래할 수 있다.깊은 복사
를 통해 객체 내부의 값들도 복제하여 완전히 새로운 객체를 생성해야 한다.원본 객체 내부의 값이 객체인 경우, 복제된 객체와 원본 객체가 같은 참조를 가지게 된다.
이 경우, 복제된 객체를 수정하면 원본 객체도 같이 수정되어 원하지 않는 결과를 초래할 수 있다.
따라서, 객체 내부의 값이 객체인 객체를 불변 객체로 만들기 위해서는 깊은 복사(deep copy)를 해야 한다.
깊은 복사를 수행하는 방법으로는 JSON.stringify()
와 JSON.parse()
를 사용하는 방법이 있다.
하지만, 이 방법은 객체 내부에 함수가 포함되어 있거나,
객체가 순환 참조(circular reference)를 가지고 있는 경우에는 사용할 수 없다.
더 나은 깊은 복사 방법으로는, lodash
나 Immutable.js
와 같은 라이브러리를 사용하거나,
직접 깊은 복사 함수를 작성하는 방법이 있다.
이를 위해서는 재귀(recursion)
를 사용하여 객체 내부의 값을 순회하며,
값을 복제하여 새로운 객체를 생성하는 방법을 사용할 수 있다.
function deepCopy(obj) {
if (typeof obj !== 'object' || obj === null) {
return obj;
}
const copiedObj = Array.isArray(obj) ? [] : {};
for (const key in obj) {
copiedObj[key] = deepCopy(obj[key]);
}
return copiedObj;
}
const deepCopyObj = deepCopy(originalObj);
deepCopyObj.a.b = 3;
console.log(originalObj.a.b); // 2
console.log(deepCopyObj.a.b); // 3