즐코

깊은 복사 vs 얕은 복사 / spread (...) ! 본문

JavaScript

깊은 복사 vs 얕은 복사 / spread (...) !

YJLEE_KR 2022. 1. 7. 16:54

1. 깊은 복사 vs. 얕은 복사

2. 얕은 복사 연습해보기

 

 

다시 떠올려보는 비교연산자

https://yjleekr.tistory.com/17 

 

Operators (연산자)에 대해서 - 1

1/ 증감 연산자 2/ 할당 연산자 3/ 논리 연산자 4/ 동등 연산자 연산자에 대해서 좀 더 공부해보았다. 산술/연결 연산자는 건너뛰고 헷갈리는 연산자에 대해서만 알아봄 1/ 증감 연산자 (Increment & dec

yjleekr.tistory.com

console.log({ } === { }) 
false가 출력된다. 왜냐하면 저번에 공부했던 object equality 에서 객체안에 같은 key:value값이 있어도
서로 다른 reference로 컴퓨터가 받아들이기 때문에 엄연히 따지면, 다른 아이들이다.

컴퓨터는 이 객체를 만들때마다 새로운 값으로 인식 및 저장한다. 

하지만 그 객체 안의 값이 같다면, 그 속성값들 자체는 같은 데이터로 인식 및 저장한다. 

 

 

1. 깊은 복사 vs 얕은 복사

 

깊은 복사 deep copy 얕은 복사 shallow copy
깊은 복사가 되는 데이터
원시 타입 (primitive type) 데이터
= number, string, boolean
얇은 복사가 되는 데이터
객체, 참조타입 (원시 타입 제외한) 데이터
= array, object, function
값 자체를 가져온다! 

데이터 복사 시
새로운 메모리 공간을 생성하여
메모리에 독립적인 값을 저장
값을 가리키는 포인터 (주소이자 링크이자 뿌리?)를 가져온다

데이터 복사 시
데이터에 대한 주소, 뿌리의 값으로 저장되기 때문에
복사 시 값 자체가 아니라, 값을 가리키는 레퍼런스,
참조 주소가 복사된다.

 

깊은 복사 (for primitive datatype)
        let a = 1;
        let b = a;

        console.log(a); // 1
        console.log(b); // 1

        a = 3;
        console.log(a) // 3
        console.log(b) // 1​

변수 a 에 1을 할당하고 / 변수 b 에 a를 할당해준다. => 즉, a == b 같아지게됨
이 때, 변수 a 에 3을 다시 할당해버리면, a는 3으로 바뀌고, b는 그대로 1이다. 

이는 a,b는 독립적인 공간을 차지하고 저장되기 때문이다. 
즉, 얇은 복사처럼 공간의 주소값, 참조값을 가져오는 게 아니라 독립적인 공간을 가지고 있으니까
a가 바껴도 b는 바뀌지 않는다. 

 

얕은 복사 (shallow copy)
	let obj1 = {name:'lee'}
        let obj2 = obj1 

        console.log(obj1) // {name : 'lee'}
        console.log(obj2) // {name : 'lee'}

        obj1.name = "kim" 
        console.log(obj1) // {name : 'kim'}
        console.log(obj2) // {name : 'kim'}

        console.log(obj1 === obj2) // true

        let obj3 = {name:"kim"}
        console.log(obj3) // {name: 'kim'}

        console.log(obj3 === obj1) // false
        console.log(obj3.name === obj1.name) // true

위에서 한 깊은 복사와 다르다.

#1 - obj2에 obj1을 대입해준 경우,

콘솔처리해보면 복사가 되는 건 똑같다.

이 때 obj2는 obj1 값을 실물 그대로 가져온 게 아니라, obj1을 가리키는 주소값, 링크를 가져온거다.
(like POINTER)
따라서, obj1 과 obj2를 비교해보면 주소값이 같기 때문에 true 가 나온다.


#2 - obj3을 obj1과 동일한 key,속성값을 가진 객체로 만든 경우 (대입X)


내부 데이터는 같을지언정, 컴퓨터는 둘을 다른 데이터 주소값으로 저장해주기 때문에
obj3과 obj1을 비교해보면 주소값이 다르기 때문에 false가 나온다. 


#3. - 마지막으로 obj3, obj1 의 객체 속성값을 비교해보면?!

obj1.name 과 obj3.name 서로 다른 객체 안에 있지만,
어쨌든 객체 내부의 원시값 자체는 깊은 복사가 되기 때문에, 

둘을 비교하면 name : "kim" 은 같은 string값이므로 true가 출력된다.  

 

 

Object.assign(A , B)  

 

단어 뜻 그대로 객체를 할당해주는 역할 / 객체 내부의 데이터값들을 바꿔준다! 

 

사용법  / Object.assign(A , B)  

첫번째에 들어가는 매개변수 A : 속성을 바꿔줄 객체 (바꿔줘!)

두번째에 들어가는 매개변수 B : A에 넣고 싶은 속성을 가지고 있는 객체 (어 바꿔줄게!)

 

연습삼아 두 가지 경우를 해보았고 결론부터 말하자면 아래와 같다.

 

1- 이미 속성값들을 가진 객체를 바꿔주면서 새로운 변수에 담아준 경우  - > 깊은 복사가 됐다 

2- 바꿔줄 객체를 빈 객체로 설정해 그 빈 객체에 복사를 한 경우 -> 얕은 복사가 됐다 

 

#1. 깊은 복사가 된 경우
   let yj = {
            name : 'A',
            age : 29,
            footsize : 230
  }

   let wantChange = {
            name : 'A',
            age : 20,
            height : 170,
  }
  
  let changedYj = Object.assign(yj,wantChange)
  
   console.log(changedYj)
   console.log(yj)

   console.log(yj===changedYj) // true

이미 속성이 있는 yj에 다른 객체 wantChange를 복사해준다.
<출력>
name : 'A' 같으니까 그대로 있고
age : 29 -> 20 으로 바뀌고
footsize : 230 은 다른 객체인 wantChange에 없는 속성이므로 그대로 가져간다.
height: 170 은 그대로 복사 되어 추가된다.

바뀐 yj 객체를 변수 changedYj에 담아주고
yj 와 changedYj를 콘솔 찍어보니 같은 객체가 나온다.

둘을 비교해보면? 
changedYj는 yj를 기반한 객체이므로 둘을 비교해보면 같은 참조값을 가진 객체이기때문에 true가 나온다.

 

 

#2. 얕은 복사가 된 경우
let lee = {
              name : 'leeyj',
              age : 30
   }

   let Newlee = Object.assign({},lee)

   console.log(lee)
   console.log(Newlee)

   console.log(Newlee===lee) // false​

lee라는 객체를 복사해서 담아줄 Newlee라는 변수를 선언해준다.
그리고 여기에 Object.assign({ },lee)를 담아줬다. 
즉, 빈 객체 { } 에 lee를 담아줬고, 콘솔을 찍어보면, lee 객체나 Newlee 객체나 같은 객체로 출력된다.

근데? 둘을 비교해보면?

false가 나온다. 

왜냐면, Newlee는 lee의 속성만을 복사해온거지, lee 객체 자체를 가져온 게 아니다.
새로운 빈 객체 { } 안에 lee 속성을 넣은 거니까 얕은 복사가 된거다. 
그래서 결과물은 lee와 Newlee가 같지만 엄연히 따지면 다른 참조값을 가진 객체이므로 false이다! 

 

 

... spread 문 ?

 

사용법 : 

속성을 복사해서 넣어줄 객체 = { 속성1, 속성2, ... 속성을 복사해오고싶은 객체명

변경할 객체 안에 복사해올 객체명을 넣고 그 바로 앞에 ... 붙이면 끝!!

 

Object.assign()과 비슷한데, Object.assign은 옛날구문이고 이 spread 문법?이 ES6 의 최신 문법이라고 한다. 

내가 봐도 이 dotdotdot 이 더 간결한 것 같다.

 

얕은 복사
 let cat = {name : 'hoochu'}
   let cat2 = {
              age : 2, ...cat
   }
   console.log(cat2)
   
   /* {age : 2,
       name : 'hoochu'} */

cat2에 다른 속성값이 있어도, 그 맨 뒤에 ...cat 만 넣으면
cat의 속성값이 복사되어 온다. 

 

 

객체 안에 객체를 복사해서 넣을수도 있다!

blockchain의 그 block이 객체이다. 
 const blockHeader = {
        hash:000000000000000000000, // 이블럭의 이름 (16진수로 들어감)
        timestamp:1641519318, //timestamp = 1970-01-01을 기준으로 날짜를 계산하자
        height:717521, // block의 속성값들은 아무거나
        Difficulty:4, //mining 시 난이도조절
        Nonce:4006,
      }
      
      
 const blockBody = [
          {
            fee:'0.000000000 BTC',
            Hash:89232461321215545465465212
          },
          {
            fee:'0.000000000 BTC',
            Hash:89232461321215545465465212
          }
        ]
        
 const block = {
      blockHeader : {...blockHeader}, // 객체의 속성값으로 객체가 들어감
      blockBody : [...blockBody] // 객체의 속성값으로 객체가 들어감
 }
 
 console.log(block)​


객체 내부의 속성값으로 객체가 넣어졌다! 

 

 

new Set (객체명) 

객체안의 중복되는 속성값들을 제거해준다. 

const dupArr = [1,2,3,1,2]
const set = new Set (dupArr)
console.log(set)​

 결과물 = 중복된 1, 2는 제거 되고 (3) [1, 2, 3] 만 남는다.

 

 

tip!

 

이전에 배울 때 const 는 상수, 즉 바뀌지 않는 고정값의 변수를 선언 할 때 쓴다고 했었다. 

https://yjleekr.tistory.com/16 

 

변수 선언 let, const / string 표현

1. let/const 변수선언 복습 1-let 2-const 2. string (문자열) 표현 tip 1. let/const 변수선언 복습 변수 선언에 대해서 변수명을 지정하면 변수를 저장하게끔 메모리에 저장공간을 확보해준다.  1- let (added..

yjleekr.tistory.com

primitive data type들은 const로 변수를 선언해버리면 바뀌지 않는데 비해

객체는 const로 선언했어도 객체 그 자체를 바꾸진 못해도 내부의 내용을 변경하거나 추가하는 건 할 수 있다..

뭔가 모순이 생겨버리는데,, 적절히 알아서 잘 쓸것,,

 

 

Comments