Search
Duplicate

깊은 복사, 얉은 복사

생성일
2023/02/08 03:26
태그
JS

깊은 복사, 얉은 복사

얉은 복사 - 객체의 참조값(주소값)을 복사
깊은 복사 - 객체의 실제값을 복사

원시값

기본 자료형(단순한 데이터)을 의미
Number, String, Boolean, Null, Undefined 등이 해당
변수에 원시값을 저장하면 변수의 메모리 공간에 실제 데이터 값이 저장된다
할당된 변수를 조작하려고 하면 저장된 실제 값이 조작된다

참조값

여러 자료형으로 구성되는 메모리에 저장된 객체
Object, Symbol 등 해당
변수에 객체를 저장하면 독립적인 메모리 공간에 값을 저장하고, 변수에 저장된 메모리 공간의 참조(위치 값)를 저장하게 된다
그래서 할당된 변수를 조작하는 것은 사실 객체 자체를 조작하는 것이 아니라, 해당 객체의 참조를 조작하는 것이다
원시값을 복사할 때, 그 값은 또 다른 독립적인 메모리 공간에 할당하기 때문에, 복사를 하고 값을 수정해도 기존 원시값을 저장한 변수에는 영향을 끼치지 않는다.
이처럼 실제 값을 복사하는 것을 깊은 복사 라고 한다.
하지만 이것은 자료형을 깊은 복사한 것이다
const a = 'a'; let b = 'b'; b = 'c'; console.log(a); // 'a'; console.log(b); // 'c'; // 기존값에 영향을 끼치지 않는다
JavaScript
복사
참조값을 복사할 때는 변수가 객체의 참조를 가리키고 있기 때문에, 복사된 변수 또한 객체가 저장된 메모리 공간의 참조를 가리키고 있다.
그래서 복사를 하고 객체를 수정하면 두 변수는 똑같은 참조를 가리키고 있기 때문에, 기존 객체를 저장한 변수에 영향을 끼친다.
이처럼 객체의 참조값(주소값)을 복사하는 것을 얉은 복사라고 한다.
const a = { one: 1, two: 2, }; let b = a; b.one = 3; console.log(a); // { one: 3, two: 2 } 출력 console.log(b); // { one: 3, two: 2 } 출력 // 기존값에 영향을 끼친다
JavaScript
복사

얇은 복사 방법

얉은 복사 : 객체를 복사할 때 기존값과 복사된 갓ㅂ이 같은 참조를 가리키고 있는 것을 말한다. 객체 안에 객체가 있을 경우, 한 개의 객체라도 기존 변수의 객체를 참조하고 있다면, 이를 얉은 복사라고 한다

Array.prototype.slice()

얉은 복사의 대표적인 예
start부터 end 인덱스까지 기존 배열에서 추출하여 새로운 배열을 리턴하는 메소드
만약 start와 end를 설정하지 않는다면, 기존 배열을 전체 얉은 복사한다
const original = ['a', 2, true, 4, "hi"]; const copy = original.slice(); console.log(JSON.stringify(original) === JSON.stringify(copy)); // true copy.push(10); console.log(JSON.stringify(original) === JSON.stringify(copy)); // false console.log(original); // ['a', 2, true, 4, 'hi'] console.log(copy); // ['a', 2, true, 4, 'hi', 10]
JavaScript
복사
→ 기존 배열에 영향을 끼치지 않아서, 깊은 복사로 보일 수 있지만, 원시값을 저장한 1차원 배열일 뿐
원시값은 기본적으로 깊은 복사이다
→ slice() 메소드는 기본적으로 얉은 복사를 수행한다
const original = [ [1, 1, 1, 1], [0, 0, 0, 0], [2, 2, 2, 2], [3, 3, 3, 3], ]; const copy = original.slice(); console.log(JSON.stringify(original) === JSON.stringify(copy)); // true // 복사된 배열에만 변경과 추가. copy[0][0] = 99; copy[2].push(98); console.log(JSON.stringify(original) === JSON.stringify(copy)); // true console.log(original); // [ [ 99, 1, 1, 1 ], [ 0, 0, 0, 0 ], [ 2, 2, 2, 2, 98 ], [ 3, 3, 3, 3 ] ]출력 console.log(copy); // [ [ 99, 1, 1, 1 ], [ 0, 0, 0, 0 ], [ 2, 2, 2, 2, 98 ], [ 3, 3, 3, 3 ] ]출력
JavaScript
복사
→ 만약 1차원 배열이 아닌 중첩 구조를 갖는 2차원 배열이면, 얉은 복사를 수행하게 된다
const original = [ { a: 1, b: 2, }, true, ]; const copy = original.slice(); console.log(JSON.stringify(original) === JSON.stringify(copy)); // true // 복사된 배열에만 변경 copy[0].a = 99; copy[1] = false; console.log(JSON.stringify(original) === JSON.stringify(copy)); // false console.log(original); // [ { a: 99, b: 2}, true ] console.log(copy); // [ { a: 99, b: 2}, false ]
JavaScript
복사
→ 배열 안에 객체를 수정하고자 할 경우, 얉은 복사를 수행
→ 하지만 원시값은 기본적으로 깊은 복사라 기존 변수에 있는 값과는 다른 값을 도출하는 것을 볼 수 있다

Object.assign()

Object.assign(생성할 객체, 복사할 객체)
메소드의 첫번째 인자로 빈 객체를 넣어주고, 두번째 인자로 복사할 객체를 넣어주면 된다
const object = { a: "a", number: { one: 1, two: 2, }, }; const copy = Object.assign({}, object); copy.number.one = 3; console.log(object === copy); // false console.log(object.number.one === copy.number.one); // true
JavaScript
복사
→ 복사된 객체 copy 자체는 기존 object와 다른 객체지만, 그 안에 들어가 있는 값은 기존 object안의 값과 같은 참조 값을 가리키고 있다
Object.assign()은 속성의 값을 복사하기 때문에, 다른 대안을 사용해야한다. 출처 값이 객체에 대한 참조인 경우, 참조값만을 복사한다

spread 연산자 (전개연산자)

→ 얉은 복사
const object = { a: "a", number: { one: 1, two: 2, }, }; const copy = {...object} copy.number.one = 3; console.log(object === copy); // false console.log(object.number.one === copy.number.one); // true
JavaScript
복사

깊은 복사 방법

깊은복사 : 객체는 객체 안에 객체가 있을 경우에도 원본과의 참조가 완전히 끊어진 객체를 말한다

JSON.stringify, JSON.parse

JSON.stringify()는 객체를 json 문자열로 변환하는데, 이 과정에서 원본 객체와의 참조가 모두 끊어진다
객체를 json 문자열로 변환 후, JSON.parse()를 이용해 다시 원래 객체(자바스크립트 객체)로 만들어준다
이 방법이 가장 간단하고 쉽지만, 다른 방법에 비해 느리다라른 것과 객체가 function일 경우, undefined로 처리해야한다는 단점이 있다
const object = { a: "a", number: { one: 1, two: 2, }, arr: [1, 2, [3, 4]], }; const copy = JSON.parse(JSON.stringify(object)); copy.number.one = 3; copy.arr[2].push(5); console.log(object === copy); // false console.log(object.number.one === copy.number.one); // fasle console.log(object.arr === copy.arr); // false console.log(object); // { a: 'a', number: { one: 1, two: 2 }, arr: [ 1, 2, [ 3, 4 ] ] } console.log(copy); // { a: 'a', number: { one: 3, two: 2 }, arr: [ 1, 2, [ 3, 4, 5 ] ] }
JavaScript
복사

재귀 함수를 구현한 복사

→ 복잡하다는 단점
const object = { a: "a", number: { one: 1, two: 2, }, arr: [1, 2, [3, 4]], }; function deepCopy(object) { if (object === null || typeof object !== "object") { return object; } // 객체인지 배열인지 판단 const copy = Array.isArray(object) ? [] : {}; for (let key of Object.keys(object)) { copy[key] = deepCopy(object[key]); } return copy; } const copy = deepCopy(object); copy.number.one = 3; copy.arr[2].push(5); console.log(object === copy); // false console.log(object.number.one === copy.number.one); // false console.log(object.arr === copy.arr); // false console.log(object); // { a: 'a', number: { one: 1, two: 2 }, arr: [ 1, 2, [ 3, 4 ] ] } console.log(copy); // { a: 'a', number: { one: 3, two: 2 }, arr: [ 1, 2, [ 3, 4, 5 ] ] }
JavaScript
복사

Lodash 라이브러리 사용

실무에서 많이 쓰는 라이브러리
라이브러리를 사용하면 더 쉽고 안전하게 깊은 복사를 할 수 있다
설치를 해야 한다는 점과 일반적인 개발에는 효율적이겠지만, 코테에서는 사용할 수 없다는 것이 단점
const deepCopy = require("lodash.clonedeep") const object = { a: "a", number: { one: 1, two: 2, }, arr: [1, 2, [3, 4]], }; const copy = deepCopy(object); copy.number.one = 3; copy.arr[2].push(5); console.log(object === copy); // false console.log(object.number.one === copy.number.one); // false console.log(object.arr === copy.arr); // false console.log(object); // { a: 'a', number: { one: 1, two: 2 }, arr: [ 1, 2, [ 3, 4 ] ] } console.log(copy); // { a: 'a', number: { one: 3, two: 2 }, arr: [ 1, 2, [ 3, 4, 5 ] ] }
JavaScript
복사

EX)

깊은 복사하기
const a = 'b'; const c = ['d', true, 1]; const e = { g: 'h'}; const i = [{ j: 'k' }, { 1: 'm' }]; const n = { o: { p: 'q' }};
JavaScript
복사
—>
const a1 = a; const c1 = c.slice(); const c2 = [...c]; const e1 = {...e}; // 객체안에 객체가 들어있지 않으니까 const i1 = JSON.parse(JSON.stringify(i)); const n1 = JSON.parse(JSON.stringify(n));
JavaScript
복사

reference