티스토리 뷰
얕은 복사는 메모리 주소값을 복사
하는 개념이고, 깊은 복사는 새로운 메모리 공간을 확보해 완전히 복사
하는 개념이다. 얕은 복사와 깊은 복사는 데이터 저장 방식과 깊은 관련이 있다.
데이터 저장 방식
let A = 컵;
let C = 우산;
let D = 우산;
let B = 컵 + 우산;
변수 선언이 일어나면 컴퓨터의 메모리에서 비어있는 공간 하나를 확보한다. 그 공간에 이름을 지정하고, 데이터를 또 다른 공간에 저장한 후, 데이터가 담긴 공간 정보를 저장한다. 다소 복잡해보이는데 왜 이렇게 할까?
- 데이터 변환을 자유롭게
- 메모리 관리를 더욱 효율적으로 하기 위해서
다음과 같이 사물함에 물건을 저장하는 그림에 비유해보았다. (실제 변수저장 메커니즘은 변수영역과 데이터영역, 메모리주소, 식별자와 값 등의 개념이 있다.)
- 데이터 변환을 자유롭게
만약 사물함에 주소 정보를 저장하는 것이 아닌, 물건을 직접 저장한다고 한다면 A에처음에 컵을 담았다가 우산으로 바꾸려고 할 때
, 크기가 작아서 안들어갈 것이다. A사물함 크기를 직접 늘리는 작업이 필요하고 만약 인접 사물함에 물건이 담겨있다면 밀리거나 변형되는 등의 영향을 끼칠 것이다. 아예 새로 다른 칸에 담는 방식도 생각할 수 있는데 이는 이어질 2번의 메모리 관리의 측면에서 비효율적이다. 실제 방식은 다른 칸 O에 우산을 담고'I로 가세요'를 'O로 가세요'로 주소 정보를 수정
하여 A는 컵에서 우산으로 변경된다. - 메모리 관리를 더욱 효율적으로 하기 위해서
데이터를 직접 저장하는 방식은 C, D, E, F, ... 등무수한 500개의 변수에 '우산'이라는 동일한 값을 넣어야 하는 경우
에 500개의 큰 사물함에 우산을 각각 다 담아야 할 것이다. 공간이 낭비된다. 실제 방식은 O사물함에 우산을 담고 500개의 변수들이 O사물함을가리킨다
. 우산처럼 큰 사물함이 아닌 주소 정보만 담을 수 있는 작은 사물함들을 사용해도 같은 결과를 가질 수 있다.
또는, B에컵 + 우산을 저장하려고 할 때
이미 컵과 우산이 들어있는 칸이 있는데 별도로 큰 사물함에 컵과 우산을 함께 담아서 생성해줘야 할 것이다. 실제 방식은 별도 생성할 필요없이 컵과 우산이 들어있는 칸 모두의주소
를 저장한다.
원시자료형과 참조자료형의 저장
let a = 'hi';
a = 'hello';
위의 그림은 해당 코드의 과정을 그린 것이다. 원시자료형의 값 @5002
또는 @5003
을 보니 값이 담겨있다.
const obj = {
a: 'hi',
b: 10
};
obj.a = 'hello';
참조자료형은 값 @5004
를 보니 또 다른 곳 @7102
와 @7103
을 참조하고 있다.
얕은 복사
얕은 복사는 한 단계 아래까지만 복사하는 것이다. 위 원시자료형과 참조자료형의 저장에서 a의 값(@5002
@5003
) 또는 obj의 값(@5004
)을 복사한다.
// 원시자료형의 얕은 복사
let a = 'hi';
let b = a;
a = 'hello';
console.log(a, b); // 'hello' 'hi'
원시자료형의 경우,
- b가 a의 값
@5002
를 복사하여 가진다. - a는 값을
@5003
으로 교체한다.
a와 b가 복사 한 후에 독립적이다. ctrl+C
ctrl+V
한 것 같은 결과이다.
// 참조자료형의 얕은 복사
const obj1 = {
a: 'hi',
b: 10
}
const obj2 = obj1;
obj2.a = 'hello';
console.log(obj1.a, obj2.a); // 'hello' 'hello'
참조자료형의 경우,
- obj2는 obj1의 값
@5004
를 복사하여 가진다. - obj2.a의 값
@5002
를@5003
으로 교체한다. - 여전히 obj1과 obj2의 값은
@5004
이고 데이터@7102~?
를 가지고(참조하고) 있다.
복사본의 값을 바꾸면 원본의 값도 같이 바뀐다. obj2가 복사한 것은 값이라기보다는 @7102~@7103을 참조하고있음
을 복사했다. 같은 것을 참조하게 되었기 때문에 복사보다는 공유라고 볼 수 있다. obj2.a를 바꾸었는데 이들은 공유되고 있기 때문에 obj1.a도 같이 바뀐다.
= (할당, assign)
let a = 100;
let b = a; // 얕은 복사
a = 50;
console.log(a===b); // false
const obj1 = {
c: 'c',
d: 'd'
}
const obj2 = obj1; // 얕은 복사
obj2.c = 'zzz';
console.log(obj1===obj2); // true
참조자료형의 경우 원본이 같이 변경된다.
Array.slice()
const arr1 = [ 1, 2, 3, [4, 5] ];
const arr2 = arr1.slice();
arr2[0] = 0;
arr2[3][0] = 0;
console.log(arr1); // [ 1, 2, 3, [0, 5] ]
console.log(arr2); // [ 0, 2, 3, [0, 5] ]
1단계 레벨까지는 잘 복사가 되지만 중첩된 구조에서는 원본이 영향을 받는다.
Object.assign()
const obj1 = {
a: 0,
b: 0,
c: {
d: 0
}
};
const obj2 = Object.assign({}, obj1);
obj2.a = 1;
console.log(obj1); // { a: 0, b: 0, c: { d: 0 } }
console.log(obj2); // { a: 1, b: 0, c: { d: 0 } }
obj2.c.d = 1;
console.log(obj1); // { a: 0, b: 0, c: { d: 1 } }
console.log(obj2); // { a: 1, b: 0, c: { d: 1 } }
복사한 객체 자체는 깊은 복사가 되지만, 내부의 객체에 대해서는 얕은 복사가 된다. 다음의 ...(전개구문)과 동일하게 1레벨 깊이에서만 효과적인 복사 방법이다.
... (전개구문, spread operator)
const arr1 = [ 1, 2, [ 0 ] ];
const arr2 = [ ...arr1 ];
arr2[0] = 0;
console.log(arr1); // [ 1, 2, [ 0 ] ]
consolt.log(arr2); // [ 0, 2, [ 0 ] ]
arr2[2].push(1);
console.log(arr1); // [ 1, 2, [ 0, 1 ] ]
consolt.log(arr2); // [ 0, 2, [ 0, 1 ] ]
Array.slice()와 다른 결과가 나왔다. Object.assign()과 동일하게 1레벨에서만 효과적으로 복사할 수 있다.
깊은 복사
복사를 하는 것은 기존의 값과 독립적으로 영향이 없도록 하기 위함일 것이다. 객체가 얕은 복사로도 복사의 기능을 할 수 있다면 문제가 없을 것이다. 그러나 JavaScript가 이런 원리로 작동하므로 이곳저곳에서 원본데이터를 동시에 변형하여 프로그램이 의도한대로 동작하지 않을 수 있다. 깊은 복사를 해주어야 한다.
재귀함수 구현 (recursive function)
function isCopyObj(origin) {
let result = {};
for (let key in origin) {
if (typeof origin[key] === 'object') {
result[key] = isCopyObj(obj[key]);
} else {
result[key] = origin[key];
}
}
return result;
}
const obj1 = {
a: 0,
b: 0,
c: {
d: 0
}
};
const obj2 = isCopyObj(obj1);
위 함수 isCopyObj()로 깊은 복사를 할 수 있다. 서로 참조관계가 없는 다른 객체이다.
JSON객체 이용
const obj1 = {
a: 0,
b: 0,
c: {
d: 0
}
};
const obj2 = JSON.parse(JSON.stringify(obj));
JSON객체의 copyTarget = JSON.parse(JSON.stringify(target))
를 이용한다. obj1과 obj2는 서로 참조관계가 없는 다른 객체이다.
이전 블로그(velog.io/@hahagarden)에서 이전해온 글입니다.
'개발 > JS, TS, React' 카테고리의 다른 글
JS var, let, const (0) | 2023.03.07 |
---|---|
JS 스코프, 그 규칙과 종류 (0) | 2023.03.07 |
JS 객체의 생성, 프로퍼티, 내장메서드 (0) | 2023.03.07 |
JS Number, parseInt, parseFloat 비교 (0) | 2023.03.07 |
JS 반복문 예제 수도코드 작성 연습 (0) | 2023.03.06 |
- Total
- Today
- Yesterday
- 춘천닭갈비
- 구글서치콘솔
- solo project
- 신불당 술집
- 이수 맛집
- Til
- 태릉 꼬치
- 티스토리
- 공릉맛집
- 춘천맛집
- 롯데월드 보조배터리
- 롯데월드 매직패스 프리미엄
- 공릉 꼬치
- 공릉 이자카야
- 티스토리검색
- sitemap
- 깃허브 데스크탑 로그아웃
- 태릉삼겹살
- 공릉 술집
- 공릉 카페
- 회고
- 을지로맛집
- 태릉 술집
- 롯데월드 키오스크
- 홍천 삼겹살
- 공릉 맛집
- 맥 깃허브 데스크탑
- 태릉 이자카야
- 공릉 밀크티
- 태릉맛집
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |