얕은 복사와 깊은 복사

seul_velog·2022년 3월 17일
0

JS note

목록 보기
8/9
post-thumbnail
post-custom-banner

데이터의 불변성(Immutability)

원시 데이터 : String, Number, Boolean, undefined, null

let x = 1;
let y = 4;
console.log(x, y, x === y); 
// 1 4 false

x = y;
console.log(x, y, x === y); 
// 4 4 true

y = 7;
console.log(x, y, x === y); 
// 4 7 false

let z = 4;
console.log(x, y, z, x === z); 
// 4 7 4 true
  • 원시데이터들은 기본적으로 불변성을 가지고 있기 때문에 쉽게 사용할 수 있다.
    ❗️tip) 원시 데이터의 경우 생긴 것이 다르면 다르다 라고 이해하면 된다.



참조형데이터 : Object, Array, Function (불변하지 않음)

let a = { k: 1 };
let b = { k: 1 };
console.log(a, b, a === b); 
// {k: 1} {k: 1} false

a.k = 55;

b = a;
console.log(a, b, a === b); 
// {k: 55} {k: 55} true

a.k = 33;
console.log(a, b, a === b); 
// {k: 33} {k: 33} true

let c = b;
console.log(a, b, c, a === c, b === c); 
// {k: 33} {k: 33} {k: 33} true true
  • 참조형데이터의 경우 참조하고 있는 참조값을 변경하면 같은 참조를 가리키는 변수들도 전부 변경이 될 수 있다.
    → 즉 참조형 데이터에서는 할당 연산자를 이용하면서 의도하지 않았던 데이터 값의 변경이 발생될 수 있다.

  • 생긴 것이 같아도 같은 데이터가 아닐 수 있음에 유의하자.

  • 따라서 각자의 독립적인 데이터 값으로 관리하고 싶으면 메모리상에서 분리 해서 사용되어야 한다. 이 때 '복사'를 이용할 수 있다. (얕은복사 & 깊은복사)




얕은 복사(Shallow copy)

Array.from, concat, slice, spread(...), Object.assign

✍️ 표면만 복사한다는 개념으로 이해하자.

Object.assign() 활용하기

const user = {
  name: "seul",
  age: 10,
  email: ["seul@gmail.com"],
  bloodType: "O",
};

const copyUser = Object.assign({}, user); // 1)
console.log(user, copyUser);
console.log(copyUser === user); // false

  • 1) Object.assign() 메서드를 이용해서 첫번째 인수자리에는 빈 객체{} 데이터를 넣고, 두번째 인수로는 복사한 user 라는 데이터를 넣어준다.
    → 대상객체에 출처객체가 가지고 있는 여러 속성들을 새롭게 담아서 반환한다.
    → 여기서 새로운 객체 데이터는 새로운 메모리 주소를 가지게 된다.
    • 첫번째 인수: 대상객체
    • 두번째 인수: 출처객체
// 데이터 수정해보기
const copyUser = Object.assign({}, user);
console.log(user, copyUser);
console.log(copyUser === user); // false

user.age = 12;
console.log(user); // 변경되었다
console.log(copyUser); // 변경되지 않았다 // 2)
  • user.age 데이터를 변경해도 copyUser 데이터에는 영향을 주지 않게 되었다.



전개연산자 활용하기

const user = {
  name: "seul",
  age: 10,
  email: ["seul@gmail.com"],
  bloodType: "O",
};

const copyUser = { ...user }; // 1)
console.log(user, copyUser);
console.log(copyUser === user); // false

user.age = 12;
console.log(user); // 변경되었다
console.log(copyUser); // 변경되지 않았다

user.email.push("abc@naver.com");
console.log(user.email === copyUser.email); // 2) true 

user.email = "xyz.naver.com";
console.log(user.email === copyUser.email); // 3) false
  • 1) 빈객체 데이터에 user라는 객체가 가지고 있는 데이터를 전개해서 넣는다.

  • 2) user에 넣은 데이터가 copyUser에도 똑같이 적용된 것을 볼 수 있다.
    - { ...user } 를 통해서 표면만 복사한 상태이므로 그 속 내용은 복사가 되지 않은 상태이다. 따라서 객체 안의 모든 데이터들도 복사(깊은복사)를 해야 한다.

  • 3) 2)같은 경우는 배열 데이터이기때문에 마찬가지로 데이터주소를 참조한다. 즉 속 내용은 복사가 되지 않은 상태이기때문에 발생한 문제처럼 보였다.
    3)처럼 그냥 일반 문자형 데이터를 넣어주면 어떻게 되는지 궁금해서 작성해 보았다.🤔

  • 아래 사진처럼 user 데이터는 변경되었지만, copyUser 는 변경되지 않음을 알 수 있었다!




깊은 복사(Deep copy)

✍️ 내부에 있는 모든 참조관계까지 새로운 메모리로 복사한다는 개념으로 이해하자.

lodash 활용하기

  • lodash패키지에 들어있는 메서드를 활용해서 간편하게 깊은복사를 만들어보자.

    .cloneDeep() : .clone() 과 비슷하지만 재귀적으로 값을 복사한다. (반복실행하면서 모든 값들을 복제한다. → 깊은 복사가 가능해진다. )

    : 배열데이터라는 껍데기만 복사한 것이 아니고 내부에 들어있는 새로운 참조형 데이터인 객체 데이터까지 전부 복사를 해서 새로운 메모리로 할당했다. 그러므로 내부의 값을 일치연산자로 비교를 해도 false가 나온다.

import _ from "lodash";


const user = {
  name: "seul",
  age: 10,
  email: ["seul@gmail.com"],
  bloodType: "O",
};

const copyUser = _.cloneDeep(user); //1)
console.log(user, copyUser);
console.log(copyUser === user); // false

user.age = 12;
console.log(user); // 변경되었다
console.log(copyUser); // 변경되지 않았다

user.email.push("abc@naver.com");
console.log(user.email === copyUser.email);
  • 1) 깊은 복제를 만들어서, 이 메소드에서 반환이 되면 그것을 copyUser로 받아서 활용할 수 있다. 인수로는 user 객체를 넣는다.



✍️
대표적인 참조형 데이터인 객체, 배열 등을 복사할 때는 그 내부에 또다른 참조형 데이터가 없다는 전제 하에서는 손쉽게 얕은 복사(Shallow copy)를 활용하고, 특정한 참조데이터 내부에 또 다른 참조형 데이터가 있다면 깊은 복사(Deep copy)를 고려하자.🤔

profile
기억보단 기록을 ✨
post-custom-banner

0개의 댓글