자바스크립트 - 데이터 타입

kdeun1·2021년 6월 13일
1
post-thumbnail

기본 용어

식별자(identifier)

식별자(identifier)는 변수나 함수명 등을 말한다.
자바스크립트에서 식별자는 영문자(대소문자), 숫자, 언더스코어, 달러만 사용가능하다.

키워드(keyword)

키워드는 몇몇 단어들을 특별한 용도로 사용하기 위한 예약어이다.
ex) var, function, if, for 등

변수(variable)

변수는 변경 가능한 데이터를 뜻하여, 데이터가 담길 수 있는 공간이다.


타입

자바스크립트의 타입은 크게 원시 타입(Primitive Type)과 객체 타입(Reference Type)으로 나뉜다.

  • 원시 타입 : Number, String, Boolean, null, undefined, Symbol(ES6), BigInt(ES10)
  • 참조 타입 : 원시 타입 외의 값(Array, Function, Date, RegExp, Map, Set 등)이며, 이름과 값을 갖는 프로퍼티의 집합이다.

원시 타입(Primitive Type)

데이터 복사가 일어날 때 메모리 공간을 새로 확보하여 독립적인 값을 저장한다.
변수에 할당할 때 메모리 상에 고정된 크기로 저장되고, 해당 저장된 값을 변수가 직접적으로 가리킨다.
원시 타입은 불변성(immutable)을 가지고 있어 재할당 시 새로운 메모리에 재할당한 값이 저장되고 변수가 가리키는 메모리가 달라진다.
원시 타입의 값은 accessed by value(값에 의한 접근)의 형태를 가지며, stack memory에 저장된다.

숫자(Number)

자바스크립트는 정수 값과 실수 값을 구분하지 않는다. 자바스크립트에서는 모든 숫자를 IEEE754 표준의 64비트 실수(부동 소수점) 형태로 표현한다.
기본적으로 숫자 리터럴 형식은 10진수이다.

var a = 10; // 10진수로 10
console.log(a); // 10
var b = 012; // 8진수로 12, 10진수로 10
console.log(b); // 10
var c = 0xA; // 16진수로 A, 10진수로 10
console.log(c); // 10

부동소수점 숫자로 인해 자바스크립트는 사칙연산 결과가 정확하지 않다. 만약 소수점 뒤에 숫자가 없다면 해당 숫자는 정수로 취급한다.

var a = 0.1;
var b = 0.2;
console.log(a + b); // 0.30000000000000004

추가적으로 Infinity, -Infinity, NaN(Not A Number)도 존재한다.

문자열(String)

16비트 값들이 연속적으로 나열된 변경 불가능한 값이다. 각 문자열은 유니코드 문자로 표현된다.
자바스크립트에서 문자열은 배열처럼 0 기반의 인덱싱을 사용한다(문자열을 반복문 for로 돌릴 수 있는 이유). 이와 같은 특성을 유사 배열이라고 한다. 문자열도 immutable이므로, 배열의 인덱싱을 이용해 값을 변경하려고 해도 바뀌지 않는다.

var a = 'abc';
console.log(a); // abc
a[0] = 'c';
console.log(a); // abc

만약 값을 바꾸고 싶다면 기존 문자열을 변경하는 것이 아니라 새로운 문자열을 할당해야한다.

var a = 'abc';
console.log(a); // abc
a = 'cbc';
console.log(a); // cbc

기본적으로 문자열 리터럴(literal)을 위해 큰 따옴표("), 작은 따옴표(')를 사용한다(개인적으로 작은 따옴표를 사용한다.). 따옴표를 표현하기 위해서는 이스케이프 시퀀스(\')를 사용해야 한다.
ES6부터는 백틱(`)을 사용하여 템플릿 리터럴로 문자열을 표현할 수 있다. 내부에 placeholder(${})를 사용할 수 있으며, 여러 줄을 쓸 수 있다.

불리언(Boolean)

논리적 참, 거짓을 표현한다. true, false 중 하나의 값이다.
자바스크립트에서는 빈 문자열(' ')이나 null, undefined, 숫자 0을 false로 간주한다.

undefined

자바스크립트에서 '없음'을 표현하는 방식 중 한가지이다.
사용자가 명시적으로 지정할수도 있지만, 자바스크립트 엔진이 자동으로 부여하는 경우도 있다.

변수 선언은 하였지만 값을 대입하지 않은 경우에 메모리 주소가 없는 식별자에 접근하는 경우
객체 내부에 존재하지 않은 속성에 접근하는 경우
명시적인 return문이 없는 경우에는 undefined이 반환되었다고 간주하며, 호출되지 않은 함수를 실행하는 경우에 undefined를 반환한다.

사용자가 undefined를 값으로서 할당할 수 있으며, 식별자에 값을 할당하지 않은 경우 해당 식별자에 접근하는 경우 자바스크립트 엔진이 하는 수 없이 undefined를 반환해주는 경우가 있다.

undefined에 대한 스펙이 헷갈릴 우려가 있으므로, 사용자는 undefined 할당을 하지 않도록 하며 값이 없다는 의미인 null을 사용하여 비어있다는 의미를 표현하면 된다.

null

undefined와 마찬가지로 자바스크립트에서 '없음'을 표현하는 방식 중 한가지이다.
비어있다는 것을 명시적으로 표현할 때 사용한다.
null은 변수를 선언하고 빈 값을 할당한 경우이다. null은 타입이 객체이며, 비어있는 변수이다.
의도적으로 값이 없는건지 빈 값을 할당한 것인지 구분할 수 있다.

null == undefinedtrue이나, null === undefinedfalse이다.
typeof null === object는 자바스크립트 자체 버그이다.

심볼(Symbol)

심볼은 ES6에서 추가된 타입으로 변경 불가능한 원시 타입의 값이다. 주로 이름의 충돌 위험이 없는 유일한 객체의 프로퍼티 키(property key)를 만들기 위해 사용한다.


참조 타입(Object Type, Reference Type)

원시 타입을 제외한 나머지는 참조 타입(객체 타입)이다.
참조 타입은 크기가 정해져 있지 않다. 데이터와 데이터의 동작을 한 단위로 묶어 놓은 타입이다.
값이 직접 해당 변수에 저장될 수 없고, 데이터에 대한 참조(객체의 위치)만 저장된다.
이러한 특징때문에 참조 타입의 데이터는 별도의 메모리 공간인 힙(heap) 영역에 저장되며, 변수에 할당 시 데이터에 대한 주소(heap 메모리의 주소)가 저장된다.
참조 타입은 값을 메모리의 다른 주소에 복사하는 방법이 아니라 그 메모리의 주소를 참조하여 값을 꺼내오는 방식으로 메모리 사용량을 줄인다.
객체는 accessed by reference(참조에 의한 접근) 방식으로 전달되며, heap memory에 저장된다.

객체(Object)

객체는 속성들을 담고 있는 collection이다. 키와 값의 매핑의 구조로 키는 문자열이고 값은 어떤 값도 될 수 있다.

배열(Array)

정수키를 가지는 일련의 값들을 표현하기 위한 객체이다. 리스트나 집합을 표현할 때 적합하다.

Date

시간을 나타낼 때 사용

Map, Set, WeakMap, WeakSet (ES6+)

키가 문자열 뿐만 아니라 객체도 될 수 있다.
Map은 Object를 변형한 것이고, Set은 Array를 변형한 것이다.


원시 타입과 참조 타입의 메모리 구조

자바스크립트 엔진이 자바스크립트를 실행할 때 원시 타입 및 참조 타입을 저장하는 메모리 구조로 콜 스택(call stack)과 힙(heap)을 가진다.

  • 콜 스택(call stack) : 원시타입 값과 함수 호출의 실행 컨텍스트(Execution Context)를 저장하는 곳이다. 정적 데이터를 저장하는데 사용하는 데이터 구조이다.
  • 힙(heap) : 객체, 배열, 함수와 같이 크기가 동적으로 변할 수 있는 참조타입 값을 저장하는 곳이다.

자바스크립트 코드가 실행되지마자, 전역 실행 컨텍스트(global execution context)를 생성한다.
원시타입 값은 콜 스택에, 참조 타입 값은 힙에 저장된다.

원시 타입의 변수 선언과 할당

다음과 같이 코드 예제가 존재한다. 자바스크립트의 모든 변수는 먼저 콜스택을 가리킨다.

var a = 10;
var b = 20;
var c = 'ABC';
var d;


이 때의 간략한 메모리 구조는 다음과 같다. 변수 a를 예를들어 설명해보겠다.
일반적으로 변수 a는 10이다. 라고 말하지만, 더 정확히 말하면 변수 a는 10이라는 값을 보유한 메모리 주소와 같다.

var e = a;


변수 e를 선언하고 a를 할당하였을 때, 변수 a의 메모리 주소를 변수 e에 할당한다. 변수 e가 10을 할당받았을 때, 새로운 주소의 10이 할당될 줄 알았는데 변수에는 콜스택의 주소값이 들어가므로 주소값을 복사받아서 할당하게 된다.
변수 e의 메모리 주소가 같게되서 10이라는 값이 나오게 된다.

e = e + 1;


변수 e에 +1하는 경우 값은 11이 나오게 된다.
연산 결과가 11로 확인되면, 자바스크립트는 메모리에 새로운 주소를 할당한다. 그 메모리의 값으로 11을 저장하고 변수 e는 새로운 메모리 주소를 가리키게 된다.

a = 30;


만일, 변수 a에 30을 재할당한다면, 새로운 메모리에 30을 저장하고 a에 저장된 주소 값을 새로운 주소값으로 교체한다.
더이상 참조되지 않은 데이터는 가비지 컬렉터가 적절한 시점에 메모리에서 해제한다.

자바스크립트의 원시 타입 데이터는 변경불가능(immutable)하기 때문에, 이런 방식으로 동작한다.

참조 타입의 변수 선언과 할당

배열, 객체, 함수 등의 참조 타입은 메모리 힙에 저장된다. 참조타입 데이터가 저장된 메모리힙의 주소 값은 콜스택에 각각 저장된다.

var arr = [1, 2, 3];
var func = function() { ... };
var obj = { key1: 'val1', key2: 'val2' };


메모리 힙의 주소 값은 콜 스택에 저장되고, 변수 arr, func, obj에는 콜 스택의 주소가 저장된다.

arr.push(4);
obj.key2 = 'val3';


메모리 힙에 저장된 배열의 값을 변경하거나, 객체의 속성에 값을 변경한 경우에도 콜스택에 저장된 힙의 주소는 변경되지 않는다.

obj = { key3: 'val3', key4: 'val3' };


새로운 객체를 할당하는 경우, 변수에 새로운 콜스택과 콜스택의 값에는 새로운 객체가 저장된 새로운 힙 주소가 저장된다.

자바스크립트 함수의 매개변수의 평가 방식은 Call By Sharing이다.

function changeStuff(a, b, c){
  a = a * 10;
  b.item = 'changed';
  c = { item: 'changed' };
}
var num = 10;
var obj1 = { item: 'unchanged' };
var obj2 = { item: 'unchanged' };

changeStuff(num, obj1, obj2);

console.log(num); // 10
console.log(obj1.item); // changed
console.log(obj2.item); // unchanged

a는 원시 타입의 전달인자이며, num 변수에 영향을 끼치지 않는 call by value 평가방식 이다.
b, c는 참조 타입의 전달인자이며, 함수 내부의 로직이 b.item = 'changed';c = { item: 'changed' };인 경우의 결과는 다르게 나온다.
전달인자가 힙 영역의 참조주소 값이 전달되며, 이를 공유에 의한 호출 즉, call by sharing라고 한다.


참고

profile
프론트엔드 개발자입니다.

0개의 댓글