[JS] 참조타입에서의 얕은복사 vs 깊은복사

Seju·2023년 7월 6일
1

JavaScript

목록 보기
17/28
post-thumbnail

📚 얕은복사와 깊은복사에 대해 알기전에 알아야 할 내용


  • 먼저 자바스크립트에서의 얕은복사와 깊은복사는 참조형 데이터 타입에서만 일어난다

원시타입에서 얕은복사와 깊은복사가 일어나지 못하는 이유는?

원시타입(String,Number,Null,undefined,Boolean,Symbol,bigInt)들은 값에 의한 전달,
즉 원시타입 자료형을 다른 변수에 할당 시 그 값자체가 복사되기 때문이다.
값 자체가 복사 되기때문에, 할당한 새로운 변수는 전에 변수와 다른 독립적인 존재이다.

let num1 = 42;
let num2 = num1; // 실제 값 복사 (참조가 아님)

num1num2는 각기 다른 메모리 영역으로 저장되고,
서로 독립적인 값을 가지므로 원본 변수에 영향을 주지 ❌

얕은복사와 깊은복사가 필요한 이유
참조타입인 객체와 배열에서 변수를 다른 변수에 할당할때 값이 아닌 참조가 복사되기 때문에


  • 자바스크립트에서 객체,배열,함수는 참조형데이터타입 에 속하는데,
    참조타입의 데이터의 복사 시에 데이터의 값이 아닌 값이 저장된 메모리 주소가 저장된다
    즉, 데이터 복사시 서로 같은 메모리 주소를 공유한다는 의미이다.

참조형데이터 타입의 복사, 같은 메모리 주소를 공유하고 있다

  • 여기서 데이터 타입을 복사시 참조형 데이터타입에서는 두가지 경우의 수로 나뉘게 되는데 이것이 바로 얕은복사와 깊은복사이다

💿 참조에 의한 복사(by reference)


  • 참조에의한 복사란 객체를 복사할때 원본 값과 복사된 값이 같은 메모리 주소를 가르키는 경우를 나타낸다
  • 이 경우를 두 변수가 같은 객체를 참조하고 있다고 한다
  • 즉 얕은복사는 원본값과 복사된값이 같은 객체를 참조하고 있다는걸 뜻한다
  • 여기서 참조에의한 복사를 하게되면 서로 같은 메모리 주소를 공유하기때문에 원본값을 복사한 값의 변경시 원본값에도 영향이가게 된다
let origin = { name: 'Jinny' }
let copy = origin;

copy.name = 'Mr.Lee';

//원본 객체에도 영향이 가는 모습
console.log(origin); // 'Mr.Lee'

console.log(copy); // ''Mr.Lee;
console.log(origin === copy) // true 메모리 주소값을 공유하기 때문에 true가 출력된다 

copyname 값을 변경했음에도 불구하고, 원본 originname이 변경되었다

🦆 얕은 복사 (Shallow Copy)


  • 참조형 데이터타입에서의 얕은 복사는 참조에의한복사처럼 메모리 주소가 복사 되는 것이 아닌 값만 복사되는 것을 뜻한다
  • 복사한 객체는 참조데이터와 다른 데이터주소를 가지며, 다른 객체로 인식된다

1. Object.assign()

  • Object.assign()메서드는 주어진 source 객체들에서 열거 가능한 모든 속상 값을 target 객체로 복사할 수 있다'
	//target ➡️ 목표 객체, 소스 객체의 프로퍼티를 복사 후 반영한 후 반환할 객체
    //source ➡️ 출처 객체, 목표 객체에 반영하고자 하는 프로퍼티를 제공
	Object.assign(target,source)

🚨주의 Object.assign()를 사용하면, 객체 자체는 깊은 복사가 수행되지만, 2차원 이상(객체 안의 객체)는 얕은 복사가 수행된다.

const sourceObject = {name : "seju", age:"26", gender:"male"}

//원본 sourceObject의 프로퍼티값 Object.assign()으로 복사함
const copyObject = Object.assign({},sourceObject) 

console.log(copyObject)
console.log(sourceObject === copyObject) //false

copyObjectsourceObject의 값은 복사했으나, 메모리 주소는 복사되었지 않기때문에 비교했을때 false가 나오는 모습이다

copyObject에 새로운 name을 할당해도, 기존의 sourceObject는 변화가 없다

2. spread operator

  • 전개 연산자(...)을 써도 객체에 대한 깊은복사가 가능하다.

    🚨주의 전개연산자도 Object.assign()과 같이 2차원 이상부터(객체의 객체등등)에 대한값들은 얕은복사가 된다

const obj = {name : 'seju', age : '25' , 사는곳 : "서울"}

//spread oprator로 원본 obj의 프로퍼티들을 전재
const spreadObj = {...obj}

console.log(obj);
console.log(spreadObj)

전개연산자로 복사한 spreadObjobj를 콘솔로그로 찍어본 모습

전개연산자로 복사한 spreadObjobj 비교 값만 복사되었기때문에 비교시 false가 나오고 spreadObj의 값을 변경시에도, 원본 객체인 obj의 값의 변화는 없다


🧑‍🚀 깊은 복사 (Deep Copy)

  • 위처럼 얕은복사는 객체내의 depth가 1층일때 밖에 복사하지못한다. (객체가 중첩되었을때는, 복사한 객체내부의 객체의프로퍼티를 변경시, 원본 객체 내부의 객체 프로퍼티에도 영향이간다)
  • 그럼 객체가 중첩되었을때 해당 객체까지 프로퍼티를 메모리 주소를 가지고 있는것이아닌(참조에의한 복사), 값만 복사하고싶다면?

이러한 중첩된 프로퍼티에 객체를 포함하는 객체 복사깊은복사라고한다.

여기서 깊은 복사를 수행하려면 여러가지 방법이 있는데 대표적으로,

  1. 객체 안의 객체에서 계속 Object.assign()
  2. lodash 라이브러리의 _.cloneDeep(obj) 사용하기
  3. 재귀 함수를 활용하여 deepCopy Function 만들기

deepCopy Function (깊은 복사 유틸리티 함수)

function cloneDeep(object) {
  return Object.fromEntries(
    Object.entries(object).map(([key, value]) => {
      let type = typeof value;
      if (value && type === 'object') {
        value = cloneDeep(value);
      }
      return [key, value];
    })
  );
}
profile
Talk is cheap. Show me the code.

0개의 댓글