TIL 78 | 코어 자바스크립트(1) 데이터타입

meow·2020년 11월 17일
0

JavaScript

목록 보기
28/46

시대의 명작 코어 자바스크립트를 읽고 자바스크립트 마스터에 도전합니다...

데이터 타입의 종류

자바스크립트 데이터 타입에는 기본형과 참조형이 있다.

기본형 Primitive Type

  • 숫자(number)
  • 문자열(string)
  • 불리언(boolean)
  • null, undefined
  • 심볼(symbol: ES6에서 추가)

참조형 Reference Type

  • 객체(object)
  • 배열(array)
  • 함수(function)
  • 날짜(date)
  • 정규표현식(RegExp)
  • 그 외 ES6에서 추가된 객체의 하위 분류에 속하는 Map, WeakMap, Set, WeakSet 등.

기본형은 값이 담긴 주소값을 바로 복제하고, 참조형은 값이 담긴 주소값들로 이루어진 묶음을 가리키는 주소값을 복제한다.

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

메모리와 데이터

컴퓨터는 데이터를 0과 1로 바꿔 기억한다. 0 또는 1로 이루어진 하나의 메모리 조각을 비트(bit)라고 한다. 많은 비트를 한 단위로 묶으면 표현할 수 있는 값도 늘어나고 효율적이다. 이렇게 8개의 비트로 이루어진 바이트(byte)가 생겨났다. 1바이트는 2^8, 총 256개의 값을 표현할 수 있다.

C/C++, 자바 등의 정적타입 언어는 데이터 타입별로 할당할 메모리 영역을 정해두었고, 허용되는 숫자 밖의 숫자를 입력하면 오류가 난다. 하지만 자바스크립트는 메모리 공간에 대해 더 자유로워졌고, 메모리 공간을 넉넉하게 할당하기 때문에 형변환을 신경쓰지 않아도 된다. ( 아, 이런 의미에서 자바스크립트를 동적타입 언어라고 하는 것인가...? 그래서 타입 스크립트가 나온걸까...? ) 모든 데이터는 바이트 단위의 식별자, 메모리 주소값을 통해 서로 구분하고 연결될 수 있다.

식별자와 변수

식별자와 변수를 혼용하는 경우가 많으나 둘은 다른 개념이다!

변수
말 그대로 '변할 수 있는 수'다. 정확히 말하면 '변할 수 있는 데이터'이다.

식별자
어떤 데이터를 식별하는데 사용하는 이름, 즉 변수명이다.

변수 선언과 데이터 할당

변수 선언

// 컴퓨터: 변할 수 있는 데이터를 만든다! 그 데이터의 식별자는 a다!
var a;

변할 수 있는 데이터이니, 선언할 때는 undefined더라도 나중에 다른 값으로 바꿀 수 있다. 숫자를 담았다가 문자열을 담는 등의 방식도 가능하다!

명령을 받은 컴퓨터는 메모리에서 비어있는 공간을 확보하고, 이 공간의 이름(식별자)을 a라고 지정한다. 이게 바로 변수 선언 과정이다!!!! 사용자가 a라는 공간에 접근하려고 하면 담긴 데이터를 반환한다.

데이터 할당

var a; // 컴퓨터: 변수 a를 선언한다!
a = 'abc'; // 컴퓨터: 변수 a에 'abc'라는 데이터를 할당한다!

var a = 'abc'; // 변수 a를 선언하고 변수 a에 'abc'라는 데이터를 할당한다!!!!

변수를 선언하고, 그 다음에 데이터를 할당하는 것이나, 한번에 변수 선언과 할당 과정을 끝내는 것이나 결국 동일하다고 볼 수 있다. 그런데! 앞서 말한 공간 a에 문자열 'abc'가 저장되는 것은 아니다. 'abc'라는 데이터를 저장하기 위한 메모리 공간을 또 다시 확보해서 'abc'를 저장하고 그 주소를 변수 영역에 저장하는 방식이다.

쉽게 말해, 식별자 a를 변수호텔 103호에 저장하고 변수 a에 문자열 'abc'를 할당하게 되면 'abc'는 데이터호텔 6003호에 저장된다. 변수호텔에서 a를 찾으면 데이터호텔 6003호에서 값을 찾으라는 쪽지가 있는 것!!

이렇게 변수 영역에 값을 직접 대입하지 않고, 굳이 한단계를 더 거치는 이유는, 나중에 데이터 변환을 할 경우에 컴퓨터가 처리할 연산을 줄여 효율적인 관리를 하기 위해서이다. 만약 나중에 'abc' 대신에 '자바스크립트'라는 완전히 다른 문자열이 할당된다 해도, 굳이 데이터 호텔 6003호에 사는 'abc'를 쫓아내지 않고도, 6004호에 '자바스크립트'를 넣고, 변수 호텔 103호의 쪽지의 호수만 수정하면 되는 것이다.

다른 예로 변수 호텔 101호부터 999호까지 모두가 숫자 1을 할당받았다고 하면, 1이 담긴 수백개의 주소를 만들 필요 없이, 데이터호텔 6005호에 1을 넣고 각 호텔에 6005호라는 동일한 쪽지만 두면 되는 것이다.

기본형 데이터와 참조형 데이터

불변값

변수와 상수를 구분하는 성질은 '변경 가능성'이다. 여기서 불변값상수는 혼동하기 쉬운데 구분되는 개념이라는 것을 기억해야 한다. 변수와 상수를 구분하는 변경 가능성의 대상은 변수 영역 메모리, 불변성 여부를 구분할 때의 변경 가능성의 대상은 데이터 영역의 메모리이다.

위 데이터 할당에서 설명했듯, 불변값인 기본형 데이터는 다른 값으로 변경되지 않고, 새로 만드는 동작으로만 저장할 수 있다. 이것이 바로 불변값의 성질이다. 한번 만들어진 값은 가비지 컬렉팅을 당하지 않는 한 변하지 않는다. (클로저 개념이 여기서 나온거구나..)

가변값

기본형 데이터가 모두 불변값이라면, 참조형 데이터는 모두 가변값일까? 참조형 데이터에는 '객체의 변수(프로퍼티) 영역'이 별도로 존재한다는 점이다. 데이터 영역의 값은 모두 불변값이지만, 객체의 변수에는 다른 값을 대입할 수 있기 때문에 참조형 데이터는 가변값이라고 말하게 되는 것이다. 즉 새로운 객체가 만들어지는 것이 아니라 객체 내부의 값만 바뀌면 되는 것이다!

var obj= { // @6000: (이름: obj, 값: @201) -> 여기는 안바뀐다.
  a: 'mia', // @201: @6001 -> 여기만 바뀌면 된다!
}; // @6001: 'mia'

obj.a = 'lee'; // @6002: 'lee'

중첩 객체 nested object

참조형 데이터의 프로퍼티에 다시 참조형 데이터를 할당하는 경우를 일컫는다.

var obj = {
  x: 1,
  arr: [1, 2, 3],
};

가비지 컬렉팅

자신의 주소를 참조하는 변수가 하나도 없게 되는 경우, 메모리 주소는 가비지 컬렉터의 수거 대상이 된다. 수거된 메모리는 다른 새로운 값을 할당할 수 있는 빈 공간이 된다.

변수 복사 비교

변수를 복사하는 과정은 기본형이나 참조형 모두 같은 주소를 바라보게 된다는 점에서 동일하다. 하지만 데이터 할당 과정에서 차이가 있기 때문에 이후 동작에는 큰 차이가 생긴다.

var a = 1;
var b = a;
var obj1 = {c: 1, d: 'mia'};
var obj2 = obj1;

b = 2; // a !== b
obj2.c = 2; // obj1 === obj2 !!!!!!!!!!!!!

변수 b의 값을 바꾸면 b의 값만 달라져서 a와 다른 주소를 바라보게 되지만, 참조형 데이터를 복사한 변수는 그 프로퍼티 값이 바뀌기 때문에 여전히 같은 객체를 바라보게 된다. 이것이 기본형과 참조형 데이터의 큰 차이점이다. 기본형은 주소값을 복사하는 과정이 한 번만 이루어지고, 참조형은 한 단계를 더 거친다는 차이가 있을 뿐이다.

아래는 같은 조건인 상태에서 각 데이터를 변경했을 경우이다.

var a = 1;
var b = a;
var obj1 = {c: 1, d: 'mia'};
var obj2 = obj1;

b = 2; // a !== b
obj2 = {c: 2, d: 'mia'}; // obj1 !== obj2 !!!!!!!!!!!!!!!!!!

위는 obj2에도 완전히 새로운 객체를 할당하면서 값을 변경하게 된다. 이렇게 되면 새로운 공간에 새로운 객체가 저장이 되고 그 주소가 변수 영역에 저장되게 된다. 이렇게 되면 값이 달라진다.

참조형 데이터가 가변값이라는 것은 참조형 데이터를 직접 변경하는 것이 아니라 그 내부의 프로퍼티를 변경할 때만 성립한다. WOW...

불변 객체

불변 객체는 값으로 전달받은 객체에 변경을 가하더라도 원본은 변하면 아되는 경우에 필요하다.

모든 프로퍼티를 복사하는 함수(얕은 복사)

var copyObject = function(target) {
  var result = {};
  for (var prop in target) {
  	result[prop] = target[prop];
  }
  return result;
}

for in 문법을 이용해 result 객체에 target 객체의 프로퍼티들을 복사하는 함수이다. 위 함수를 이용하여 객체를 복사하면 아래와 같다.

var user = {
  name: 'mia',
  gender: 'female'
}

var user2 = copyObject(user);
user2.name = 'lee';

console.log(user.name, user2.name); // mia lee
console.log(user === user2); // false

얕은 복사는 위에서 설명했듯이 바로 아래 단계의 값만 복사하기 때문에 중첩된 객체에서 참조형 데이터가 저장된 프로퍼티를 복사하면 그 주솟값만 복사하게 된다. 한단계 더 들어간 프로퍼티들은 기존의 데이터를 그대로 복사하는 것이다.

따라서 어떤 객체를 복사할 때, 기본형 데이터일때는 그대로 복사하면 되지만 참조형 데이터의 경우 다시 그 내부의 프로퍼티들을 복사해야 한다.

객체의 깊은 복사를 수행하는 범용 변수

var copyObjectDeep = function(target) {
  var result = {};
  if (typeof target === 'object' && target !== null) {
    for (var prop in target) {
      result[prop] = copyObjectDeep(target[prop]);
    }
  }	else {
    result = target;
  }
  return result;
}

target이 객체인 경우에는 내부 프로퍼티를 순회하며 copyObjectDeep 함수를 재귀적으로 호출하고 아닌 경우에는 target을 그대로 지정한다. 이렇게 깊은 복사를 하면 원본과 사본이 완전히 다른 객체를 참조하게 되어 어떤 프로퍼티를 변경하더라도 다른 쪽에는 영향이 가지 않는다.

JSON을 사용한 깊은 복사

객체를 JSON 문법으로 표현된 문자열로 전환했다가 다시 JSON 객체로 바꾸는 방법이다. 리퀘스트로 받은 데이터를 저장한 객체를 복사할 때와 같을때 사용하면 좋은 방법이다.

var copyObjectViaJSON = function(target) {
  return JSON.parse(JSON.stringify(target));
};
var obj = {
  a: 1.
  b: {
    c: null,
  	d: [3, 4],
   	func1: function () { console.log(5); }
  },
  func2: function () { console.log(6); }
};
var obj2 = copyObjectViaJSON(obj);

undefined vs null

둘 다 '없음'을 나타낸다. undefined는 어떤 변수에 값이 존재하지 않을 경우를 의미하고 null은 사용자가 명시적으로 '없음'을 표현하기 위해 대입한 값이다!!

profile
🌙`、、`ヽ`ヽ`、、ヽヽ、`、ヽ`ヽ`ヽヽ` ヽ`、`ヽ`、ヽ``、ヽ`ヽ`、ヽヽ`ヽ、ヽ `ヽ、ヽヽ`ヽ`、``ヽ`ヽ、ヽ、ヽ`ヽ`ヽ 、ヽ`ヽ`ヽ、ヽ、ヽ`ヽ`ヽ 、ヽ、ヽ、ヽ``、ヽ`、ヽヽ 🚶‍♀ ヽ``ヽ``、ヽ`、

4개의 댓글

comment-user-thumbnail
2020년 11월 19일

여얼.. 열공하셨네여 좋은 정리 잘보고 갑니다~!

1개의 답글
comment-user-thumbnail
2020년 11월 26일

자바스크립트 마스터 화이팅...!! 💘💘

1개의 답글