6장 데이터 타입
JS의 모든 ‘값’은 무조건 데이터 타입을 가진다.
| 구분 | 데이터 타입 | 설명 |
|---|---|---|
| 숫자 타입 (number) | 숫자 타입(정수, 실수 구분 X) | |
| 문자열 타입 (string) | 문자열 | |
| 원시 타입 | 불리언 타입 (boolean) | true, false |
| undefined 타입 | var 키워드 선언된 변수에 암묵적으로 할당되는 값 | |
| null 타입 | 값이 없다는 것을 의도적으로 명시할 때 사용하는 값 | |
| symbol 타입 | EX6에서 추가된 타입 | |
| 객체 타입 | 객체, 함수, 배열 |
개발자는 자신이 할당한 값이, 명확히 어떤 의미를 가지는지 타입으로 구별해줘야 함
ex) 숫자 1과 문자열 ‘1’은 엄연히 다르다
6-1. 숫자 타입
-
JS는 int, long, float 없이 하나의 타입만 존재한다.
-
64비트 부동소수점 형식을 따른다 → 즉, 모든 값을 실수로 처리한다.
// 모두 number 타입이다 var integer = 10; var double = 10.12; var negative = -20; //숫자 타입은 모두 실수로 처리된다. console.log(1 === 1.0); //true console.log(4 / 2); //2 console.log(3 / 2); //1.5 -
모두 64비트 부동소수점 형식의 2진수로 저장된다 → JS는 모두 해석을 10진수로 한다.
var binary= 0b0100001;//2진수 var octal = 00101; // 8진수 var hex = 0×41; // 16진수 console.log(binary); //65 console.log(octal); //65 console.log(hex); //65 console.log(binary === octal); //true console.log(octal === hex); // true -
숫자 타입의 3가지 특별한 값 표현
- NaN : 산술 연산 불가(not-a-number)
- JS는 대소문자를 구별하기에, 무조건 NaN으로 정확하게 작성해줘야 한다.
- -Infinity : 양의 무한대
- Infinity : 음의 무한대
console.log(10 / 0); //Infinity console.log(10 / -0); //-Infinity console.log(1 * 'String'); //NaN - NaN : 산술 연산 불가(not-a-number)
6-2. 문자열 타입
텍스트 데이터를 나타나는데 사용
-
0개 이상의 16비트 유니코드 문자(UTF-16)의 집합으로 전 세계 대부분의 문자 표현 가능
-
작은따옴표(
’’), 큰 따옴표(””) 또는 백틱(````)으로 텍스트를 감싼다. → 키워드, 식별자와 같은 토큰과 구분하기 위해서 따옴표를 사용한다.//문자열 타입 var string; string = '문자열'; //작은따옴표 string = '문자열'; //큰따옴표 string = `문자열`; //백틱 string = '작은따옴표로 감싼 문자열 내의 "큰따옴표"는 문자열로 인식된다.'; string = "큰따옴표로 감싼 문자열 내의 '작은따옴표'는 문자열로 인식된다."; -
원시 타입이며, 변경 불가능한 값이다! (이후에 더 자세히 알아보자)
6-3. 템플릿 리터럴
ES6부터 새로 도입된 문자열 표기법이다.
- 멀티라인 문자열, 표현식 삽입, 태그드 템플릿 등 편리한 문자열 처리 기능을 제공한다.
- 백틱(````)을 사용해서 표현하는 방법이다 -> 런타임에 일반 문자열로 변환되어 처리된다
var template = `Template literal`; console.log(template); //Template literal
6-3-1. 멀티라인 문자열
-
일반 문자열 방식 : 줄바꿈이 허용되지 않아서, 백슬래시(
\)로 시작하는 이스케이프 시퀀스를 사용해야 한다.// 그냥 개행 추가 -> 오류 var str = 'Hello worrld.'; // SyntaxError: Invalid or unexpected token // 이스케이프 시퀀스 활용 var template = '<ul>\n\t<li><a href="#">Home</a></li>\n</ul>'; console.log(template); -
멀티라인 문자열 방식 : 이스케이프 시퀀스 사용하지 않고도 사용 가능하다
var template = `<ul> <li><a href="#">Home</a></li> </ul>`; console.log(template);//출력 결과 <ul> <li> <a href="#">Home</a> </li> </ul>
6-3-2. 표현식
-
일반 문자열 방식 : 문자열 연산자
+를 사용해 연결 가능하다.-
- : 피연산자 중 하나 이상인 경우에 문자열 연결 연산자로 동작한다
var first = 'Ung-mo'; var last = 'Lee'; // ES5 : 문자열 연결 console.log('My name is ' + first + ' ' + last + '.'); //My name si Ung-mo Lee. -
-
표현식 삽입 방식 : 연산자 없이,
${ }를 활용해 문자열을 연결할 수 있다.-
더 가독성이 좋다!
var first = 'Ung-mo'; var last = 'Lee'; // ES6 : 문자열 연결 console.log(`My name is ${first} ${last}.`); //My name si Ung-mo Lee. -
반드시 템플릿 리터럴 내에서 사용해야 한다.
- 연산이 아닌, 문자열로 취급될 수 있다
console.log(`1 + 2 = ${1 + 2}`); // 1 + 2 = 3 console.log('1 + 2 = ${1 + 2}'); // 1 + 2 = ${1 + 2}
-
6-4. 불리언 타입
true, false 값을 말한다. 그 이외의 값은 없다.
var foo = true;
console.log(foo); // true
foo = false;
console.log(foo); // false6-5. undefined 타입
undefined 타입의 값은 undefined가 유일하다
-
undefined: 값이 할당된 적이 없다는 것을 나타내는 값 ⇒ 값 자체가 없다는 것을 명시하고 싶을 때는null을 사용해야 한다! -
var 키워드로 선언한 변수 : 암묵적으로
undefined로 초기화된다.var foo; console.log(foo); // undefined- 변수 선언에 의해 메모리 공간이 확보된다.
- 처음 할당이 이루어질 때까지 쓰레기 값으로 두지 않고 JS 엔진이
undefined로 초기화한다.
6-6. null 타입
null 타입의 값은 null이 유일하다.
-
null: 값 자체가 없다는 것을 명시하고 싶을 때 사용 -
JS는 대소문자를 구별하기에, 무조건 null로 정확하게 작성해줘야 한다.
-
변수에 null을 할당한다는 의미 : 변수가 이전에 참조하던 값을 더 이상 참조하지 않겠다는 의미 ⇒ 이전에 할당되어 있던 값을 명시적으로 삭제하는 역할이기도 하다
var foo = 'Lee'; // 이전에 참조하던 값을 더이상 참조하지 않는다 // 이전에 참조하던 값을 아무도 참조하지 않는다면 가비지 콜렉션을 수행할 것이다. foo = null;
6-7. symbol 타입
ES6에서 추가된 7번째 타입, 변경 불가능한 원시 타입의 값
⇒ 다른 값과 중복되지 않은 유일무이한 값을 나타낸다.
-
주로 이름이 충돌할 위험이 없는 객체의 유일한 프로퍼티 키를 만들기 위해 사용한다.
-
Symbol 타입의 값은,
Symbol함수를 활용해 생성한다.- 이때 생성된 값은 외부에 노출되지 X, 유일무이
// 심벌 값 생성 var key = Symbol('key'); console.log(typeof key); // symbol // 객체 생성 let obj = {}; // 이름이 충돌할 위험이 없는 유일무이한 값인 심벌을 프로퍼티 키로 사용한다. obj[key] = 'value'; console.log(obj[key]); //value
6-8. 객체 타입
지금까지 살펴본 원시 타입 이외의 것들은 모두 객체 타입이다.
⇒ JS를 이루고 있는 거의 모든 것이 객체로 취급된다. (중요)
6-9. 데이터 타입의 필요성
그렇다면 데이터 타입을 구분하는 것은 왜 필요한 것일까?
- 값을 저장할 때 확보해야 하는 메모리 공간의 크기를 결정하기 위해
- 값을 참조할 때 한 번에 읽어 들여야할 메모리 공간의 크기를 결정하기 위해
- 메모리에서 읽어 들인 2진수를 어떻게 해석할지 결정하기 위해
아래 score에 숫자 값 100을 저장하는 예제를 기준으로 살펴보자.
var score = 100;6-9-1. 데이터 타입에 의한 메모리 공간의 확보와 참조
값을 메모리에 저장하고 참조할 때, 확보할 공간의 크기를 정해 낭비와 손실 없이 저장할 수 있는지 알아야 한다.
ex) 100을 저장할 때 타입 정보를 기준으로 다음과 같은 과정을 거친다.
- 리터럴 100을 숫자 타입의 값으로 해석
- 숫자 타입이기에 8바이트 공간 확보
- 100을 2진수로 저장
- 해당 score 값을 참조하는 경우, JS엔진은 숫자 타입을 인식한다.
- 숫자 타입의 메모리 공간의 크기가 8바이트이기에 8바이트 만큼 읽어들인다.
6-9-2. 데이터 타입에 의한 값의 해석
모든 값은 메모리에 2진수, 즉 비트의 나열로 저장될텐데
읽어 들인 값은 어떻게 알맞은 방법으로 해석하는가? → 데이터 타입에 의해 해석한다.
6-10 동적 타이핑
변수의 타입은 어떻게 결정되는가?
6-10-1. 동적 타입 언어와 정적 타입의 언어
정적 타입 언어(C, java, kotlin, go …)
- 명시적 타입 선언 방식 → 변수의 데이터 타입을 변수를 선언하는 시점, 즉 사전에 선언해야 한다.
// c 변수에는 1바이트 정수 타입의 값 (-128 ~ 127)만 할당할 수 있다.
char c;
// num 변수에는 4바이트 정수 타입의 값 (-2,124,483,648 ~ 2,124,483,647)만 할당할 수 있다.
int num;- 변수의 타입을 변경할 수 없고, 최초로 선언한 타입에 맞는 값만 할당 가능하다.
- 컴파일 시점에 타입 체크를 수행한다.
- 만약 통과하지 못 하면? 에러 발생, 프로그램 실행 자체를 막는다.
- 타입의 일관성을 강제하기에 더욱 안정적인 코드 구현 → 런타임에 발생하는 에러가 줄어든다.
동적 타입 언어 (JS, python, PHP, Ruby …)
-
변수를 선언할 때 타입을 선언하지 않고,
var/let/const라는 키워드를 사용해 선언한다. -
정해진 타입이 아닌, 상황에 따라 자유롭게 값을 할당할 수 있다.
-
즉, 변수의 타입은 값에 따라 동적으로 결정된다.
var foo; console.log(typeof foo); //undefined foo = 3; console.log(typeof foo); //number foo = 'Hello'; console.log(typeof foo); //string foo = true; console.log(typeof foo); //boolean foo = null; console.log(typeof foo); //object foo = Symbol(); //심벌 console.log(typeof foo); //symbol foo = {}; //객체 console.log(typeof foo); //object foo = []; //배열 console.log(typeof foo); //object foo = function () {}; //함수 console.log(typeof foo); //functiontypeof연산자 : 해당 연산자 뒤에 위치한 피연산자의 데이터 타입을 문자열로 반환하는 함수
-
기본적으로 변수는 타입을 갖지 않고, 값은 타입을 가진다.
6-10-2. 동적 타입 언어와 변수
동적 타입 언어는 유연성은 높지만 신뢰성은 떨어진다!
- 동적 타입의 특징 :
- 데이터 타입에 대해 무감각해질 정도로 편리함
- 변수의 타입이 고정되어 있지 않고 동적으로 변하는 동적 타입 언어의 변수는 값의 변경에 의해 타입도 언제든지 변경될 수 있다
- 복잡한 프로그램에서는 변화하는 변수 값을 추적하기 어려울 수 있다.
- 값을 확인하기 전에는 타입을 확신할 수 없다.
- 개발자의 의도와는 상관없이 자바스크립트 엔진에 의해 암묵적으로 타입이 자동으로 변환되기도 한다.
- 즉, 숫자 타입의 변수일 것이라고 예측하지만 사실은 문자열 타입의 변수일 수도 있다는 말
- 변수를 사용할 때 주의할 사항
- 변수는 꼭 필요한 경우에 한해 제한적으로 사용해야 한다.
- 변수의 유효 범위(스코프)는 최대한 좁게 만들어 변수의 부작용을 억제해야 한다.
- 전역 변수는 최대한 사용하지 않도록 한다.
- 변수보다는 상수를 이용해 값의 변경을 억제한다.
- 변수 이름은 변수의 목적이나 의미를 파악할 수 있도록 네이밍한다.
⇒ 동작하는 코드가 아닌, 가독성이 좋은 코드가 좋은 코드다 라는 것을 잊지 말아야 한다!