javascript

[CoreJavascript] 1과 데이터 타입

프로일기꾼 2025. 1. 24. 23:17

JS type

  • primitive, reference type으로 구성
  • primitive(원시값)
    • number, string, boolean, null, undefined, Symbol (ES6에 Symbol이 추가)
  • Reference(참조값)
    • Object(객체): Array, Function, RegExp, Set / WeakSet, Map / WeakMap (Set, WeakSet, Map, WeakMap 등 ES6에 추가되었다.)
  • 이 두 타입을 구분하는 이유가 있다. 이 두개의 타입은 메모리에 저장되는 형식이 다르기 때문이다.
  • javascript 메모리 구조는 Stack Memory와 Heap Memory 영역으로 구성되어 있다.
  • 스택 영역에는 변수, 기본형 데이터 저장(정적 할당)
  • 힙 영역에는 참조형 데이터(동적 할당)

기본형 데이터 할당 Process

  var a = 'abc';
  /** 아래와 같은 코드
   * var a; // 선언 후
   * a = 'abc'; // 할당
   */
  • 변수를 선언하면 컴퓨터가 수행하는 일은 아래와 같다.
  1. 메모리에 데이터가 담길 공간을 미리 확보(메모리 1003 번지에 a라는 변수를 지정)
  2. 메모리 식별자(어떤 변수인지를 알 수 있는 혹은, 변수 a가 가리키는 주소)에 변수명을 지정

  • a = 'abc'는 'abc'를 a에 할당하라는 것으로, 컴퓨터는 아래의 프로세스를 수행
  1. 문자열 abc를 메모리(eg, 5003번지)에 저장.
  2. 5003이라는 주소를 변수 a가 가리키는 주소로 이동하여 값을 @5003 으로 넣는다.

  • a = 'abcdef'; 라는 명령이 있다면 a가 가리키고 있는 @5003 번지 메모리가 저장하고 있는 값을 'abcdef'로 변경(abc -> abcdef)하면 될 것 같은데, 그렇게 작동하지 않는다. 별개의 문자열로 지정한다.
  • 즉 'abcdef'라는 문자열을 임의의 공간 @5004번지에 저장한 뒤, 이 주소를 a 변수가 가리키는 주소로 찾아가 @5004 값으로 변경한다.
  • 기본형 데이터의 값을 바꿀 때, 가리키고 있는 주소를 직접 바꾼다.

참조형 데이터 할당 Process

  var obj = {
    a: 1,
    b: 'bbb'
  }
  • 메모리 구조 공간 하나에는 값이 하나씩 밖에 들어가지 못하는데, obj에 할당된 값은 여러개의 값들이 있는 상황. 따라서 특정 메모리 공간에 객체가 저장되는 메모리 위치(시작, 끝 위치)를 저장한다.
  obj.a = 2
  • 이러한 코드를 만나면 메모리는 아래와 같은 모습이 된다.

 


  • a라는 프로퍼티를 변경했을때, obj.a 값이 @5002에서 @5003으로 변경됬지만 obj라는 이름을 가진 값의 주소(@5002)로 변경되지 않음.
  • 객체의 프로퍼티 값을 바꿔도 객체 자체가 가리키고 있는 값의 주소는 변경되지 않는다. 기본형 데이터에 비해 참조형 데이터가 메모리 할당 과정에서 1단계를 더 거치기 때문.



예시 1

  var obj = {
    x: 3,
    arr: [3, 4]
  }
  • 위의 코드를 수행하면 아래와 같은 메모리 상태를 갖게 된다.

  obj.arr = 'str'
  • 이러한 코드를 만나는 경우, 메모리는 아래와 같은 상태를 갖는다.

  • 이 과정에서 @5004번 메모리([3, 4]를 저장하고 있던)를 더 이상 참조하는 대상은 아무 것도 없어지게 되는데, 이를 참조 카운트가 0이라고 표현한다. 참조를 하고 있는 대상이 몇 개인지를 나타내는게 '참조 카운트'이며, 0개의 참조 카운트 인 경우, gc의 수집 대상이 되어 사라지게 된다.
  • 그리고 연쇄적으로 메모리 5004 번지에서 가리키고 있던 8001, 8002 메모리도 참조하고 있는 대상이 없어 가비지 컬렉팅이 되어 수거된다.

여기서 잠깐 ?

  • x 프로퍼티와 arr의 0번째 인덱스은 모두 3이라는 값을 가리키고 있는데, 이 두 메모리 공간에서는 값으로 3이 아닌 3이 저장된 메모리의 주소(@5003)를 저장하고 있다.

'x 프로퍼티, arr 0번째 인덱스를 가리키는 메모리 공간에 값으로 3이 저장되어 있는 메모리 주소(@5003)을 저장하는 것이 아니라 바로 3을 저장하면 메모리 5003 번지 할당을 피해 성능 이득이 있을 수 있는 것 아닌가'

하는 질문이 작은 수에 대해서는 일정 부분 합리적일 수 있다. 하지만 아래의 상황을 통하여 어찌하여 기본형 데이터에 하나의 메모리를 부여하는지 알게 된다.

  var obj = {
    x: '매우 큰 용량을 차지하는 문자열!',
    arr: ['매우 큰 용량을 차지하는 문자열!', 4]
  }
  • 위 obj의 메모리는 아래와 같이 나타낼 수 있다.
    (메모리에 값을 꾸겨 넣을 수 있다 가정 & 실제 저장되는 값은 이진수로 저장하지만 너무 길어 16진수로 표현.)
  obj.x === obj.arr[0];
  • 이러한 데이터를 각각 저장한 뒤 위의 코드를 수행하려면 엄청난 길이의 이진수를 일일히 비교해야하고, 결국 엄청난 성능 저하를 초래한다. 그런데 만약 큰 용량을 차지하는 데이터가 한 두곳이 아닌 1000개, 10000개라고 한다면...??
    이건 메모리에 너무나 큰 부담을 주게 된다.
  • 따라서 하나의 메모리를 할당해 그 공간에 위와 같은 문자열을 저장해두어 관리하는 것이 좋다. 비록 문자열 리터럴이 새롭게 저장될 때마다 기존에 저장된 데이터가 있는지 일일히 비교해야하는 단점이 있어 할당에 있어서는 시간 소요가 될 수 있다. 하지만 이후 비교 과정에서 발생하는 소요 시간, 그리고 메모리 할당 시 차지되는 총 용량은 적다(= 메모리 낭비 최소화)는 이점이 있다.
  • 비교에 비용이 들지 않는다는 표현은 같은 값이 전체 메모리 구조 상에 오직 하나만 존재한다는 뜻이다. 곧 기본형 데이터는 불변값이라고 할 수 있다.

변수 복사

  var a = 10;
  var b = a;
  var obj1 = { c: 10, d: 'ddd' };
  var obj2 = obj1;
  • 위 코드는 아래와 같이 메모리에 데이터가 저장된다.

b = 15;
obj2.c = 20;
  • 위와 같은 코드를 만나면 데이터는 메모리에 다음과 같이 저장된다.

 

  • 기본형 데이터와 참조형 데이터의 이러한 차이 때문에 새롭게 만든 객체의 값(obj2.c)을 바꿨는데 기존 객체의 속성값이 변경되는 이유를 알 수 있다.