TIL - 원시자료형 / 참조 자료형, 얕은 / 깊은 복사

moontag·2022년 5월 12일
0

JavaScript TIL

목록 보기
6/20


그림출처







원시 자료형(primitive data type)

Number, String, Boolean, undefined, null, Symbol(ES6), Bigint

💌 변수에 값을 할당

🍔  Stack에 저장

  • 🍔  Stack : 고정된 데이터 공간을 사용
    데이터보관함(Stack) 한 칸에 하나의 데이터만 보관할 수 있다
    햄버거처럼 위로 차곡차곡 쌓이는 구조다
  • 변수에 한 개 값만 할당된다
    값 자체는 변경 불가능하지만(immutable), 변수에 다른 값으로 재할당하면 가능 (const 제외)
  • 불변값 immutable Value : 데이터를 복사후 복사본을 수정해도 원본에 영향을 주지 않는다.
// ex
let x = 2;
let y = x;  // x값을 복사하여 y에 저장 y = 2
y = 3; // x는 그대로 2. y값만 재할당 된 것. y = 3



참조 자료형(reference data type)

Array, Object, Function

🏠  변수에 주소를 할당

메모리 주소(참조값)는 Stack에 저장

🌲  실제 값들은 Heap에 저장

  • 🌲  Heap : 동적인 데이터 공간을 사용
    이 데이터 보관함의 크기는 동적으로 변하고 주소값을 저장한다

Q. 왜 동적(dynamic)인 데이터 저장소를 사용하는가?
데이터 추가,삭제 과정에서 고정된 데이터 공간은 비효율적이므로
상황에 따라 동적으로 변하는 데이터 저장소가 적합하다

  • 주소를 복사하기때문에, 복사한 곳에서 데이터를 변경한다면 기존데이터에도 영향이 간다
// ex
let x = { foo: 3 };
let y = x;  // y에 x의 주소를 참조
y.foo = 2;  // 같은 주소를 참조하므로, x값이 y값과 동일하게 변경됨
  • 참조자료형 === 참조자료형 끼리의 주소값은 다르다고 판단한다
    ===(strict equality)는 주소값이 같은지 확인한다
    그런데 참조자료형은 생성하면서 각자 다른 주소값을 참조하므로 ===로 비교할시 false를 반환한다
console.log([1,2,3] === [1,2,3]); // false
console.log({ foo: 'bar' } === { foo: 'bar' }); // false
console.log([] === []); // false
  • 가변값 mutable value






복사

얕은 복사 shallow copy

주소(참조)를 복사
중첩된 구조를 변경하면 원본과 복사본 모두 영향이 미친다

  • 객체 안 객체가 있을 때,
    내부에서 1개 객체라도 원본 객체를 참조한다면 얕은 복사다.
// ex)
const arr = [1, 2, [3, 4]];
const copied = arr.slice();
copied[2].push(5);
console.log(arr);    // [1, 2, [3, 4, 5]]
console.log(copied); // [1, 2, [3, 4, 5]]

얕은복사 예시

  1. arr.slice()
//arr.slice()
const arr = [1, 2, [3, 4]];
const copied = arr.slice();
copied[2].push(5);  //arr에도 push되어 있음 [1, 2, [3, 4, 5]]
  1. Object.assign
  • 1차원 객체일 때에는 깊은 복사지만, 2차원 이상부터는 얕은 복사다
//예 1 - 배열
const arr = [1, 2, [3, 4]];
const copied = Object.assign([], arr);
copied[2].push(5);
console.log(arr) //arr에도 push되어 있음 

//예 2 - 객체
let origin = {
    a: 1,
    b: { c: 2 }
};
let copy = Object.assign({}, origin);
copy.b.c = 3

console.log(origin === copy) // false
console.log(origin.b.c === copy.b.c) // true
  1. ...spread
  • 1차원 객체일 때에는 깊은 복사지만, 2차원 이상부터는 얕은 복사다
//예 1 - 배열
const arr = [1, 2, [3, 4]];
const copied = [ ...arr ];
copied[2].push(5);
console.log(arr) //arr에도 push되어 있음 

//예 2 - 객체
const obj = {
  a: 1,
  b: {
    c: 2,
  }
}
const newObj = {...obj};
newObj.b.c = 99;

console.log(obj === newObj); // false
console.log(obj.b.c); // 99
console.log(obj.b.c === newObj.b.c); // true

깊은 복사 deep copy

새로운 주소에 실제 값만 복사
복사본과 원본의 주소는 공유안한다

  1. JSON.parse & JSON.stringify
    • JSON.stringify - 데이터를 문자로 변형
    • JSON.parse() - 변형된 문자를 다시 객체로 변형
  • 단점 - 처리속도가 느림
    한계 - BigInt, new Date(), 함수 역시 JSON.stringify가 처리할 수 없는 객체
    const arr = [1, 2, [3, 4]];
    const copied = JSON.parse(JSON.stringify(arr));
    copied[2].push(5);
    console.log(arr);  // [1, 2, [3, 4]]
    console.log(copied); //[1, 2, [3, 4, 5]]
  1. 재귀함수를 이용
    복잡하다

  2. 라이브러리(lodash, ramda 등) 사용
    설치를 해야하며 낮은 퍼포먼스를 가진다

    // 'npm install lodash'로 설치 후, Node.js 환경에서 실행
    const _ = require('lodash');
    let obj1 = { name: 'hi', age: 22 };
    let obj3 = _.cloneDeep(obj1);
    obj3.name = 'hello';
    console.log(obj1); // { name: 'hi', age: 22 }
    console.log(obj3); // { name: 'hello', age: 22 }
  • 깊은 복사를 하는 방식이라고 알려진 것들은 사실 깊은 복사를 못한다
  • 일부 라이브러리(lodash, ramda 등) 에선 모든 요소를 복사하는 메소드를 구현해놨다
  • 하지만, 낮은 퍼포먼스를 감수하면서까지 정말 깊은 복사를 해야하는지 생각해봐야한다



💎 추가로 찾아볼만한 것

heap
stack call stack
event loop
callback queue
web apis







참조

노마드코더 - (EP 03.) 자바스크립트 개발자라면 알아야하는 핵심 컨셉 33개 | #3. Value Types and Reference Types

어쨌든 이벤트 루프는 무엇입니까? | Philip Roberts | JSConf EU

깊은 복사와 얕은 복사에 대한 심도있는 이야기

JavaScript에서 객체 복사하기

그림출처 - How JavaScript works: an overview of the engine, the runtime, and the call stack









profile
터벅터벅 나의 개발 일상

0개의 댓글