불변 객체
지난 시간에 배운 내용에 따르면, 자바스크립트의 데이터 타입은 기본형과 참조형으로 분류되며,
기본형 데이터는 불변값, 참조형 데이터는 가변값을 나타냅니다.
이번 시간에 배울 객체는 참조형 데이터에 속하며, 가변의 성질을 지니는데요.
그렇다면 불변 객체는 왜 필요할까요?
아래의 코드는 user 객체를 user2 객체로 복사하여 user2의 name 프로퍼티를 변경하였습니다.
var user = {
name: 'Gaeun',
gender: 'female',
};
var user2 = user;
user2.name = 'Jiwon';그러나 아래의 그림과 같이 user 객체와 user2 객체는 같은 내부 프로퍼티를 공유하므로, user2의 이름을 변경할 시 기존 user의 이름도 함께 변경됩니다.

이는 참조형 데이터 객체의 가변성 때문인데요.
💡 이 대목에서 불변 객체의 필요성이 대두됩니다.
1-5-1 불변 객체를 만드는 간단한 방법
데이터의 가변은 앞서 본 예시처럼 내부 프로퍼티를 변경할 때만 성립합니다.
그러므로 불변 객체를 만드는 첫 번째 간단한 방법은, 새로운 데이터를 할당하는 것입니다.
var user = {
name: 'Gaeun',
gender: 'female',
};
var changeName = function (user, newName) {
return {
name: newName,
gender: user.gender,
};
};
var user2 = changeName(user, 'Jiwon');그림과 같이 user 객체와 user2는 서로 다른 데이터를 참조하고 있으므로, 각각의 내부
프로퍼티 변경에 영향을 받지 않게 됩니다. 
그러나 이 방법의 경우 아래와 같이 객체가 가진 프로퍼티가 많으면, 이를 모두 작성해야 한다는 단점을 지닙니다.
var changeName = function (user, newName) {
return {
name: newName,
gender: user.gender,
age: user.age,
address: user.address,
phoneNumber: user.phoneNumber,
.
.
.
};
};모든 프로퍼티를 복사하는 함수 만들기
아래의 함수 copyObject는 모든 프로퍼티를 순회하여 복사하는 함수입니다.
var user = {
name: 'Gaeun',
gender: 'female',
};
var copyObject = function (target) {
var result = {};
for (var prop in target) {
result[prop] = target[prop];
}
return result;
};
var user2 = copyObject(user);
user2.name = 'Jiwon';
console.log(user.name, user2.name); // Gaeun Jiwon
console.log(user === user2); // false그러나 이 함수는 얕은 복사만 수행한다는 한계를 지닙니다.
얕은 복사가 무엇일까요?
1-5-2 얕은 복사와 깊은 복사
다음은 참조형 데이터가 저장된 프로퍼티를 가진 user 객체입니다.
var user = {
name: 'Gaeun',
gender: 'female',
//👇🏻 참조형 데이터가 저장된 프로퍼티
urls: {
github: 'github.com/gaeunnlee',
blog: 'gaeunnlee.tistory.com',
},
};이 user 객체를 복사하는 경우, 얕은 복사와 깊은 복사는 다음과 같이 이루어집니다.
| 얕은 복사 | 깊은 복사 |
|---|---|
| 바로 아래 단계의 값만 복사 | 내부의 모든 값을 복사 |
❗따라서 얕은 복사의 경우, 중첩된 객체에서 참조형 데이터가 저장된 프로퍼티를 복사시 그 주솟값만 복사합니다.
앞서 본 copyObject 함수는 내부의 모든 값까지 복사해야 한다는 점을 간과하였으므로 얕은 복사입니다.
그러므로 copyObject를 통하여 user을 복사한 user2의 경우,
프로퍼티 중 참조형 데이터인 urls의 내부 프로퍼티 github을 변경할 시,
기존 user의 github도 함께 변경됩니다.
var user2 = copyObject(user);
user2.urls.github = 'github.com/user2';
console.log(`user github: ${user.urls.github}`); // user github: github.com/user2
console.log(`user2 github: ${user2.urls.github}`); // user2 github: github.com/user2모든 프로퍼티를 깊게 복사하는 함수 만들기
그렇게 모든 프로퍼티를 깊게 복사하는 함수를 만들면 다음과 같습니다.
copyObjectDeep 함수는 프로퍼티가 참조형 데이터인 경우 copyObjectDeep함수를 재귀적으로 호출하여 깊은 복사를 수행합니다.
var copyObjectDeep = function (target) {
var result = {};
if (typeof target === 'object' && target !== null) {
for (var prop in target) {
result[prop] = copyObjectDeep(target[prop]);
}
} else {
result = target
}
return result;
};JSON을 활용한 깊은 복사
마지막으로, JSON을 활용하여 간단히 깊은 복사를 수행할 수 있는데요.
단, JSON으로 변경할 수 없는 프로퍼티들은 모두 무시하여 복사합니다.
var copyObjectViaJSON = function (target) {
return JSON.parse(JSON.stringify(target))
}아래의 코드에서 참조형 데이터를 지닌 프로퍼티까지 모두 깊은 복사가 알맞게 수행된 것을 확인할 수 있지만,
JSON으로 변경할 수 없는 프로퍼티인 sayHi는 무시하여 복사한 것을 확인할 수 있습니다.
var user = {
name: 'Gaeun',
gender: 'female',
urls: {
github: 'github.com/gaeunnlee',
blog: 'gaeunnlee.tistory.com',
},
//👇🏻 JSON으로 변경할 수 없는 프로퍼티
sayHi: () => {
console.log('hi');
},
};
var copyObjectViaJSON = function (target) {
return JSON.parse(JSON.stringify(target));
};
var user2 = copyObjectViaJSON(user);
user2.name = 'Jiwon';
user2.urls.blog = 'user2.blog.com'
user2.urls.github = 'github.com/user2'
console.log(user);
console.log(user2);