데이터 타입

JiHyun·2023년 2월 21일
0

JavaScript

목록 보기
8/8
post-thumbnail

데이터 타입은 값의 종류를 말한다. 자바스크립트에는 7개의 타입이 있는데 크게 2가지로 분류된다.

원시 타입객체 타입
number : 숫자
string : 문자열객체, 함수, 배열 등
boolean : true, false
undefined : var키워드로 선언된 변수에 암묵적으로 할당되는 값
null : 값이 없다는 것을 의도적으로 명시할때 사용하는 값
symbol : ES6에서 추가된 타입?

숫자

1과 1.0은 같을까?
자바스크립트에선 정수와 실수를 구분하지 않고 모든 수를 실수로 표현한다. 정수로 표시되는 수끼리 나눠도 실수가 나올 수 있다.
숫자 타입엔 3가지 특별한 값이 있다

  • Infinity : 양의 무한대
  • -Infinity : 음의 무한대
  • NaN : Not a Number, 숫자가 아님
    자바스크립트는 대소문자를 구별하기 때문에 NaN는 꼭 대소문자 구분해서 써줘야 한다.

문자열

문자열은 ''나 "", 백틱으로 텍스트를 구분한다. 그 이유는 키워드나 식별자 같은 토큰과 구분하기 위해서다. 의도적으로 공백을 줄때도 사용한다.
자바스크립트의 문자열은 원시 타입이며, 변경 불가능한 값이다. 문자열이 생성되면 그 문자열을 변경할 수 없다.
+연산자를 사용해 문자열을 연결할 수 있다. 하나 이상의 문자열인 경우 문자열 연결 연산자로 동작하고 그 외의 경우 덧셈 연산자로 동작한다.

const first = 'jihyun'
const last = 'yun'
console.log('My name is ' + first + ' ' + last + '.') // My name is jihyun yun.

하지만 이걸 표현식${}을 통해 간단히 문자열을 삽입할 수 있다.

const first = 'jihyun'
const last = 'yun'
console.log(`My name is ${first} ${last}.`) // My name is jihyun yun.

여기서 표현식은 문자열이 아니더라도 문자열로 변환해서 삽입한다.

undefined

var 키워드로 선언한 변수는 변수 선언후 값이 할당되기 전까지 undefined로 초기화된다. 따라서 값을 할당하지 않은 변수를 참조하면 undefined를 반환한다. 이처럼 undefined는 개발자가 의도적으로 할당한 값이 아니라 자바스크립트 엔진이 변수를 초기화할 때 사용하는 값이다. 개발자가 의도적으로 undefined를 변수에 할당한다면 혼란을 줄 수 있으므로 권장하지 않는다고 한다.

null

프로그래밍 언어에서는 값이 없다는걸 의도적으로 표현하기위해 null을 사용한다. 변수에 null을 할당하면 이전에 참조하던 값을 제거하는 의미도 있다.
함수가 유효한 값을 반환할 수 없는 경우 null을 반환하기도 한다.

const element = document.querySelector('.myClass');

console.log(element) // null

이 처럼 html문서에 myClass 클래스를 갖는 요소가 없다면 null을 반환한다.

symbol

변경불가능한 원시 타입의 값이며 다른 값과 중복되지 않는 유일한 값이다. 주로 객체의 유일한 프로퍼티 키를 만들기 위해 사용한다.
symbol값은 Symbol 함수를 호출하여 생성한다. 생성자 함수로 객체를 생성하는 것과 비슷해보이지만 new연산자를 사용하지 않는다.

const key1 = Symbol('key');
const key2 = Symbol('key');

console.log(typeof key1);  // symbol

console.log(key1 === key2); // false

symbol에 대해 내용을 더 추가할 예정

동적 타이핑

그렇다면 변수도 데이터 타입을 가질까?

  • 정적 타입 (c, java 등)
    변수 선언시 변수에 할당할 수 있는 값의 데이터 타입을 선언해야함(명시적 타입 선언)
    변수의 타입을 변경할 수 없고 변수에 선언한 타입에 맞는 값만 할당 할 수 있다. 컴파일 시점에 타입체크를 수행하여 타입이 맞지않으면 에러를 발생시키고 프로그램 실행을 막는다.

  • 동적 타입 (javascript, python 등)
    변수 선언시 타입을 따로 지정해서 선언하지 않고 키워드를 통해 모든 타입의 변수를 선언한다.(var, let, const) 값을 할당하는 시점에 변수의 타입이 동적으로 결정되고 변수의 타입을 자유롭게(동적으로) 변경할 수 있다.

그래서 변수는 타입을 가질까? 결론적으로 변수는 타입을 갖지않고 변수에 할당된 값이 타입을 갖는다. 변수는 값의 별명이기 때문이다.
동적 타입 언어는 변수에 할당하는 데이터 타입의 값이 자유로워 편하다고 느낄것이다. 하지만 단점도 있다.
변수 값은 언제든지 변경이 가능하고 프로그램이 복잡해질수록 변수 값을 추적하기 어려울 수 있다. 게다가 값을 확인하기 전까진 타입을 확신할 수 없다. 숫자 타입일거라 생각했지만 문자열 타입일경우 프로그램은 오류가 난다. 안정적인 프로그램을 만들기 위해 데이터타입을 체크해야한다.

원시(기본형) 타입 vs 객체(참조형) 타입

원시 타입과 객체 타입을 나누는 이유가 무엇이고 어떤 차이가 있을까?
이 두 타입을 차이를 알기위해선 각 타입의 메모리 동작방식을 알아야한다.

기본형 타입

var a;

//...

a = 'abc';

우선 변수명(식별자) a를 선언하면 컴퓨터는 메모리 안에서 데이터가 담길 공간(메모리 주소)을 미리 확보한다. 여기서 우린 임의의 공간 1003을 확보했다고 가정하자. 확보한 공간에 식별자를 저장한다. 이후 a='abc'라는 코드를 만나면 컴퓨터는 'abc'를 메모리주소(ex.5004)에 저장하고 이 주소를 가진채로 식별자 a가 가리키는 주소로 이동하여 값에 5004를 넣는다.

이후 같은 변수명 a에 'abcdef'를 넣으라고 하면 어떻게 될까? 컴퓨터는 별개의 문자열로 인식하고 임의의 공간 5005에 문자열 'abcdef'를 넣고 이 5005를 들고 다시 a의 주소로 가서 값에 5005를 할당한다.

주소...10021003...
데이터이름: a
값: @5004
-> @5005
주소...50045005...
데이터'abc''abcdef'

기본형 데이터는 데이터를 바꿀때 가리키고 있는 주소를 직접 바꾸는 방식으로 진행된다.

var b = 'abc'

만일 식별자 b에 a와 같은 값이 할당되었다면 b가 가리키는 메모리 주소의 값에는 a와 같은 값의 주소를 저장할 것이다. 이렇게 하면 값의 주소는 재활용하기 때문에 메모리 낭비없이 사용이 가능하다.

주소...10021003...
데이터이름: a이름: b
값: @5004값: @5004

참조형 타입
참조형 데이터는 기본형 데이터에비해 동작이 좀 더 복잡하다.

var obj;

//...

obj = {
  a: 1,
  b: 'bbb'
}

obj라는 객체를 선언하면 컴퓨터는 먼저 선언이 이루어지고 할당이 되는 방식으로 동작한다. 선언이 이루어질때 메모리주소에 임의의 공간 1002를 확보한뒤 거기에 식별자 obj를 저장한다. 그 다음 할당이 이루어질때 메모리 주소 5002에 값을 넣는다. 하지만 메모리 주소에는 값이 하나밖에 들어가지 않기에 또 다른 임의의 공간인 7000번대부터 70nn번까지의 공간을 마련해두고 5002번에 그 주소값을 넣는다. 이제 7000번에 프로퍼티 a를 넣고 비어있는 5003번에 그 값인 1을 넣은뒤 7000번에 다시 가서 값의 주소인 5003번을 넣어준다.
메모리주소 1000번대

주소...10021003...
데이터이름: obj
값: @5002

메모리주소 5000번대

주소...500250035004
데이터@7000~70nn1'bbb'

메모리주소 7000번대

주소...70007001...
데이터이름: a이름: b
값: @5003값: @5004

기본형 데이터는 5002만 할당하면 끝이였지만 참조형 데이터는 이렇게 몇단계를 더 거치게 된다.

obj.a = 2

이렇게 프로퍼티 값이 바뀌게 되면 또 다른 임의의 공간인 5005번에 2를 저장하고 그 값을 a가 있는 7000번으로 찾아가서 값의 주소를 바꾸게 되는 것이다. 하지만 1002번에 있는 값의 주소는 바뀌지 않았다.

그런데 이렇게 값을 직접 1002번에 같이 저장하지 않고 굳이 값의 메모리주소를 따로 만들어서 저장하는 이유는 무엇일까?
만일 식별자와 같은 메모리주소에 값도 같이 저장한다고 가정하자. 식별자 a의 값에 매우 큰 용량의 값이 있고, 그 값과 동일한 값이 또다른 곳 b에 할당되어있다고 생각해보자. 그 두개의 값은 각자의 식별자가 가리키는 주소에 저장되어있다. 이렇게 하면 저장하는데에는 단계가 줄어드니 시간이 매우 단축될 것이다. 하지만 그 두개의 값이 동일한지를 비교했을때 컴퓨터는 이 둘을 이진법으로 바꾼뒤 하나하나 비교를 할것이고 이렇게 비교하는 것만으로도 상당한 비용을 야기할 수 밖에 없다.

하지만 값의 주소를 저장하는 방식은 값이 할당되면 그 값과 동일한 값이 있는지를 비교해서 동일한 값이 있으면 메모리주소를 재활용하고 없다면 새로운 메모리 주소에 저장한다. 이러면 처음 저장할때는 동일성 비교를 위해 시간이 좀 더 걸리겠지만 이후에는 이러한 비교과정이 없기에 전혀 비용을 발생하지 않게된다. 또한 이러한 방식이 메모리를 훨씬 덜 차지하게 된다. 예를들어 아까처럼 매우 큰 용량을 가진 동일한 값이 여러군데 저장되어 있는것 보다 한군데에만 저장되어있고 여러군데에서 재활용한다면 메모리를 더 아낄 수 밖에 없지 않을까?

값을 직접 저장 : 데이터 할당시에는 빠름, 값의 비교에 많은 비용을 차지, 메모리 낭비 심함
값의 주소 저장 : 데이터 할당시에는 느림, 값의 비교에 비용이 들지 않음, 메모리 낭비 최소화

또 한가지 값의 비교에 비용이 전혀 들지 않는다는 말은 같은 값이 오직 하나만 존재한다는 말과 동일하다. 그리고 이것이 곧 불변값을 의미한다. 그렇기때문에 기본형 데이터가 불변값이라고 불리는 것이다.

profile
비전공자의 개발일기📝

0개의 댓글