코딩쌀롱

[코어 자바스크립트] 1. 데이터 타입 본문

[코어 자바스크립트] 1. 데이터 타입

이브✱ 2020. 12. 17. 17:58

1-1. 데이터 타입의 종류

- 기본형(primitive type): number, string, boolean, null, undefined, symbol

- 참조형(reference type): object, array, function, Date, RegExp, Map, WeakMap, Set, WeakSet

1-2 데이터 타입에 관한 배경지식

메모리와 데이터

컴퓨터는 모든 데이터를 0, 1로 바꿔 기억.

- 비트(bit): 0, 1을 표현하는 하나의 메모리 조각, 고유한 식별자로 위치를 확인

- 바이트(byte): 8bit

 

모든 데이터는 바이트 단위의 식별자, 정확하게는 메모리 주솟값을 통해 서로 구분하고 연결

1-3 변수 선언과 데이터 할당

- 변수 선언: 메모리에서 비어있는 공간(변수 영역)을 확보하고 그 공간의 이름(식별자)을 설정

- 데이터 할당: 데이터를 저장하기 위한 별도의 메모리 공간(데이터 영역)을 다시 확보하고 이 주소를 변수 영역에 저장

   메모리 영역 → 변수 영역, 데이터 영역으로 나눠서!

 

<변수 영역에 직접 값을 대입하지 않고, 데이터 영역에 굳이 저장하는 이유>

  • 1. 자유로운 데이터 변환: 미리 확보한 공간 내에서만 데이터 변환을 할 수 있다면 변환된 데이터를 다시 저장하기 위해서 '확보된 공간을 변환된 데이터 크기에 맞게 늘리는 작업'을 해야한다. 중간에 있는 데이터를 늘려야 한다고 가정하면, 뒤에 있는 데이터들을 뒤로 옮기고, 주소를 다시 식별자에 연결해줘야 한다. 처리해야할 연산이 늘어날 수 밖에 없다.
    → 변수, 데이터를 별도의 공간에 나눠 저장, 데이터에 어떤 변환이든 무조건 새로 만들어 새로운 공간에 저장
  • 2. 메모리를 효율적으로 관리: 500개의 변수에 숫자 5 할당하는 상황을 가정. 모든 변수 공간에 5를 할당하면 숫자형 8바이트를 500개 사용하는 것이고, 비효율적이다.
    → 5를 데이터 영역에 한 번 저장, 해당 주소만 변수 영역에 입력! 중복된 데이터에 대한 처리 효율이 높아진다

1-4 기본형 데이터, 참조형 데이터

불변값 (기본형 데이터)

// (1)
let a = 'abc';
a = a + 'def';

// (2)
let b = 5;
let c = 5;
b = 7;

(1) 기존의 'abc'가 'abcdef'로 바뀌는 것이 아니라, 새 공간에 문자열 'abcdef'를 만들어 그 주소를 변수 a에 저장.

    → 'abc', 'abcdef'는 완전 별개의 데이터, 값을 변경X 새로 값을 만듦O  

(2) 데이터 영역에 5가 있으면 주소 활용, 없으면 새로운 데이터 공간 만들어 5를 저장, 주소를 변수 b에 저장.

     위에서 이미 만들어 놓은 값이 있으니 데이터 영역에서 5를 찾아 주소를 변수 c에 저장.

     기존에 저장된 5를 7로 바꾸는 것이 아니라, 7을 찾아서 있으면 재활용, 없으면 새로 만들어 b에 저장.

 

값이 변경되는 게 아니라, 아예 새 공간을 새로 만들어 재할당 하는 것.(이미 있다면 주소 재활용)

→ 한 번 만든 값은 바꿀 수 없다. 변경은 새로 만드는 동작을 통해서만 이뤄진다! (불변)

→ 한 번 만들어진 값은 가비지 컬렉팅을 당하지 않는 한 영원히 변하지 않는다

 

가변값 (참조형 데이터)

// (1)
let obj = {
   a: 1,
   b: 'bbb'
};
// (2)
obj.a = 2;

(1) 변수 영역의 빈 공간 확보, (이름: obj) 

    데이터 영역에 저장하려고 보니 여러 개의 프로퍼티를 가진 객체. 별도의 변수 영역 마련 (이름: obj / 값: 현주소)

    객체의 변수 영역에 프로퍼티 a, b 이름 지정

    데이터 영역에서 1 검색, 없으면 저장. (이름:a / 값: 현주소)

    데이터 영역에서 'bbb'검색. 없으면 저장 (이름:b / 값: 현주소)

    → ①변수 영역, ②데이터 영역, ③객체의 변수 영역이 존재.

    → 변수 영역에는 데이터주소만, 데이터데이터 영역

 

(2) 데이터 영역에서 2 검색, 없으면 저장 (이름:a / 값: 현주소)

    변수 영역은 그대로, 객체의 변수 영역의 데이터 주소가 바뀐 것.

    → 새로운 객체가 만들어진 것이 아니라 기존의 객체 내부 값만 바뀐 것이기 때문

 

→ 객체의 변수(프로퍼티) 영역이 별도로 존재, 데이터 영역은 기존의 메모리 공간 활용

 데이터 영역 값은 불변, 변수 영역 값은 가변 

변수 영역에는 (이름: 식별자 / 값: 데이터 영역 주소) 저장

데이터 영역에는 (기본형 데이터 값 or 참조형 데이터의 주소) 저장

 

참조 카운트: 어떤 데이터에 대해 자신의 주소를 참조한 변수의 개수

참조카운트가 0인 메모리 주소는 가비지 컬렉터의 수거 대상이 되고,

가비지 컬렉터는 특정 시점이나 메모리 사용량이 포화 상태에 임박할 때마다 자동으로 수거 대상들을 수거한다.

수거된 메모리는 다시 새로운 값을 할당할 수 있는 빈 공간이 된다.

 

변수 복사 비교

let a = 10;
let b = a;
let obj1 = {c: 10, d: 'ddd'};
let obj2 = obj1;

b = 15; // (1)값을 직접 변경
obj2.c = 20; // (2)값이 아닌 내부 프로퍼티 변경

(1) 데이터 영역에서 15 값을 찾고, 없으면 새로 만들어 주소값 할당

(2) 데이터 영역에서 20 값을 찾고, 없으면 새로 만듦

     이 주소를 든 채로 변수 영역에서 obj2를 찾고,

     obj2의 값(데이터 영역 주소)이 가리키는 객체의 변수 영역에서 c를 찾고,

     값에 20의 데이터 영역 주소를 대입한다.

 

→ 결과적으로, (1)의 a, b는 변수 영역의 값이 달라짐 (값이 달라 주소값이 다르니까)

→ (2)는 obj2는 obj1을 참조하기 때문에, 변수 영역의 값은 달라지지 않고 그대로

    달라진 것은 obj2.c 프로퍼티의 값이 달라짐 → obj1.c도 바뀜(같은 주소니까 당연한 것)

a !== b
obj1 === obj2

 

 어떤 데이터 타입이든 변수에 할당하기 위해서는 주솟값을 복사해야 하기 때문에, 엄밀히 따지면 JS의 모든 데이터 타입은 참조형 데이터일 수 밖에 없다. 다만 기본형은 주솟값을 복사하는 과정이 한 번이고, 참조형은 한 단계를 더 거치게 된다는 과정에 차이가 있고, 변수 복사 이후의 동작에도 큰 차이가 발생하는 것이다.

 

 참조형 데이터가 '가변값'이라고 설명할 때의 '가변'은 참조형 데이터 자체를 변경할 경우를 말하는 것이 아니다. (이 경우에는 데이터 주소가 바뀌기 때문에 값 자체가 달라진다.) 참조형 데이터의 내부 프로퍼티를 변경할 때만 성립한다.

 

1-5 불변 객체

값으로 전달받은 객체를 바꾸더라도, 원본 객체는 변하지 않아야 하는 경우에 불변객체가 필요하다.

예를 들면, 정보가 바뀐 시점에 알림을 보내야 한다거나, 정보의 차이를 가시적으로 보여줘야 하는 등의 기능을 구현할 때

(코드스쿼드 마스터즈 문제 - 큐브를 돌리는 과정을 차례대로 출력할 때)

얕은 복사, 깊은 복사

- 얕은 복사: 바로 아래 단계의 값만 복사

   중첩된 객체에서 참조형 데이터가 저장된 프로퍼티를 복사할 때 그 주솟값만 복사한다는 의미.

   해당 프로퍼티에 대해 원본과 사본이 동일한 참조형 데이터의 주소를 가리키게 된다.

   즉, 사본을 바꾸면 원본도 바뀌고, 원본을 바꾸면 사본도 바뀐다.

- 깊은 복사: 내부의 모든 값들을 하나하나 찾아서 전부 복사

let user = {
   name: 'eve',
   urls: {
      portfolio: 'http://github.com/abc',
      blog: 'https://blog.com'
   }
};
let user2 = copyObject(user); //얕은 복사

user2.name = 'yeji';
console.log(user.name === user2.name); //(1)false

user.urls.portfolio = 'http://portfolio.com';
console.log(user.urls.portfolio === user2.urls.portfolio); //(2)true

user2.urls.blog = '';
console.log(user.urls.blog === user2.urls.blog); //(3)true
   

(1) user 객체에 직접 속한 프로퍼티에 대해서는 복사해서 완전히 새로운 데이터가 만들어졌다. → false

(2) 한 단계 더 들어간 urls의 내부 프로퍼티들은 기존 데이터를 그대로 참조하는 것. → true

 

→ 어떤 객체를 복사할 때 객체 내부의 모든 값을 복사해서 완전히 새로운 데이터를 만들고자 할 때, 객체의 프로퍼티 중에서 그 값이 기본형 데이터일 경우에는 그대로 복사하면 되지만, 참조형 데이터는 다시 그 내부의 프로퍼티들을 복사해야 한다. 참조형 데이터가 있을 때마다 재귀적으로 수행해야만 비로소 깊은 복사가 되는 것이다.

JSON.parse(JSON.stringify(object)); // (1)객체 깊은 복사

// (2)마스터즈 테스트 문제에서 배열 얕은 복사 2번
let copied = this.dataArr.map(e => [...e]);

(1) 객체를 깊은 복사할 수 있는 간단한 방법 중 하나로, 객체를 JSON 문법으로 표현된 문자열로 전환했다가 다시 JSON 객체로 바꾸는 것. 다만 메서드나 숨겨진 프로퍼티인 __proto__나 getter/setter 등과 같이 JSON으로 변경할 수 없는 프로퍼티들은 모두 무시한다. 순수한 정보만 다룰 때 활용하기 좋은 방법

(2) 코드스쿼드 마스터즈 테스트를 풀 때 2차 배열을 복사하기 위해서 얕은 복사를 2번했던 방법이다.

 

1-6 undefined와 null

'없음'을 나타내는 두 가지. 의미는 같은 것 같지만 미세하게 다르고, 사용하는 목적 또한 다르다.

- null: '비어있음'을 명시적으로 나타내고 싶을 때 사용

- undefined: 값을 대입하지 않은 변수에 접근하고자 할 때 자바스크립트 엔진이 반환해주는 값

 

<null 주의 사항>

let n = null
console.log(typeof n); //object

console.log(n == undefined); //true
console.log(n == null); //true

console.log(n === undefined); //false
console.log(n === null); //true

typeof null → object라는 점을 주의해야 한다.(JS 자체 버그)

어떤 변수의 값이 null인지 여부를 판별하기 위해서는 typeof 대신 다른 방식으로 접근해야 한다.

동등 연산자(==)로 비교할 경우 null, undefined가 같다고 판단.

일치 연산자(===)로 비교해야 정확히 판별할 수 있다.

 

 

 


정재남 작가님의 〔 코어 자바스크립트 〕 책을 공부하며 정리했습니다.

Comments