Notice
Recent Posts
Recent Comments
Link
250x250
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
Tags
- 프로그래머스
- 스레드
- App Runner
- 해시
- javascript
- ip
- 리액트
- 타입스크립트
- 프로세스
- 프로젝트캠프
- 웅진씽크빅
- 자바스크립트
- react
- Algorithm
- CS
- 스나이퍼팩토리
- 인사이드아웃
- 네트워크
- IT개발캠프
- #프로젝트캠프 #프로젝트캠프후기 #유데미 #스나이퍼팩토리 #웅진씽크빅 #인사이드아웃 #IT개발캠프 #개발자부트캠프 #리액트 #react #부트캠프 #리액트캠프
- BFS
- typescript
- React.js
- html
- react-query
- 메모리
- 개발자부트캠프
- 알고리즘
- 유데미
- cs #네트워크
Archives
- Today
- Total
Bin's Blog
불변 객체 본문
728x90
1. 불변 객체란
- 이전에 참조형 데이터에 대해서 살펴봤을 때, 참조형 데이터의 가변은 데이터 자체가 아닌 내부 프로퍼티를 변경할 때만 성립한다. 데이터 자체를 변경하려고 하면 기본형 데이터와 마찬가지로 기존 데이터는 변하지 않는다.
- 불변성을 확보할 필요가 있을 경우에 불변 객체를 취급하고, 그렇지 않은 경우에 기존 방식대로 사용하는 식으로 상황에 따라 대처한다.
- 불변 객체가 필요한 경우는 값으로 전달받은 객체에 변경을 가하더라도 원본 객체는 변하지 않아야 하는 경우가 종종 발생한다.
- 객체의 가변성에 따른 문제점
let user = {
name: "Jaenam",
gender: "male"
};
let changeName = (user, newName) => {
let newUser = user;
newUser.name = newName;
return newUser;
};
let user2 = changeName(user, "Lee");
if (user !== user2) {
console.log("유저 정보가 변경되었다");
}
// Lee, Lee
console.log(user.name, user2.name);
// true
console.log(user === user2);
- 위에 코드를 보면 user2.name만 바꿨는데 user.name도 바뀐 것을 볼 수 있다.
- 해결 방법
let user = {
name: "Jaenam",
gender: "male"
};
let changeName = (user, newName) => {
return {
name : newName,
gender: user.gender
};
};
let user2 = changeName(user, "Lee");
// 유저 정보가 변경되었다.
if (user !== user2) {
console.log("유저 정보가 변경되었다");
}
// Jaenam , Lee
console.log(user.name, user2.name);
// false
console.log(user === user2);
- changeName 함수가 새로운 객체를 반환하도록 수정해서 이제 user와 user2는 서로 다른 객체이다.
- 그러나 ChangeName 함수는 새로운 객체를 만들면서 gender를 변경할 필요가 없는데 하드코딩으로 입력했다. 이런 식으로 하게 되면 대상 객체에 정보가 많을수록, 변경해야 할 정보가 많을수록 사용자가 입력하는 수고가 늘어난다.
- 그래서 프로퍼티 개수와 상관 없이 모든 프로퍼티를 복사하는 함수를 만드는 것이 좋을 것 같다.
- 얕은 복사(기존 정보를 복사해 새로운 객체를 반환하는 함수)
let copyObject = (target) => {
let result = {};
for (let prop in target) {
result[prop] = target[prop];
}
return result;
};
let user = {
name: "Jaenam",
gender: "male"
};
let user2 = copyObject(user);
user2.name = "Jung";
// 유저 정보가 변경되었다.
if (user !== user2) {
console.log("유저 정보가 변경되었다");
}
// Jaenam , Lee
console.log(user.name, user2.name);
// false
console.log(user === user2);
1-1. 얕은 복사와 깊은 복사
- 얕은 복사는 바로 아래 단계의 값만 복사하는 방법이고, 깊은 복사는 내부의 모든 값들을 하나하나 찾아서 전부 복사하는 방법이다.
- 위에 얕은 복사 코드에서 봤듯이 중첩된 객체에서 참조형 데이터가 저장된 프로퍼티를 복사할 때 그 주솟값만 복사한다는 뜻이다.
- 그러면 얕은 복사의 경우 해당 프로퍼티에 대해서 원본과 사본이 모두 동일한 참조형 데이터의 주소를 가리키게 되고 사본을 바꾸면 원본도 바뀌고 원본도 바뀌면 사본도 바뀐다.
- 예시
let copyObject = (target) => {
let result = {};
for (let prop in target) {
result[prop] = target[prop];
}
return result;
};
let user = {
name: "Jaenam",
urls: {
portfolio: "http://github.com/abc",
blog: "http://blog.com",
facebook: "http://facebook.com/abc"
}
};
let user2 = copyObject(user);
let user2 = copyObject(user);
user2.name = "Lee";
// false
console.log(user.name === user2.name);
user.urls.portfolio = "http://portfolio.com";
// true
console.log(user.urls.portfolio === user2.urls.portfolio);
user2.urls.blog = "";
// true
console.log(user.urls.blog === user2.urls.blog)
- 위에 봤듯이 user의 정보를 바꿨더니 user2의 정보도 따라서 바뀌고 user2의 정보를 바꿨더니 user의 정보도 바뀌었다.
- user객체에 직접 속한 프로퍼티에 대해서는 복사해서 완전히 새로운 데이터가 만들어진 반면에 한 단계 더 들어간 urls의 내부 프로퍼티들은 기존 데이터를 그대로 참조한다.
- 이런 현상을 방지하려면 user.urls 프로퍼티에 대해서도 불변 객체로 만들 필요가 있다.
- 중첩된 객체에 대한 깊은 복사
let user2 = copyObject(user);
user2.urls = copyObject(user.urls);
user.urls.portfolio = "http://portfolio.com";
// false
console.log(user.urls.portfolio === user2.urls.portfolio);
user2.urls.blog = "";
// false
console.log(user.urls.blog === user2.urls.blog);
- 객체의 깊은 복사를 수행하는 범용 함수
let copyObjectDeep = (target) => {
let result = {};
if (typeof target === "object" && target !== null) {
for (let prop in target) {
result[prop] = copyOjbectDeep(target[prop]);
}
} else {
result = target;
}
return result;
};
- 위에서 재귀적으로 copyObjectDeep 함수를 호출해서 속성값을 복사하고, 복사된 값을 result[prop]에 저장된다. 이 과정에서 객체의 중첩된 속성까지 깊이 복사가 이루어진다.
- JSON을 활용한 간단한 깊은 복사
let copyOjbectViaJSON = (target) => {
return JSON.parse(JSON.stringify(target));
};
let obj = {
a: 1,
b: {
c: null,
d: [1, 2],
func1: function () { console.log(1);}
},
func2: function () { console.log(4); }
};
}
let obj2 = copyObjectViaJSON(obj);
obj2. a = 3;
obj2.b.c = 4;
objb.d[1] = 3;
// { a : 1, b: { c:null, d: [1, 3], func1: f()}, func2: f()}
console.log(obj);
// { a : 3, b: { c: 4, d: [1, 2]}
console.log(obj2);
- 위의 방식은 객체를 JSON 문법으로 표현된 문자열로 전환했다가 다시 JSON 객체로 바꾸는 것이다. 그러나 이 메서드는 함수, 심볼, 순환 참조 등을 변환하지 않는다. 따라서 변환과정에서 함수와 같은 정보는 손실돼서 위와 같이 obj2를 출력하면 func이 사라진다.
- JSON으로 변경할 수 없는 프로퍼티들은 모두 무시한다. httpRequest로 받은 데이터를 저장한 객체를 복사할 때 등 순수한 정보만 다룰 때 활용하기 좋은 방법이다.
728x90
'JavaScript' 카테고리의 다른 글
undefined와 null (0) | 2023.05.05 |
---|---|
Map() 객체 (0) | 2023.05.04 |
JavaScript Array.map() 함수 (0) | 2023.05.02 |
불변값과 가변값 (0) | 2023.05.01 |
데이터 타입이란? (0) | 2023.04.25 |