불변 객체 📖
불변 객체란 말 그대로 변하지 않는 객체로 객체가 생성된 후 내부 상태가 변하지 않는 객체를 의미합니다.
1. 불변 객체의 필요성
예시 코드 ✅
// user 객체를 생성
var user = {
name: 'wonjang',
gender: 'male',
};
// 이름을 변경하는 함수, 'changeName'을 정의
// 입력값 : 변경대상 user 객체, 변경하고자 하는 이름
// 출력값 : 새로운 user 객체
// 특징 : 객체의 프로퍼티(속성)에 접근해서 이름을 변경했네요! -> 가변
var changeName = function (user, newName) {
var newUser = user;
newUser.name = newName;
return newUser;
};
// 변경한 user정보를 user2 변수에 할당하겠습니다.
// 가변이기 때문에 user1도 영향을 받게 될거에요.
var user2 = changeName(user, 'twojang');
// 결국 아래 로직은 skip하게 될겁니다.
if (user !== user2) {
console.log('유저 정보가 변경되었습니다.');
}
console.log(user.name, user2.name); // twojang twojang
console.log(user === user2); // true
이 예시코드 19번째 줄에서 유저명을 변경한 후 console.log()를 출력해 보면 `user1`과 `user2`가 같다는 출력이 나오게 됩니다. 결국 가변성 때문에 문제가 생기는데요,, 이를 개선할 수 있는 방법이 있습니다.
//19번째 줄
var user2 = changeName(user, 'twojang');
console.log(user.name, user2.name); // twojang twojang
console.log(user === user2); // true
개선 코드 ☑️
// user 객체를 생성
var user = {
name: 'wonjang',
gender: 'male',
};
// 이름을 변경하는 함수 정의
// 입력값 : 변경대상 user 객체, 변경하고자 하는 이름
// 출력값 : 새로운 user 객체
// 특징 : 객체의 프로퍼티에 접근하는 것이 아니라, 아에 새로운 객체를 반환 -> 불변
var changeName = function (user, newName) {
return {
name: newName,
gender: user.gender,
};
};
// 변경한 user정보를 user2 변수에 할당하겠습니다.
// 불변이기 때문에 user1은 영향이 없어요!
var user2 = changeName(user, 'twojang');
// 결국 아래 로직이 수행되겠네요.
if (user !== user2) {
console.log('유저 정보가 변경되었습니다.');
}
console.log(user.name, user2.name); // wonjang twojang
console.log(user === user2); // false 👍
`changeName`함수 내부에서 새로운 객체를 생성해 리턴하기 때문에 불변성을 유지할 수 있게 됩니다. 그런데 이 방법에도 문제점이 있습니다.
문제점 🤔
`changeName`함수 내부에 프로퍼티(속성)가 100개라면? => 하드코딩의 문제
그래서 이러한 문제를 해결할 수 있는 방법으로 `얕은 복사`에 대해서 설명해보려고 합니다. (완벽하지 않음)
얕은 복사 📖
데이터 자체를 복사한 것이 아니라 해당 데이터의 참조 값을 전달하여 하나의 데이터를 공유하는 것이다.
패턴과 적용
// 패턴
var copyObject = function (target) {
var result = {};
// for ~ in 구문을 이용하여, 객체의 모든 프로퍼티에 접근할 수 있습니다.
// 하드코딩을 하지 않아도 괜찮아요.
// 이 copyObject로 복사를 한 다음, 복사를 완료한 객체의 프로퍼티를 변경하면
// 되겠죠!?
for (var prop in target) {
result[prop] = target[prop];
}
return result;
}
// 위 패턴을 예제에 적용
var user = {
name: 'wonjang',
gender: 'male',
};
var user2 = copyObject(user);
user2.name = 'twojang';
if (user !== user2) {
console.log('유저 정보가 변경되었습니다.');
}
console.log(user.name, user2.name);
console.log(user === user2);
`copyObject`함수를 이용해 `user`를 복사하면 `user`와 `user2`는 별도의 object가 될 수 있습니다. 그런데 얕은 복사 패턴
도 중첩된 객체에 대해서는 완벽한 복사를 할 수 없습니다.
중첩된 객체에 대한 얕은 복사
var user = {
name: 'wonjang',
urls: {
portfolio: 'http://github.com/abc',
blog: 'http://blog.com',
facebook: 'http://facebook.com/abc',
}
};
var user2 = copyObject(user);
user2.name = 'twojang';
// 바로 아래 단계에 대해서는 불변성을 유지하기 때문에 값이 달라지죠.
console.log(user.name === user2.name); // false
// 더 깊은 단계에 대해서는 불변성을 유지하지 못하기 때문에 값이 같아요.
// 더 혼란스러워 지는거죠 ㅠㅠ
user.urls.portfolio = 'http://portfolio.com';
console.log(user.urls.portfolio === user2.urls.portfolio); // true
// 아래 예도 똑같아요.
user2.urls.blog = '';
console.log(user.urls.blog === user2.urls.blog); // true
중첩된 객체에서 `user`의 urls를 변경하게 되면 `user2`의 urls도 똑같이 변경이 되기 때문에 결국 `user.urls`도 불변 객체로 만들어야 됩니다.
깊은 복사 📖
얕은 복사처럼 참조 주소값이 복사 되는 것이 아닌, 값만 복사되는 것이다. 깊은 복사는 원본과의 참조가 완전히 끊어진 객체입니다.
예제 코드 ✅
var user = {
name: 'wonjang',
urls: {
portfolio: 'http://github.com/abc',
blog: 'http://blog.com',
facebook: 'http://facebook.com/abc',
}
};
// 1차 copy
var user2 = copyObject(user);
// 2차 copy -> 이렇게까지 해줘야만 해요..!!
user2.urls = copyObject(user.urls);
user.urls.portfolio = 'http://portfolio.com';
console.log(user.urls.portfolio === user2.urls.portfolio);
user2.urls.blog = '';
console.log(user.urls.blog === user2.urls.blog);
중첩된 객체를 복사하는 방법으로 `user`에 대해서 복사를 진행한 후 `user.urls`도 복사를 해주면 문제를 해결할 수 있는 거 아니냐?라고 생각하실 수 있지만 만약 중첩이 여러 번 되어 있다면 문제가 또 발생하게 됩니다.
해결 방법 ✨
1. 재귀적으로 수행하게 되면 문제를 해결할 수 있습니다.
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;
}
//결과 확인
var obj = {
a: 1,
b: {
c: null,
d: [1, 2],
}
};
var obj2 = copyObjectDeep(obj);
obj2.a = 3;
obj2.b.c = 4;
obj2.b.d[1] = 3;
console.log(obj);
console.log(obj2);
재귀적으로 수행하면서 객체 안에 깊은 곳까지 복사를 수행하게 됩니다.
장단점 ✅
얕은 복사
얕은 복사의 장점은 메모리를 적게 사용하고, 복사 과정이 간단하다는 점입니다. 이를 통해 성능이 좋고, 객체의 상태를 부분적으로 복제할 수 있습니다. 그러나 얕은 복사는 객체의 상태를 완전히 복제할 수 없기 때문에 안전하지 않을 수 있습니다.
깊은 복사
깊은 복사의 장점은 객체의 상태를 완전히 복제할 수 있다는 점입니다. 이를 통해 원본 객체와 복사된 객체가 독립적으로 동작할 수 있습니다. 그러나 깊은 복사는 메모리를 많이 사용하고, 복사 과정이 복잡하기 때문에 성능이 저하될 수 있습니다.
감사합니다~!
출처 🏷️
'공부 > javascript' 카테고리의 다른 글
javascript 심화 5 (Promise, async, await) (0) | 2024.07.29 |
---|---|
javascript 심화 4 (콜백함수) (0) | 2024.07.29 |
javascript 심화 3 (this, 화살표함수, call, apply, bind) (0) | 2024.07.26 |
javascript 심화 2 (호이스팅, var let const 차이점, TDZ) (5) | 2024.07.24 |
javascript 심화 1 (실행 컨텍스트) (0) | 2024.07.24 |
javascript 기본 3 (기본형, 참조형 데이터와 불변성) (1) | 2024.07.23 |
Javascript 기본 2 (조건문, 반복문, 배열, 객체) (2) | 2024.07.22 |
Javascript 기본 1 (기본 문법) (0) | 2024.07.22 |