일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- revalidatepath
- tanstack query
- Next.js
- Form
- 리액트
- 자바스크립트
- 프로그래머스
- Supabase
- 스택
- 토이프로젝트
- 리덕스
- React
- 그리디
- 코어자바스크립트
- TypeScript
- 코테
- tailwind
- styled component
- 동적계획법
- react pattern
- 프론트엔드
- JavaScript
- 토이 프로젝트
- 리액트 라우터 돔
- react router dom
- reduxtoolkit
- 타입스크립트
- 코딩테스트
- 리액트 패턴
- React Query
- Today
- Total
느려도 한걸음씩
1.5.2 불변 객체 - 얕은 복사와 깊은 복사 본문
얕은 복사(shallow copy)는 바로 아래 단계의 값만 복사하는 방법이고, 깊은 복사(deep copy)는 내부의 모든 값들을 하나하나 찾아서 전부 복사하는 방법이다.
앞에서 작성했던 copyObject 함수는 얕은 복사만 수행했다. 이 말은 중첩된 객체에서 참조형 데이터가 저장된 프로퍼티를 복사할 때는 그 주소값만 복사한다는 의미이다 그러면 해당 프로퍼티에 대해 원본과 사본이 모두 동일한 참조형 데이터의 주소를 가리키게 된다 사본을 바꾸면 원본도 바뀌고, 원본도 바꾸면 사본도 바뀌게 된다
var user = {
name : 'Jaenam',
urls: {
portfolio : 'http://github.com/abc',
blog: 'http://blog.com',
facebook: 'http://facebook.com/abc'
}
}
var copyObject = function (target) {
var result = {}
for(var prop in target) {
result[prop] = target[prop]
}
return result
}
var user2 = copyObject(user)
user2.name = 'Jung'
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
user2의 name 프로퍼티를 바꿔도 user의 name 프로퍼티는 바뀌지 않았다 반면 urls 객체에 대해서는 원본과 사본 중 어느 쪽을 바꾸더라도 다른 한쪽의 값도 함꼐 바뀐 것을 확인할 수 있다 즉 user 객체에 직접 속한 프로퍼티에 대해서는 복사해서 완전히 새로운 데이터가 만들어진 반면, 한 단계 더들어간 urls의 내부 프로퍼티들은 기존 데이터를 그대로 참조하는 것이다. 이런 현상이 발생하지 않게 하려면 user.urls 프로퍼티에 대해서도 불변 객체로 만들 필요가 있다.
var user = {
name : 'Jaenam',
urls: {
portfolio : 'http://github.com/abc',
blog: 'http://blog.com',
facebook: 'http://facebook.com/abc'
}
}
var copyObject = function (target) {
var result = {}
for(var prop in target) {
result[prop] = target[prop]
}
return result
}
var user2 = copyObject(user)
user2.urls = copyObject(user.urls) // urls 객체에 대해서도 copyObject 함수 적용
user2.name = 'Jung'
console.log(user.name === user2.name) // false
user.urls.portfolio = 'http://portfolio.com';
console.log(user.urls.portfolio === user2.urls.portfolio) // false
user2.urls.blog = ''
console.log(user.urls.blog === user2.urls.blog) // false
urls 프로퍼티에 copyObject 함수를 실행한 결과를 할당했다 이제 urls 프로퍼티의 내부까지 복사해서 새로운 데이터가 만들어 졌으므로 5번째 줄과 8번째 줄에서 서로 다르다는 결과를 얻을 수 있다
정리하자면 어떤 객체를 복사할 때 객체 내부의 모든 값을 복사해서 완전히 새로운 데이터를 만들고자 할 때, 객체의 프로퍼티 중에서 그 값이 기본형 데이터일 경우에는 그대로 복사하면 되지만 참조형 데이터는 다시 그 내부의 프로퍼티들을 복사해야 한다. 이 과정을 참조형 데이터가 있을 때 마다 재귀적으로 수행해야만 비로소 깊은 복사가 되는 것이다
이 개념을 바탕으로 copyObject 함수를 깊은 복사 방식으로 고쳐보자.
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
}
target이 객체인 경우에는 내부 프로퍼티들을 순회하며 copyObjectDeep 함수를 재귀적으로 호출하고 객체가 아닌 경우에는 target을 그대로 지정하게끔 했다 이 함수를 사용해 객체를 복사한 다음에는 원본과 사본이 서로 완전히 다른 객체를 참조하게 되어 어느 쪽의 프로퍼티를 변경하더라도 다른 쪽에 영향을 주지 않는다
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) // { a: 1, b: { c: null, d: [ 1, 2 ] } }
console.log(obj2) // { a: 3, b: { c: 4, d: { '0': 1, '1': 3 } } }
추가로 hasOwnProperty 메서드를 활용해 프로토타입 체이닝을 통해 상속된 프로퍼티를 복사하지 않게끔 할 수도 있다 ES5의 getter/setter를 복사하는 방법은 안타깝게도 ES6의
Object.getOwnPropertyDescriptor 또는 Object.getOwnPropertyDescriptors
외에는 마땅한 방법이 없다
끝으로 간단하게 깊은 복사를 처리할 수 있는 방법이 하나 더있다 원리도 단순한데 객체를 JSON 문법으로 표현된 문자열로 전환했다가 다시 JSON 객체로 바꾸는것이다.
이 방법은 단순함에도 볼구하고 잘 동작한다. 다만 메서드(함수)나 숨겨진 프로퍼티인 __proto__나 getter/setter 등과 같이 JSON으로 변경할 수 없는 프로퍼티들은 모두 무시한다. httpRequest로 받은 데이터를 저장한 객체를 복사할 때 등 순수한 정보만 다룰 때 활용하기 좋은 방법이다.
var copyObjectViaJSON = function (target) {
return JSON.parse(JSON.stringify(target))
};
var obj = {
a : 1,
b : {
c : null,
d : [1, 2],
fuc1 : function () {
console.log(3)
},
fu2 : function () {
console.log(4)
}
}
}
var obj2 = copyObjectViaJSON(obj)
obj2.a = 3;
obj2.b.c = 4;
obj.b.d[1] = 3;
console.log(obj) // { a: 1, b: { c: null,d: [ 1, 3 ],fuc1: [Function: fuc1], fu2: [Function: fu2] }}
console.log(obj2) // { a: 3, b: { c: 4, d: [ 1, 2 ] } } (함수는 모두 무시됨)
'개발공부 > 코어자바스크립트' 카테고리의 다른 글
2 실행 컨텍스트란? (0) | 2024.10.15 |
---|---|
1.6 undefined와 null (0) | 2024.10.14 |
1.5.1 불변 객체 - 불변 객체를 만드는 간단한 방법 (2) | 2024.10.14 |
1.4.3 기본형 데이터와 참조형 데이터 - 변수 복사 비교 (0) | 2024.10.12 |
1.4.1 기본형 데이터와 참조형 데이터 - 불변값 (1) | 2024.10.09 |