3장은 데이터에 관한 장이며, 데이터를 자바 스크립트가 이해할 수 있는 형식으로 바꾸는 법을 배운다. 인간에게 익숙한 형태인 숫자와 텍스트, 날짜 등을 다루기 위해 자바 스크립트에서 제공하는 문법을 데이터 타입이라고 한다. 자바스크립트가 데이터를 보관하는 메커니즘이 되는 변수와 상수, 리터럴에 대해 알아본 후, 데이터 타입을 본격적으로 다루어보자.
3.1 변수와 상수
변수란 간단히 말해 이름이 붙은 값으로, 값은 언제든 바뀔 수 있다. 아래 두 문장은 변수 currentTempC를 선언하는 동시에 초깃값을 할당한다. ES6에서 let이라는 키워드가 새로 생겼고, 이것은 변수 선언에 사용된다. 변수 선언은 각 변수당 한번만 가능하다.
currentTempC = 22;
let currentTempC = 22.5;
초깃값 지정 없이 변수만 선언할 수도 있다. 초깃값을 할당하지 않으면 암시적으로 특별한 값 undefined가 할당된다.
let targetTempC;
let 문 하나에서 변수 여러개를 선언할 수도 있다. targetTempC는 undefined가 할당될 것이고, room1과 room2는 문자열(텍스트) 변수이다.
let targetTempC, room1 = "conference_room_a", room2 = "lobby";
상수는 ES6에서 새로 생긴 문법으로, 변수와 마찬가지로 값을 할당 받을 수 있으나 한 번 할당한 값을 바꿀 수는 없다. consts라는 키워드를 통해 선언할 수 있고, 여러개를 동시에 선언할 수도 있다. 보통 상수 이름을 정할 때에는 대문자와 밑줄만 사용한다.
const ROOM_TEMP_C = 21.5, MAX_TEMP_C = 30;
3.2 변수와 상수 중 어떤 것을 써야할까요?
될 수 있으면 변수보다 상수를 써야한다. 데이터의 값이 아무때나 막 바뀌는 것보다는, 고정된 값이 이해하기 쉽고, 잘못된 값으로 설정되는 오류를 방지할 수 있다. 심지어 여러 개의 값을 가질만한 변수라고 해도 그냥 여러개의 상수로 지정하는 편이 낫다.
그래도 변수를 써야하는 상황이 있을 수 있는데, 예를 들면 배울 루프 제어에는 변수를 써야 한다. 시간이 지나면서 값이 바뀌는 경우에도 변수를 써야 한다.
3.3 식별자 이름
변수와 상수, 함수 이름을 식별자라고 한다. 그리고 식별자에는 규칙이 있다.
- 식별자는 글자나 달러 기호($), 밑줄(_)로 시작해야 한다.
- 식별자에는 글자와 숫자, 달러 기호, 밑줄만 사용할 수 있다.
- 유니코드 문자도 쓸 수 있다.
- 예약어는 식별자로 쓸 수 없다.
다른 언어에서는 달러 기호를 특수 문자로 사용하나 자바스크립트는 식별자로 사용이 가능하다. 제이쿼리는 이를 활용하여 달러 기호 자체를 식별자로 사용한다.
자바스크립트의 식별자 표기법은 여러가지이나, 가장 널리 쓰이는 것은 다음 두가지이다.
- 카멜 케이스(camel case)
중간중간의 대문자가 낙타의 혹처럼 보여서 카멜이라는 이름이 붙었다. - 스네이크 케이스(snake case)
카멜 케이스보다는 조금 덜 쓰인다.
어떤 표기법을 쓰든 간에, 일관성을 위해서 한 가지 표기법만 사용하는 것이 좋다. 또, 다음과 같이 추가적인 방침을 염두에 두는 것이 좋다.
- 식별자는 클래스와 구별하기 위해 첫글자에 대문자를 사용하지 않는다.
- 밑줄로 시작하는 식별자는 아주 특별한 상황 또는 내부 변수에서만 사용한다. 자신만의 특별한 변수 카테고리를 만들지 않는한, 웬만하면 밑줄로 시작하는 식별자는 사용하지 말아야 한다.
- 제이쿼리를 사용할 경우, 달러 기호를 시작하는 식별자는 제이쿼리 객체라는 의미를 갖는다.
3.4 리터럴
리터럴(literal)은 값을 만드는 방법으로, 자바스크립트는 사용자가 제공한 리터럴 값을 받아 데이터를 만든다.
리터럴과 식별자는 엄연히 다른 것으로 자바스크립트에서는 따옴표를 통해 리터럴과 식별자를 구별한다. 식별자는 숫자로 시작할 수 없으므로 숫자 리터럴에는 따옴표가 필요 없다.
다음 예제에서는 conference_room_a라는 식별자가 없기 때문에 오류이다.
let room1 = "conference_room_a";
let currentRoom = room1;
currentRoom = conference_room_a; // 에러
3.5 원시 타입과 객체
자바스크립트의 값은 원시 값(primitive) 또는 객체(object) 두 종류가 있다. 문자열과 숫자 같은 원시 타입은 불변(immutable)이다. 즉, 숫자 5는 항상 숫자 5이고, 문자열 "alpha"도 항상 "alpha"이다. 만약 "alpha" + "omega"처럼 문자열을 병합하면 그 결과는 원래의 문자열과 엄연히 다른 문자열이다. 다만 불변성이라는 말이 변수의 값이 바뀔 수 없다는 뜻은 아니다. 만약 변수의 값을 초깃값과 다른 값으로 지정했다면 그것은 초깃값과 또다른 불변값을 변수에 할당하는 것이다.
원시타입에는 여섯가지가 있다.
- 숫자
- 문자열
- 불리언
- null
- undefined
- 심볼(symbol)
여섯가지 원시 타입 이외에 커스텀 데이터를 만들 때에는 객체를 많이 사용하는데, 자바스크립트에는 다음과 같이 몇 가지 내장된 객체 타입이 있다.
- Array
- Date
- RegExp
- Map과 WeakMap
- Set과 WeakSet
원시 타입 중 숫자와 문자열, 불리언에는 각각 대응하는 객체 타입인 Number, String, Boolean이 있다. 이 객체에는 실제 값이 저장되지는 않으나, 대응하는 원시 값에 추가적인 기능을 제공한다.
3.6 숫자
컴퓨터로는 표현할 수 없거나, 근사치로만 표현할 수 있는 숫자들이 있다. 자바스크립트도 다른 언어와 마찬가지로 실제 숫자의 근사치를 저장할 때 IEEE-764 배정도 부동소수점 숫자 형식을 사용한다. 이 형식을 '더블'이라고 칭한다.
더블 형식의 근사치 결과는 종종 이상한 값을 나타나는 경우가 있는데, 예를 들면 0.1 + 0.2 = 0.30000000000000004를 리턴하는 경우가 있다. 이 결과는 무한한 값을 유한한 메모리 안에서 정확히 짐작하려는 계산 안에서 생긴 결과이다.
자바스크립트에는 단순한 언어이기 때문에, 그리고 고성능 정수 연산이나 정밀한 소수점 연산이 필요한 애플리케이션에서 쓸 수 없도록 하기 위해 숫자형 데이터 타입이 하나 밖에 없다.
자바스크립트는 10진수, 2진수, 8진수, 16진수의 네 가지 숫자형 리터럴을 인식한다. 10진 리터럴에는 소수점 없는 정수, 소수점 있는 10진수, 지수 표기법을 쓸 수가 있다. 그 외에도 무한대, 음의 무한대, '숫자가 아님'을 나타내는 특별한 값들도 존재한다. 이들은 엄밀히는 숫자형 리터럴은 아니고, 일종의 플레이스 홀더이다.
let count = 10; // 더블
const blue = 0x0000ff; // 16진수
const umask = 0o0022; // 8진수
const roomTemp = 21.5; // 10진수
const c = 3.0e6; // 지수 표기법
const inf = Infinity;
const ninf = -Infinity;
const nan = NaN; // '숫자가 아님'
어떤 리터럴 형식을 사용해도 숫자는 더블 형식으로 저장된다. 다양한 리터럴 형식은 숫자를 간편한 형식으로 표현할 수 있도록 제공된 것 뿐이다. 자바스크립트가 표시할 수 있는 숫자 형식에는 제한이 있다.
또한 숫자에 대응하는 Number 객체에는 중요한 숫자형 값에 해당하는 유용한 프로퍼티가 있다.
const small = Number.EPSILON; // 1을 더했을 때 1과 구분하는 결과를 만들 수 있는 가장 작은 값 2.2e-16
const bigInt = Number.MAX_SAFE_INTEGER; // 표현할 수 있는 가장 큰 정수
const max = Number.MAX_VALUE; // 표현할 수 있느 가장 큰 숫자
const minInt = Number.MIN_SAFE_INTEGER; // 표현할 수 있는 가장 작은 정수
const min = Number.MIN_VALUE; // 표현할 수 있는 가장 작은 숫자
const nInf = Number.NEGATIVE_INFINITY // -Infinity
const nan = Number.NaN; // NaN
const inf = Number.POSITIVE_INFINITY // Infinity
3.7 문자열
문자열은 텍스트 데이터이다. string이라는 단어는 string of characters에서 나왔다. 자바스크립트의 문자열은 유니코드 텍스트이다. 유니코드는 텍스트 데이터에 관한 표준이며 사람이 사용하는 언어 대부분의 글자와 심볼에 대항하는 코드 포인트(code point)를 포함한다. 유니코드 자체는 모든 언어의 텍스트를 나타낼 수는 있으나, 유니코드를 사용하는 소프트웨어가 모든 코드 포인트를 정확히 렌더링할 것을 보장하지는 않는다. 따라서 널리 쓰이지 않는 글자나 언어를 사용해야 한다면, 유니코드에서 코드 포인트를 어떻게 렌더링하는지 따로 조사해야 한다.
자바스크립트의 문자열 리터럴에는 작은 따옴표, 큰따옴표, 백틱(backtick)을 사용한다. 백틱은 ES6에서 도입하였고, 곧 설명한 템플릿 문자열(template string)에서 사용한다.
3.7.1 이스케이프
자바스크립트에서는 텍스트 데이터와 프로그램 자체를 구별하기 위해 문자열을 따옴표 안에 작성한다. 하지만 문자열 안에 따옴표를 써야할 경우가 있을 것이다. 이 문제를 해결하려면 문자열 안에서 쓸 따옴표를 이스케이프해서 문자열 주위에 쓰는 부호가 아님을 나타내야 한다.
다음 예제에는 이스케이프가 필요하지 않다. 작은 따옴표와 큰 따옴표가 이미 구별되어 사용되고 있기 때문이다.
const dialog = 'Sam looked up, and said "hello, old friend!", as Max walked in.';
const imperative = "Don't do that!";
그러나 두 가지 따옴표를 모두 써야한다면 문제가 발생한다.
const dialog = "Sam looked up and said "don't do that!" to Max"; // 에러 발생
이럴 경우에는, 역슬래시(\)를 써서 따옴표를 이스케이프하면 문자열이 여기서 끝나지 않는다고 자바스크립트에게 알릴 수가 있다. 앞에서 본 에제를 다음과 같이 고쳐 쓰면 어떤 따옴표라도 쓸 수 있다.
const dialog1 = "He looked up and said \"don't do that\" to Max";
const dialog2 = 'He looked up and said "dont\'t do that" to Max';
그러나 아직 해결되지 않은 문제가 있다. 문자열에서 역슬래시 문자를 써야할 경우이다. 역슬래시는 두 번 사용함으로써 자기 자신을 이스케이프할 수 있다.
const s = "In JavaScript, use \\ as an escape character in strings";
큰 따옴표를 쓸지, 작은 따옴표를 쓸지는 스스로 정하면 된다. 다만 자바스크립트 문자열 안에 HTML 문장을 작성할 때에는 큰 따옴표를 쓸 일이 많으므로 작은 따옴표를 쓰는 것이 좋다.
3.8 특수문자
따옴표 이외에, 줄바꿈 문자처럼 화면에 표시되지 않는 일부 특수문자나 임의의 유니코드 문자를 나타낼 때에도 역슬래시를 사용한다.
코드 | 설명 | 예제 |
\n | 줄바꿈 문자(Newline), ASCII/Unicode 10인 라인피드(line feed) 문자 | "Line1\nLine2" |
\r | 캐리지 리턴(Carriege return) (ASCII/Unicode 13) | "Windows line 1\r\n Windows line2" |
\t | 탭(ASCII/Unicode 9) | "Speed:\t60kph" |
\' | 작은따옴표 | "Dont\'t" |
\` | 백틱, ES6에서 새로 생겼다. | `New in ES6: \`strings.\ |
\$ | 달러 기호, ES6에서 새로 생겼다. | `New in ES6: $(interpolation)` |
\\ | 역슬래시 | "Use \\\\ to represent\\!" |
\uXXXX | 임의의 유니코드 포인트, 여기서 XXXX는 16진수 코드 포인트이다. | "De Morgan's law: \u2310(P \u22c0Q) \u21D4 (\u2310P) \u22c1 (\u2310Q)" |
\xXX | 라틴-1 문자, 여기서 XX는 16진수 라틴-1 코드 포인트이다. | "\xc9p\xe9e is fun, but foil is more fun." |
라틴-1 문자셋은 유니코드의 부분집합이며 라틴-1 문자 \xXX는 유니코드 코드 포인트 \u00XX와 똑같다.
유니코드의 문자 코드를 이스케이프하지 않고, 에디터에서 직접 입력할 수도 있다. 유니코드 문자를 직접 입력하는 방법은 여러가지로 에디터와 운영체제에 따라 다르므로 관련 문서를 참고해야 한다.
자주 쓰이지 않는 특수문자도 다음과 같은 것들이 있다.
코드 | 설명 | 예제 |
\0 | NUL 문자(ASCII/Unicode 0) | "ASCII NUL: \0" |
\v | 세로 탭(Vertical tab) (ASCII/Unicode 11) | "Vertical tab: \v" |
\b | 백스페이스(ASCII/Unicode 8) | "Backspace: \b" |
\f | 폼 피드(Form feed) (ASCII/Unicode 12) | "Form feed: \f" |
3.8.1 템플릿 문자열
값을 문자열 안에 써야할 때에는 문자열 병합(concatenation)을 통해 변수나 상수를 문자열 안에 쓸 수가 있다.
let currentTemp = 19.5;
const message = "The current temperature is " + currentTemp + "\u00B0C";
ES6 이전에는 변수나 상수를 문자열 안에 쓰는 방법은 문자열 병합 뿐이었으나 ES6에서는 문자열 템플릿이라는 기능을 도입했다. 이 기능을 문자열 채우기(interpolation)라 부르기도 한다. 문자열 템플릿은 문자열의 정해진 위치에 값을 채워넣는 문법이다. 문자열 템플릿에는 따옴표가 아닌 백틱을 사용한다.
const message2 = `The current temperature is ${currentTemp}\u00B0C`;
문자열 템플릿 안에서는 달러 기호가 특수문자가 된다. 달러 기호 다음에 중괄호로 감싼 값을 쓰면 그 값이 문자열에 삽입된다. 다만 문자열 템플릿 안에 달러 기호를 쓰기 위해서는 역슬래시로 이스케이프를 해줘야 한다.
3.8.2 여러 줄 문자열
ES6 이전의 여러줄 문자열 지원은 소스 코드의 각 행 마지막에서 줄바꿈 문자를 이스케이프할 수 있도록 정의했으나, 브라우저의 지원이 부족했으므로 이 기능이 잘 사용되지 않았다. ES6에서는 이 기능이 좀 더 발전하였으나 아직까지는 신중히 사용해야 한다. 이 기능을 시험해보려면 자바 스크립트 파일을 만들어야 한다. 따옴표를 사용한 문자열에서 다음과 같이 줄바꿈 문자를 이스케이프할 수 있다.
const multiline = "line1\n" +
"line2\n" +
"line3";
이렇게 하면 코드를 쉽게 읽을 수 있고, 결과 문자열도 원하는 형태로 만들어지낟. 문자열 병합을 사용할 때에는 따옴표와 백틱을 섞어 써도 괜찮다.
const multiline2 = `Current temperature:\n`+
`\t${currentTemp}\u00b0C\n`+
"Don't worry... the heat is on!";
3.8.3 숫자와 문자열
숫자를 따옴표 안에 넣으면 그건 숫자가 아니라 문자열이다. 자바스크립트는 그러나 필요하다면, 숫자가 들어있는 문자열을 자동으로 숫자로 변환한다. 다음 예제에서 문자열을 숫자로 바꾸는 경우와 숫자로 바꾸지 않는 경우를 묘사했다.
const result1 = 3 + '30'; // 3이 문자열로 바껴 결과는 '330'
const result2 = 3 * '30'; // '30'이 숫자로 바껴 결과는 90
그래도 왠만하면 숫자가 필요할 땐 따옴표를 사용하지 말고, 문자열이 필요할 땐 문자열을 쓰자. 모호한 부분은 사용자의 입력을 받을 때이다. 사용자 입력은 대부분 문자열로 들어오므로 숫자가 필요할 때 숫자로 바꾸는 건 개발자가 해야 한다.
3.9 불리언
불리언은 true와 false 두가지 값밖에 없는 데이터 타입이다. C 같은 일부 언어에서는 불리언 대신 숫자를 사용한다. 0은 false이고, 다른 숫자는 모두 true이다. 자바스크립트에서도 모든 값을 참 같은 값(truthy), 거짓 같은 값(falsy)으로 나눌 수가 있다.
불리언을 쓸 생각이라면 따옴표 안에 넣지 않도록 조심하자. 문자열 "false"는 참 같은 값(truthy)이다.
let heating = true;
let cooling = false;
3.10 심볼
심볼은 유일한 토큰을 나타내기 위해 ES6에서 도입한 새 데이터 타입이다. 심볼은 항상 유일하며 어떤 심볼과도 일치하지 않다는 면에서 객체와 유사하다. 그러나 항상 유일하다는 점을 제외하면 원시 값의 특징을 모두 갖고 있으므로 확장성 있는 코드를 만들 수 있다.
심볼은 Symbol() 생성자로 만든다. 원한다면 생성자에 간단한 설명을 추가할 수 있다.
const RED = Symbol("The color of a sunset!");
const ORANGE = Symbol("The color of a sunset!");
RED === ORANGE // false
우연히 다른 식별자와 혼동해서는 아되는 고유한 식별자가 필요하다면 심볼을 사용하는 것이 좋다.
3.11 null과 undefined
null과 undefined는 자바스크립트의 특별한 타입이다. null이 가질 수 있는 값은 null 하나뿐이며 undefined도 undefined 값 하나만 가질 수 있다. 둘 다 존재하지 않음을 뜻하지만 둘은 차이가 있다.
일반적인 규칙을 제시한다면, null은 프로그래머에게 허용되었다면, undefined는 자바스크립트 자체에서 사용한다. 프로그래머도 언제든지 undefined를 사용할 수는 있으나 꼭 필요한 때가 아니면 사용하지 않도록 주의해야 한다. 예를 들면 아직 값이 주어지지 않은 변수의 동작을 고의로 흉내내야 할 때 사용된다. 대부분 변수의 값을 모르거나 적용할 수 없는 경우 null을 사용하는 것이 좋다. 다만 변수를 선언하기만 하고 명시적으로 값을 할당하지 않으면 그 변수에는 기본적으로 undefined가 할당된다.
let currentTemp; // undefined
const targetTemp = null; // null
currentTemp = 19.5;
currentTemp = undefined; // 초기화되지 않음. 권장 X
3.12 객체
원시 타입은 단 하나의 값만 나타낼 수 있고 불변이나, 객체는 여러가지 값이나 복잡한 값을 나타낼 수 있으며 변할 수도 있다. 객체의 본질은 컨테이너이다. 컨테이너는 내용물은 변경될 수 있으나 컨테이너 자체가 바뀌지는 않는다. 값이 바껴도 여전히 같은 객체인 것이다.
객체에도 중괄호를 사용하는 리터럴 문법이 있다. 중괄호는 한 쌍이므로 객체가 어디에서 시작하고 어디에서 끝나는지 나타낼 수가 있다. 빈 객체를 일단 만들어보자.
const obj = {};
객체의 콘텐츠는 프로퍼티(property) 또는 멤버(member)라고 부른다. 프로퍼티는 이름(키)과 값으로 구성된다. 프로퍼티 이름은 반드시 문자열 또는 심볼이어야하며, 값은 어떤 타입이든 상관 없고, 다른 객체여도 괜찮다. obj에는 color 프로퍼티를 추가한다.
obj.color="yellow";
프로퍼티 이름에 유효한 식별자를 써야 멤버 접근 연산자(member access operator(.))를 사용할 수가 있다. 프로퍼티 이름에 유효한 식별자가 아닌 이름을 쓰면 계산된 멤버 접근 연산(computed member access operator([]))를 써야 한다. 프로퍼티 이름이 유효한 식별자여도 계산된 멤버 접근 연산자로 접근을 할 수는 있다.
obj["not an identifier"] = 3;
obj["not an identifier"]; // 3
obj["color"]; // yellow;
심볼 프로퍼티에 접근할 때도 대괄호를 사용한다.
const SIZE = Symbol();
obj[SIZE] = 8;
obj[SIZE];
이제 obj에는 "color"(유효한 식별자 문자열), "not an identifier"(유효한 식별자가 아닌 문자열), SIZE(심볼) 세가지 프로퍼티가 있다.
자바스크립트 콘솔에서는 SIZE를 obj의 프로퍼티로 나열하지 않는다. SIZE가 obj의 프로퍼티인 것은 맞으나, 심볼 프로퍼티는 다르게 처리되며, 기본적으로는 표시하지 않는다. 또한 이 프로퍼티의 키는 SIZE 심볼이지 문자열 "SIZE"가 아니다.
원시 값과 객체의 차이에 대해서 되짚자면, 우리는 변수 obj에 저장된 객체를 수정했지만, obj는 항상 같은 객체를 가리키고 있다. 바뀐 것은 오직 이 객체의 프로퍼티이다.
obj는 빈 객체로 만들었으나, 객체 리터럴 문법에서는 객체를 만드는 동시에 프로퍼티를 만들 수가 있다. 중괄호 안에서 각 프로퍼티를 쉼표로 구분하고, 프로퍼티 이름과 값은 콜론으로 구분한다.
const sam1 = {
name: 'Sam',
age: 4,
};
const sam2 = {name: 'Sam', age: 4};
const sam3 = {
name: 'Sam',
classification: { // 프로퍼티 값도 객체가 될 수 있다.
kingdom: 'Anamalia',
phylum: 'Chordata',
class: 'Mamalia',
order: 'Carnivoria',
family: 'Felidae',
subfamily: 'Felinae',
genus: 'Felis',
species: 'catus',
},
};
이 예제에서 객체 리터럴 문법에 따라 세가지 객체를 만들었다. sam1과 sam2의 프로퍼티는 똑같으나, 둘은 서로 다른 객체이다. 원시 값은 그에 비해 같은 값을 갖는 두 변수는 서로 같은 원시 값을 가리킨다.
sam3의 classification 프로퍼티는 그 자체로 객체이다. sam3의 family의 값에 접근하는 방법은 여러가지이다. 여기서는 큰 따옴표를 썼으나, 작은 따옴표나 백틱을 써도 된다.
sam3.classification.family;
sam3["classification"].family;
sam3.classification["family"];
sam3.classification["family"];
sam3["classification"]["family"];
객체에 함수를 담을 수도 있다. 함수에 대해서는 6장에서 자세히 설명한다. 지금은 함수가 일종의 부속 프로그램이라고 생각하면 된다. sam3에 함수를 추가할 때는 다음과 같이 한다.
sam3.speak = function(){return "Meow!";};
이제 함수 뒤에 괄호를 붙여 함수를 호출할 수가 있다.
sam3.speak();
마지막으로, 객체에서 프로퍼티를 제거할 때는 delete 연산자를 사용한다.
delete sam3.classification;
delete sam3.speak;
3.13 Number, String, Boolean 객체
이 장 초반에서 숫자와 문자열, 불리언에는 각각에 대응하는 객체 타입이 있다고 말했다. 이들 객체에는 두 가지 목적이 있다. 하나는 Number.INFINITY 같은 특별한 값을 저장하는 것이고, 다른 하나는 함수의 형태로 기능을 제공하는 것이다.
const s = "hello";
s.toUpperCase();
이 에제의 s는 마치 객체처럼, 즉 함수 프로퍼티를 가진 것처럼 보인다. 자바스크립트는 일시적인 String 객체를 만들고, 이 임시 객체에는 toUpperCase 함수가 들어있다. 자바스크립트는 함수를 호출하는 즉시 임시 객체를 파괴한다. 객체가 임시로 만들어진다는 사실은 다음과 같이 문자열에 프로퍼티를 할당해 보면 알 수 있다.
s.rating=3;
s.rating; // undifined
이 예제는 마치 문자열 s에 프로퍼티를 할당하는 것처럼 보인다. 사실은 일시적인 String 객체에 프로퍼티를 할당한 것이므로 임시 객체는 즉시 파괴되고 s.rating 은 undfined가 되는 것이다.
3.14 배열
자바 스크립트 배열은 특수한 객체이다. 일반적인 객체와 달리 배열 콘텐츠에는 항상 순서가 있고, 키는 순차적인 숫자이다. 배열은 유용한 메서드를 많이 가진 대단히 강력한 데이터 타입이다.
자바 스크립트 배열은 C언어의 효율적인 배열과 더 강력한 동적 배열, 링크드 리스트를 혼합한 것이다. 자바스크립트 배열에는 다음과 같은 특징이 있다.
- 배열 크기는 고정되지 않는다. 언제든 요소를 추가하거나 제거할 수 있다.
- 요소의 데이터 타입을 가리지 않는다.
- 배열요소는 0부터 시작한다.
배열은 기능이 추가된 특수한 객체이므로 배열에 숫자가 아닌 키나 분수, 음수 등을 키로 쓸 수 있다. 배열은 기능이 추가된 특수한 객체이므로 배열에 숫자가 아닌 키나 분수, 음수 등을 쓸 수는 있지만 이런 행동을 설계 목적에 어긋나므로 피해야 한다.
자바스크립트의 배열 리터럴은 다음과 같이 대괄호 안에 배열 요소를 쉼표로 구분해서 사용한다.
const a1 = [1, 2, 3, 4];
const a2 = [1, 'two', 3, null];
const a3 = [
"What the hammer? What the chain?",
"In What furnace was thy brain?",
"What the anvil? What dread grasp",
"Dard its deadly terrors clasp?"
];
const a4 = [
{ name:"Ruby", hardness: 9 },
{ name:"Diamond", hardness: 10 },
{ name:"Topaz", hardness: 8 },
];
const a5 = [
[1, 3, 5],
[2, 4, 6],
]
배열에는 요소 숫자를 반환하는 length 프로퍼티가 있다.
const arr = ['a', 'b', 'c'];
arr.length // 3
배열 요소에 접근할 때에는 대괄호 안에 요소의 인덱스 숫자를 쓴다.
const arr = ['a', 'b', 'c'];
arr[0]; // 'a'
arr[arr.length - 1]; // 'c'
배열 요소의 값을 덮어쓸 때는 새 값을 할당하면 된다.
const arr = [1, 2, 'c', 4, 5];
arr[2] = 3;
3.15 객체와 배열 마지막의 쉼표
객체와 배열 요소를 여러 행에 나눠썼을 때 마지막에 쉼표를 추가했었다. 이러한 마지막 쉼표를 trailing comma, dangling comma, terminal comma 등으로 부른다.
자바스크립트 문법에서는 마지막 쉼표를 계속 허용했었지만 익스플로러 초기버전에서 마지막 쉼표를 사용하면 에러가 났으므로 쓰지 않는 프로그래머들이 많다. 이 쉼표를 쓰느냐 마느냐는 각 개발자들의 선호에 따라 다르다. 널리 사용되는 자바스크립트 객체 표기법(JSON)에서는 마지막 쉼표를 쓰지 않는다.
3.16 날짜
자바 스크립트의 날짜와 시간은 내장된 Date 객체에서 담당한다.
현재 날짜와 시간을 나타내는 객체를 만들 때에는 new Date()를 사용한다.
const now = new Date();
now;
특정 날짜에 해당하는 객체를 만들 때에는 다음과 같이 해야 한다.
const halloween = new Date(2016, 9, 31);
특정 날짜와 시간에 해당하는 객체를 만들 때에는 다음과 같이 한다.
const halloweenParty = new Date(2016, 9, 31, 19, 0);
날짜 객체를 만들면, 다음과 같이 각 부분을 가져올 수 있다.
halloweenParty.getFullYear(); // 2016
halloweenParty.getMonth(); // 9
halloweenParty.getDate(); // 31
halloweenParty.getDay(); // 1
halloweenParty.getHours(); // 19
halloweenParty.getMinutes(); // 0
halloweenParty,getSeconds(); // 0
halloweenParty.getMilliseconds(); // 0
3.17 정규표현식
정규표현식(regular expression)은 자바스크립트의 부속 언어에 가깝다. 정규표현식은 여러가지 언어에서 일종의 확장으로 제공하며, 문자열에서 필요한 복잡한 검색과 교체 작업을 비교적 단순하게 만든다.
자바스크립트의 정규표현식은 RegExp객체를 사용한다. 슬래시 한 쌍(/.../) 사이에 심볼을 넣는 리터럴 문법도 있다. 정규표현식을 처음 본다면 괴상망측해 보일 것이다.
const email = /\b[a-z0-9._-]+0[a-z_-]+(?:\.[a-z]+)+\b/;
const phone = /(:?+1)?(:?\d{3}\)\sd{3}[\s-]?)\d{3}[\s-]?\d{4}/;
3.18 맵과 셋
ES6에서는 새로운 데이터 타입 Map과 Set, 그리고 그들의 약한 짝인 WeakMap과 WeakSet을 도입했다. 맵은 객체와 마찬가지로 키와 값을 연결하지만 특정상황에서 객체보다 유리한 부분이 있다. 셋은 배열과 비슷하지만, 중복이 허용되지 않는다.
위크맵과 위크셋은 맵과 셋과 마찬가지로 특정상황에서 성능이 더 높아지도록 일부 기능을 제거한 버전이다.
3.19 데이터 타입 변환
데이터 타입을 다른 타입으로 바꾸는 일은 매우 자주 하는 작업이다. 사용자 입력이나 다른 시스템에서 가져온 데이터를 그대로 쓸 수 있는 경우는 별로 없고, 대개 변환해야 한다. 데이터 변환에 자주 쓰이는 테크닉은 다음과 같다.
3.19.1 숫자로 바꾸기
문자열을 숫자로 바꿔야 하는 경우 방법이 몇가지 있다. 첫번째는 Number 객체 생성자를 사용하는 방법이다.
const numStr = "33.3";
const num = Number(numStr); // 이 행은 숫자 값을 만들며, Number 객체의 인스턴스를 만드는 것이 아니다.
숫자로 바꿀 수 없는 문자열에서는 NaN이 반환된다.
두 번째 방법은 내장 함수인 parseInt나 parseFloat 함수를 사용하는 방법이다. 이들은 Number 생성자와 달리, parseInt를 사용할 때는 기수(radix)를 넘길 수 있다. 기수는 변환할 문자열이 몇 진수 표현인지 알려줄 수가 있다. 예를 들어 16진수를 변환할 때에는 기수로 16을 넘기면 된다. 기본값은 10이므로 10진수 표현을 변환할 때에는 기수를 쓰지 않아도 되지만, 항상 기수를 명시하길 권한다. parseInt와 parseFloat는 모두 숫자로 판단할 수 있는 부분까지만 변환하고, 그 뒤에 있는 무자열은 무시한다. parseFloat은 항상 기수가 10이라고 가정한다.
const a = parseInt("16 volts", 10); // 16
const b = parseInt("3a", 16); // 58
const c = parseFloat("15.5 kph"); // 15.5
Date 객체를 숫자로 바꿀 때에는 valueOf() 메서드를 사용한다. 이 숫자는 UTC 1970년 1월 1일 자정으로부터 몇 밀리초가 지났는지 나타내는 숫자이다.
const d = new Date();
const ts = d.valueOf();
불리언 값을 1(true)나 0(false)로 바꿀 때에는 조건 연산자를 사용한다.
const b = true;
const n = b ? 1 : 0;
3.19.2 문자열로 변환
자바 스크립트의 모든 객체에는 문자열 표현을 반환하는 toString() 메서드가 있다. 이 메서드는 문자열 병합에서 자동으로 숫자를 문자열로 변환하므로, 이 메서드를 통해 숫자를 문자열로 직접 바꿀 일을 많지 않지만, 어떤 경우에서든 toString() 메서드는 잘 동작하며 상식적인 결과를 반환한다.
const n = 33.5;
n; // 33.5
const s = n.toString();
s; // "33.5"
Date 객체의 toString() 메서드는 좀 길긴 하지만, 쓸만한 결과를 반환한다. 하지만 대부분의 객체는 문자열 [object Object]를 반환하므로 객체의 toString() 메서드를 수정해서 더 유용한 표현을 반환할 수 있도록 하는 것이 좋다. 한편, 배열의 toString()은 각 요소를 문자열로 바꾼 다음 쉼표로 연결한 문자열을 반환한다.
const arr = [1, true, "hello"];
arr.toString(); // "1,true,hello"
3.19.3 불리언으로 변환
간단히는, 부정 연산자(!)를 써서 모든 값을 불리언으로 바꿀 수가 있다. 부정 연산자를 한번 사용하면 참같은 값은 false로 바뀐다. 부정 연산자를 한번 더 쓰면 true를 얻을 수 있다. 숫자형 변환과 마찬가지로 Boolean 생성자를 써도(여기서 new 키워드는 사용하지 않는다.) 결과는 같다.
const n = 0;
const b1 = !!n;
const b2 = Boolean(n);
참조형과 원시형
원시 값은 불변이고 원시 값을 복사/전달할 때는 값 자체를 복사/전달한다. 따라서 원본의 값이 바뀌더라도 사본의 값이 따라서 바뀌지는 않는다.
let a = 1;
let b = a;
a = 2;
console.log(b) // 1
값 자체를 복사했으므로 변수와 값은 일치한다.
a === 2 // true
값 자체를 전달하므로 함수 안에서 변수의 값이 바뀌더라도 함수 외부에서는 바뀌지 않은 상태로 남는다.
a = 3;
change(a);
console.log(a); // 3
그러나 객체는 가변이고, 객체를 복사/전달히면 객체가 아니라 객체를 가리키는 참조를 복사/전달하므로, 원본이 바뀌면 사본도 함께 바뀐다. 이런 특징을 강조하고 싶을 때 객체를 참조 타입이라고 부르기도 한다.
let o = {a:1};
let p = o;
o.a = 2;
console.log(p); // {a:2}
그러나 다음의 예제의 결과는 다르다.
let o = {a: 1};
let p = o;
p === o; // true
o = {a: 2};
p === o; // false
console.log(p) // {a:1}
객체를 가리키는 변수는 그 객체를 가리키고 있을 뿐, 그 객체 자체는 아니므로 변수와 객체는 일치하지 않는다.
let q = {a: 1};
q === {a: 1} // false;
참조를 전달하므로 함수 안에서 객체를 변경하면 함수 외부에서도 바뀐다.
function change_o (o) {
o.a = 999;
}
let o = {a: 1};
change_o(o);
console.log(o) // {a: 999}
'Front-end > JavaScript' 카테고리의 다른 글
러닝 자바스크립트 4장 : 제어문 (0) | 2020.11.17 |
---|---|
러닝 자바스크립트 2장 : 자바스크립트 개발 도구 (0) | 2020.10.31 |
러닝 자바스크립트 1장 : 첫번째 애플리케이션 (0) | 2020.10.31 |