Skip to content

언어의 기초

Title
언어의 기초
Category
JavaScript
Tags
Aliases
언어의 기초
Created
4 years ago
Updated
last year

JavaScript 언어가 가장 기초적인 수준에서 어떻게 동작하는지를 알아본다. 이러한 것들에는 문법과 연산자, 데이터 타입, 내장된 기능 등이 있다.

문법

대소문자 구분

어디서든 대소문자를 구분한다는 것을 기억하자. 변수나 함수 이름, 연산자 모두 대소문자를 구분한다. testTest 는 다른 변수이고, typeof 는 키워드로 함수 이름에 쓸 수 없지만, typeOf 는 유효한 함수 이름이다.

식별자

'식별자'란 변수나 함수, 프로퍼티, 함수 매개변수의 이름이다. 식별자는 다음 형식에따라야한다.

  • 첫 번째 문자는 반드시 글자나 밑줄( _ ), 달러 기호( $ ) 중 하나여야 한다.

  • 다른 문자에는 글자나 밑줄, 달러 기호, 숫자를 자유롭게 쓸 수 있다.

글자에는 확장 ASCII나 유니코드 문자를 쓸 수 있으나 권장하지 않는다.

ECMAScript 식별자는 관습적으로 카멜 케이스로 쓴다. firstSecond , myCar , doSomethingImportant ...

javascript
var myVar // o
var _myVar // o
var $myVar // o
var 1myVar // 첫 번째 문자에는 숫자가 올 수 없다.
var myVar // o
var _myVar // o
var $myVar // o
var 1myVar // 첫 번째 문자에는 숫자가 올 수 없다.

주석

ECMAScript는 한 줄 주석과 블록 주석 모두 C 언어 스타일로 표기한다.

javascript
// 한 줄 주석
// 한 줄 주석
javascript
/*
 * 여러 줄 주석을 쓸 때는
 * 가독성을 높이기 위하여
 * 이러한 형식을 사용한다.
 */
/*
 * 여러 줄 주석을 쓸 때는
 * 가독성을 높이기 위하여
 * 이러한 형식을 사용한다.
 */

스트릭트 모드

스트릭트 모드는 기존과는 다른 방식으로 JavaScript를 파싱하고 실행하라고 지시하는것으로 안전하지 않은 동작에 대해 에러를 반환하도록 한다. 전체 스크립트에 스트릭트 모드를 적용하려면 다음 문장을 스크립트 맨 위에 추가한다.

"use strict";

함수 단 하나만 스트릭트 모드로 선언하려면 함수 본문 맨 앞에 추가한다.

javascript
function doSomething() {
	'use strict';
	// 함수 본문
}
function doSomething() {
	'use strict';
	// 함수 본문
}

문장

ECMAScript에서 각 문장은 세미콜론으로 종료한다. 세미콜론을 생략할 수도 있는데, 생략하면 자바스크립트 엔진에서 문장이 끝나는 위치를 판단한다.

javascript
var sum = a + b;
var diff = a - b;
var sum = a + b;
var diff = a - b;

이 책에서는 세미콜론을 쓰는 것을 권장한다. 세미콜론을 쓰는 습관을 들이면 타이핑하다가 멈췄던 부분을 깜빡 잊는 등의 에러를 쉽게 발견할 수 있고, 여분의 공백을 제거해서 코드를 압축할 수도 있으며, 또한 문장을 세미콜론으로 끝내지 않을 경우 압축했을 때 문법 에러가 발생할 수 있기 때문이다.

하지만 자바스크립트에서 세미콜론에 관한 의견이 갈리는 것 같다. 자세한 내용은 아래의 글을 참조한다.

🔗 자바스크립트, 세미콜론을 써야 하나 말아야 하나 | 박연오

또한 C 언어 스타일처럼 여러 문장을 코드 블록으로 합칠 수 있고, if 문 같은 제어문에서 실행하는 문장이 하나뿐일 때는 코드 블록을 생략할 수도 있다. 하지만 문장 하나만 실행하더라도 항상 코드 블록을 쓰길 권장한다.

javascript
if (test) {
	test = false;
	alert(test);
}

if (test) alert(test); // 문장이 하나뿐이라면 코드 블록을 생략할 수 있다. 하지만 권장하지 않는다.
if (test) {
	test = false;
	alert(test);
}

if (test) alert(test); // 문장이 하나뿐이라면 코드 블록을 생략할 수 있다. 하지만 권장하지 않는다.

키워드와 예약어

ECMAScript에는 제어문의 시작과 끝을 나타내거나 특정한 조작에 쓸 목적으로 정의되어 있는 키워드들이 있다. ( break , case , continue ... ) 또한 아직 특별한쓰임새가 없지만 미래에 키워드로 쓸 가능성이 있어 예약해 둔 예약어들도 있다. 이러한 키워드와 예약어는 식별자나 프로퍼티 이름에 쓸 수 없으므로 유의한다.

🔗 JavaScript Reserved Words

변수

ECMAScript는 느슨한 변수 타입을 사용하여, 변수 하나에 어떤 타입의 데이터라도 저장할 수 있다. 변수를 정의할 때는 var 키워드를 사용한다. 또한 변수를 선언하는동시에 값을 할당할 수 있다.

javascript
var name;
var message = 'Hello';
var name;
var message = 'Hello';

message 변수에 문자열 "Hello" 를 저장했지만, 이렇게 초기화했다고 해서 변수에문자열 타입을 지정한 것은 아니다. 변수에 저장된 값을 바꿀 수 있을 뿐만 아니라 타입을 바꿀 수도 있다. 하지만 이렇게 변수의 데이터 타입을 바꾸는 행위는 권장하지않는다.

javascript
var message = 'Hello';
message = 100; // 권장하지 않음
var message = 'Hello';
message = 100; // 권장하지 않음

var 연산자는 변수를 로컬 스코프에서 정의한다. 즉 함수 내부에서 var 키워드를사용해 변수를 정의하면 해당 변수는 함수 내부에서만 사용할 수 있고, 함수가 종료되는 즉시 파괴된다.

javascript
function test() {
	var message = 'Hello'; // 로컬 스코프
}
test();
console.log(message); // 에러
function test() {
	var message = 'Hello'; // 로컬 스코프
}
test();
console.log(message); // 에러

그런데 var 연산자를 생략하면 함수 내부에서도 변수를 전역으로 정의할 수 있다. 하지만 가급적 이 패턴을 피하길 권장하는데, 전역 변수를 로컬에서 정의하면 관리하기 어렵고, var 키워드를 의도적으로 생략한 것인지 실수 인지 바로 알 수 없어서혼란을 줄 수 있기 때문이다.

javascript
function test() {
	message = 'Hello'; // 전역 변수
}
test();
console.log(message); // "Hello"
function test() {
	message = 'Hello'; // 전역 변수
}
test();
console.log(message); // "Hello"

변수를 여러 개 선언할 때 쉼표로 구분하여 한 문장에서 선언할 수 있다.

javascript
var message = 'hi',
	found = false,
	age = 29;
var message = 'hi',
	found = false,
	age = 29;

데이터 타입

최신 ECMAScript에는 여섯가지 기본 타입(Primitive type)과 한가지 참조 타입 (Reference type)의 7개의 데이터 타입이 정의되어 있다.

데이터 타입설명타입
Booleantrue , false 두 가지 값을 가질 수 있는 논리 타입['기본 타입']
Null딱 한가지 값 null 만을 가질 수 있는 타입['기본 타입']
Undefined값을 할당하지 않은 변수, undefined 값을 가지는 타입['기본 타입']
Number숫자 자료형['기본 타입']
String문자열 자료형['기본 타입']
Symbol (ECMAScript 6에 추가됨)변경 불가능한(immutable) 원시 타입['기본 타입']
Object식별자로 참조할 수 있는 메모리에 있는 값['참조 타입']

Symbol 타입은 ES6에서 추가된 타입으로 이 책에서는 다루지 않는다. Symbol 타입에대해 자세히 알아보려면 아래의 링크를 참조하자.

🔗 Symbol | PoiemaWeb

typeof 연산자

ECMAScript는 느슨한 타입을 채택했으므로 변수의 데이터 타입을 알아내야 할 때는 typeof 연산자를 사용해야 한다. 변수에 typeof 연산자를 적용하면 다음 문자열 중 하나를 반환한다.

  • 정의되지 않은 변수 : "undefined"

  • 불리언 : "boolean"

  • 문자열 : "string"

  • 숫자 : "number"

  • 함수를 제외한 객체 또는 null : "object"

  • 함수 : "function"

typeof 연산자는 다음과 같이 사용한다.

javascript
var message = 'some string';
console.log(typeof message); // string
console.log(typeof message); // string
var message = 'some string';
console.log(typeof message); // string
console.log(typeof message); // string

typeof 는 함수가 아니라 연산자이므로 괄호는 쓰지 않아도 된다.

몇 가지 typeof의 결과들이다.

javascript
typeof ''; // string
typeof 1; // number
typeof NaN; // number
typeof true; // boolean
typeof []; // object
typeof {}; // object
typeof new String(); // object
typeof new Date(); // object
typeof /test/gi; // object
typeof function () {}; // function
typeof undefined; // undefined
typeof null; // object (설계적 결함)
typeof undeclared; // undefined (설계적 결함)
typeof ''; // string
typeof 1; // number
typeof NaN; // number
typeof true; // boolean
typeof []; // object
typeof {}; // object
typeof new String(); // object
typeof new Date(); // object
typeof /test/gi; // object
typeof function () {}; // function
typeof undefined; // undefined
typeof null; // object (설계적 결함)
typeof undeclared; // undefined (설계적 결함)

typeof null === 'object'; 의 결과가 혼란스러울 수 있다. JavaScript의 첫 번째버전에서 변수의 값은 32비트 단위로 저장되었는데, 1-3비트는 타입 태그로 변수의 타입에 대해 저장했고 나머지 비트에 실제 데이터를 저장했다. 그런데 이 때 객체의 타입태그가 000 이었고, null 은 대부분의 플랫폼에서 0x00 을 가지는 NULL 포인터였으므로 typeof의 결과가 "object" 로 나오는 것이다.

이에 대해 책에서는 null 이 빈 객체를 참조하는 특별한 값으로 올바른 값이라고 설명했고, 몇몇 글에서는 불행히도 수정될 수 없는 버그 또는 설계적 결함이라고 설명했다. 기본 타입으로 정의된 null 을 타입 체크 연산자 typeof으로 알 수 없다는 것은아무래도 혼란을 초래하는 구현이라고 생각된다.

🔗 typeof - MDN 🔗 "typeof-null"의 역사 - FEDevelopers/tech.description 🔗 Type Checking | PoiemaWeb

undefined

변수를 정의할 때 초기화 하지 않는다면 undefined 가 할당된다.

javascript
var message;
console.log(message == undefined); // true
var message;
console.log(message == undefined); // true

명시적으로 undefined 리터럴을 할당할 수도 있지만 권장하지 않는다.

undefined 를 통해 정의되지 않은 변수와 초기화되지 않은 변수를 구분할 수 있다. 하지만 typeof는 정의되지 않은 변수에도 실행되므로 유의한다.

javascript
var message;
// var age;

console.log(message); // undefined
console.log(age); // error

console.log(typeof message); // undefined
console.log(typeof age); // undefined
var message;
// var age;

console.log(message); // undefined
console.log(age); // error

console.log(typeof message); // undefined
console.log(typeof age); // undefined

따라서 변수를 항상 초기화하는 것을 권장하는데, 항상 초기화한다면, typeof에서 undefined 를 반환했을 때 해당 변수가 초기화되지 않은 것인지 정의되지 않은 것인지 알 수 있다.

Null

Null 타입은 하나의 값 null 을 갖는다. null 은 빈 객체를 가리키는 포인터이다. 변수를 정의 할 때 해당 변수가 객체를 가리키게 할 생각이라면, 해당 변수를 null 로 초기화하고, 객체를 이용할 수 없을 때에는 항상 null 이 오게 해야한다. 그렇게할 때 해당 변수가 객체를 가리키는지 명시적으로 확인할 수 있다.

javascript
if (car != null) {
	// car를 사용하는 코드
}
if (car != null) {
	// car를 사용하는 코드
}

undefinednull 에서 파생하여 표면적으로는 동일한 것으로 정의되어 있어 == 연산자를 사용하면 true 를 반환한다. 하지만 이는 == 연산자가 암시적 형변환을 하기 때문이고, === 연산자를 사용하면 false 를 반환한다.

javascript
console.log(null == undefined); // true
console.log(null === undefined); // false
console.log(null == undefined); // true
console.log(null === undefined); // false

Boolean

Boolean 타입은 truefalse 두 가지 리터럴 값을 가지는 논리 타입이다. 리터럴은 대소문자를 구분하기 때문에 TrueFalse는 Boolean 타입이 아니다.

ECMAScript에서는 모든 타입을 Boolean 값으로 표현할 수 있는데, Boolean() 함수를호출하거나, if 조건문에 넣으면 해당 타입을 Boolean으로 바꾼다.

데이터 타입true로 변환되는 값false로 변환되는 값
Booleantruefalse
String비어 있지 않은 모든 문자열"" (빈 문자열)
Number0이 아닌 모든 숫자, 무한대 포함0 , -0 , 0n , NaN
Undefined해당 없음undefined
Objectnull 이 아닌 모든 객체null

Number

ECMAScript는 IEEE-754 형식의 정수, 부동소수점 숫자 표준을 사용한다.

기본적인 숫자 리터럴 형식은 10진수이고 8진수, 16진수 리터럴 역시 사용할 수 있다. 8진수 리터럴은 첫 번째 숫자가 0, 그 뒤에는 0부터 7까지의 숫자가 와야하고, 벗어나는 숫자가 있다면 처음의 0을 무시하고 10진법으로 취급한다. (스트릭트 모드에서는 8 진법 리터럴을 허용하지 않고 문법 에러를 반환한다.)

16진수 리터럴은 첫 두 문자에 0x을 쓰고 16진수 숫자를 쓴다. 대소문자는 구별하지않는다.

javascript
var intNum = 55; // 10진수 정수

var octNum1 = 070; // 56의 8진법 표기
var octNum2 = 079; // 10진법 79로 간주함
var octNum3 = 08; // 10진법 8로 간주함

var hexNum1 = 0xa; // 10의 16진법 표기
var hexNum2 = 0x1f; // 31의 16진법 표기
var hexNum3 = 0x1f; // 31의 16진법 표기 (대소문자 구별하지 않음)
var intNum = 55; // 10진수 정수

var octNum1 = 070; // 56의 8진법 표기
var octNum2 = 079; // 10진법 79로 간주함
var octNum3 = 08; // 10진법 8로 간주함

var hexNum1 = 0xa; // 10의 16진법 표기
var hexNum2 = 0x1f; // 31의 16진법 표기
var hexNum3 = 0x1f; // 31의 16진법 표기 (대소문자 구별하지 않음)

부동소수점 숫자를 표현하려면 반드시 소수점이 있어야 하며, 소수점 뒤에 숫자를 쓴다. 부동소수점 숫자는 정수보다 메모리를 두 배 소모하므로 ECMAScript에서는 가능한한 정수로 변환하여 저장하려 한다.

javascript
var floatNum1 = 1.1;
var floatNum2 = 0.1;
var floatNum3 = 1; // 소수점 뒤에 숫자가 없으므로 1로 간주
var floatNum4 = 10.0; // 소수점 뒤가 0이므로 10으로 간주
var floatNum1 = 1.1;
var floatNum2 = 0.1;
var floatNum3 = 1; // 소수점 뒤에 숫자가 없으므로 1로 간주
var floatNum4 = 10.0; // 소수점 뒤가 0이므로 10으로 간주

대단히 크거나 작은 부동소수점 숫자를 표현할 때는 'e-표기법(지수 표기법)'을 쓴다. 지수 표기법은 e 앞의 숫자에 10을 e 뒤의 숫자만큼 곱하는 표기법이다. e 는 대소문자를 가리지 않는다.

javascript
var floatNum = 3.125e7; // 31,250,000
var floatNum = 3.125e7; // 31,250,000

ECMAScript는 소수점 뒤에 0이 6개 이상 있는 모든 부동소수점 숫자를 지수 표기법으로 변환한다. 부동소수점 숫자는 소수점 아래 17자리까지 정확하기는 하지만 사칙 연산에 있어서는 부정확하다. 예를 들어 0.1과 0.2를 더하면 0.3이 아니라 0.30000000000000004를 반환한다. 이런 에러 때문에 부동소수점 숫자를 평가하기 어렵다.

☝️ 이 문제는 IEEE-754 표준의 문제이고 ECMAScript에만 이런 버그가 있는 것은 아니다.

ECMAScript에는 표현할 수 있는 제일 작은 양의 숫자(최솟값이 아님)와 최댓값이 Number 객체의 MIN_VALUE, MAX_VALUE 프로퍼티에 저장되어 있다. MAX_VALUE를 넘는 숫자인 경우 양수는 Infinity로 변환되며 음수는 -Infinity로 변환된다. MIN_VALUE 이하의 숫자는 0 또는 -0 으로 변환된다.

숫자형 값 중에는 NaN(Not a Number)라는 값이 있는데, 이 값은 숫자를 반환할 것으로의도한 조작이 실패했을 때 반환하는 값이다. 예를 들어 어떤 숫자를 0으로 나누려 하면 다른 프로그래밍 언어에서는 일반적으로 에러를 반환하지만, ECMAScript에서는 NaN 을 반환하고 나머지 처리는 계속 수행한다.

다음은 NaN의 몇 가지 특징이다.

  • NaN이 포함된 조작은 항상 NaN이다. (NaN / 10 = NaN)

  • NaN은 어떤 값과도 일치하지 않으며 NaN 끼리도 일치하지 않는다.

때문에 ECMAScript에는 isNaN() 이라는 함수가 있고 이 함수는 매개변수로 들어온값이 '숫자가 아닌 값'인지 검사한다.

javascript
isNaN(NaN); // true
isNaN(undefined); // true
isNaN({}); // true

isNaN(true); // false
isNaN(null); // false
isNaN(37); // false

// strings
isNaN('37'); // false: "37" is converted to the number 37 which is not NaN
isNaN('37.37'); // false: "37.37" is converted to the number 37.37 which is not NaN
isNaN('37,5'); // true
isNaN('123ABC'); // true:  parseInt("123ABC") is 123 but Number("123ABC") is NaN
isNaN(''); // false: the empty string is converted to 0 which is not NaN
isNaN(' '); // false: a string with spaces is converted to 0 which is not NaN

// dates
isNaN(new Date()); // false
isNaN(new Date().toString()); // true

// This is a false positive and the reason why isNaN is not entirely reliable
isNaN('blabla'); // true: "blabla" is converted to a number.
// Parsing this as a number fails and returns NaN
isNaN(NaN); // true
isNaN(undefined); // true
isNaN({}); // true

isNaN(true); // false
isNaN(null); // false
isNaN(37); // false

// strings
isNaN('37'); // false: "37" is converted to the number 37 which is not NaN
isNaN('37.37'); // false: "37.37" is converted to the number 37.37 which is not NaN
isNaN('37,5'); // true
isNaN('123ABC'); // true:  parseInt("123ABC") is 123 but Number("123ABC") is NaN
isNaN(''); // false: the empty string is converted to 0 which is not NaN
isNaN(' '); // false: a string with spaces is converted to 0 which is not NaN

// dates
isNaN(new Date()); // false
isNaN(new Date().toString()); // true

// This is a false positive and the reason why isNaN is not entirely reliable
isNaN('blabla'); // true: "blabla" is converted to a number.
// Parsing this as a number fails and returns NaN

☝️ ES6에서는 Number.inNaN() 함수를 포함하고 이 방법이 숫자 x 에 대해 NaN 인지테스트하는 더 믿을 수 있는 방법이다.

숫자가 아닌 값을 숫자로 바꾸는 함수는 Number() , parseInt() , parseFloat() 함수 세 가지이다. Number() 함수는 모든 데이터 타입에 쓸 수 있으며, 다른 두 함수는 문자열을 숫자로 바꾸는데 사용한다.

Number() 함수 규칙

  • Boolean 타입 truefalse 는 각각 1과 0으로 반환

  • 숫자는 그대로 반환.

  • null 은 0

  • undefinedNaN

  • 문자열을 넘겼다면

    • 문자열이 숫자만으로 구성되었다면 리딩 제로를 모두 버리고 나머지를 10진수로변환. 문자열 맨 앞의 +- 기호가 있었다면 보존.

    • "1.1"과 같은 유효한 부동소수점 형식이라면 해당하는 부동소수점 숫자 반환

    • "0xf" 같은 유효한 16진수 형식이라면 그에 해당하는 정수를 반환

    • 빈 문자열이라면 0을 반환

    • 앞에서 설명한 형식에 맞지 않으면 NaN 을 반환

  • 객체를 넘기면 valueOf() 메소드 반환 값에 대해 Number() 함수 규칙 적용, 결과가 NaN 이면 toString() 메소드 호출 후 문자열 변환 규칙 적용.

javascript
var num1 = Number('Hello world'); // NaN
var num2 = Number(''); // 0
var num3 = Number('000011'); // 11
var num4 = Number(true); // 1
var num1 = Number('Hello world'); // NaN
var num2 = Number(''); // 0
var num3 = Number('000011'); // 11
var num4 = Number(true); // 1

parseInt()

Number() 함수로 문자열을 숫자로 바꾸려면 복잡한 규칙을 기억해야 하므로 정수 형태의 문자열을 숫자로 바꿀 때는 보통 parseInt() 함수를 쓴다. parseInt() 함수는 다음과 같이 동작한다.

  • 리딩 스페이스를 전부 버린다.

  • 리딩 스페이스를 버린 결과의 첫 문자가 숫자나 + , - 기호가 아니라면 NaN 을 반환한다.

  • 리딩 스페이스를 버린 결과의 첫 문자가 숫자나 + , - 기호라면 숫자가 아닌 문자를 만날 때까지 변환을 진행한다.

    • parseInt() 함수는 10진수, 16진수를 인식하고 그에 맞게 변환한다. (ES5 부터 parseInt 는 8진수 문자열을 해석하지 않는다.)
javascript
parseInt('1234blue'); // 1234
parseInt(''); // NaN
parseInt('0xA'); // 10
parseInt('-0xA'); // -10
parseInt('22.5'); // 22
parseInt('1234blue'); // 1234
parseInt(''); // NaN
parseInt('0xA'); // 10
parseInt('-0xA'); // -10
parseInt('22.5'); // 22

두 번째 매개변수로 파싱하려는 값의 형식을 지정할 수 있다. 이렇게 하면 16진수, 8 진수 리터럴을 사용하지 않아도 해당 형식으로 파싱한다.

☝️ 진법 매개변수를 입력하지 않으면 브라우저에서 의도와 다르게 해석할 수 있으므로 늘 입력하는 것이 좋다.

javascript
parseInt('10', 2); // 2
parseInt('10', 8); // 8
parseInt('10', 10); // 10
parseInt('10', 16); // 16
parseInt('10', 2); // 2
parseInt('10', 8); // 8
parseInt('10', 10); // 10
parseInt('10', 16); // 16

parseFloat()

parseFloat() 함수는 0번째 위치부터 각 문자를 변환한다는 점에서 parseInt() 와마찬가지이다. parseFloat() 함수는 문자열의 잘못된 부동소수점 숫자를 만날 때까지 파싱을 계속한다. parseInt() 와의 또 다른 차이는 리딩 제로를 항상 무시하여, 16진수 리터럴을 항상 0으로 해석한다. (진법 매개변수를 넘겨도 무시한다) 그리고 문자열에 소수점이 없거나 소수점 뒤에 0만 있다면 parseFloat() 는 정수를 반환한다.

javascript
parseFloat('1234blue'); // 1234
parseFloat('0xA'); // 0
parseFloat('22.5'); // 22.5
parseFloat('22.34.5'); // 22.34
parseFloat('0908.5'); // 908.5
parseFloat('3.125e7'); // 31250000
parseFloat('1234blue'); // 1234
parseFloat('0xA'); // 0
parseFloat('22.5'); // 22.5
parseFloat('22.34.5'); // 22.34
parseFloat('0908.5'); // 908.5
parseFloat('3.125e7'); // 31250000

String

문자열 데이터 타입은 16비트 유니코드 문자의 연속으로 큰따옴표( " )나 작은따옴표( ' )로 감싸서 표현한다. 큰따옴표로 감싼 문자열과 작은따옴표로 감싼 문자열은완전히 똑같다. 큰따옴표로 시작한 문자열은 반드시 큰따옴표로 끝나야 하며, 작은따옴표로 시작한 문자열은 반드시 작은따옴표로 끝내야 한다.

문자열 데이터 타입에서 입력할 수 있는 문자 리터럴은 다음과 같다.

리터럴의미
\n줄바꿈
\t
\b백스페이스
\r캐리지 리턴
\f폼 피드
\\역슬래시
\'작은따옴표 - 작은따옴표로 감싼 문자열 안에서 작은따옴표를 써야할 때 사용
\"큰따옴표 - 큰따옴표로 감싼 문자열 안에서 큰따옴표를 써야할 때 사용
\xnn16진수 코드 'nn'으로 표현한 문자. n은 0부터 f까지의 16진수.
\unnnn16진수 코드 'nnnn'으로 표현한 문자. n은 0부터 f까지의 16진수.

이 문자 리터럴은 문자열 속 어디에든 쓸 수 있고, 한 문자로 취급된다.

javascript
var text = 'This is the letter sigma: \\u03a3.';
var text = 'This is the letter sigma: \\u03a3.';

문자열의 길이는 length 프로퍼티를 통해 얻을 수 있다. 이 프로퍼티는 문자열에 포함된 16비트 문자의 개수를 반환하는데, 문자열에 32비트 문자가 들어있다면 length 프로퍼티가 문자열 길이를 정확히 반환하지 못할 수도 있다.

javascript
text.length; // 28
text.length; // 28

ECMAScript의 문자열은 불변(immutable)이다. 즉 문자열이 일단 만들어 지면 그 값을바꿀 수 없다. 변수에 저장한 문자열을 바꾸려면 기존의 문자열 대신 새 문자열을 해당 변수에 할당해 주어야 한다.

javascript
var lang = 'Java';
lang = lang + 'Script';
var lang = 'Java';
lang = lang + 'Script';

위 예제는 단순히 lang 에 담긴 "Java" 문자열 뒤에 "Script"를 붙이는 것으로 생각되지만, 내부에서는 10글자를 저장할 수 있는 문자열을 먼저 만들고, "Java" 와 "Script"를 채워 "JavaScript"를 만든 다음, 필요 없어진 기존의 "Java"와 "Script"를파괴하는 방식으로 이루어진다.

문자열 변환

값을 문자열로 바꾸기 위해 toString() 를 사용할 수 있다.

javascript
var age = 11;
var ageAsString = age.toString(); // 문자열 "11"
var found = true;
var foundAsString = found.toString(); // 문자열 "true"
var age = 11;
var ageAsString = age.toString(); // 문자열 "11"
var found = true;
var foundAsString = found.toString(); // 문자열 "true"

toString() 메서드는 Number, Boolean, Object, String 에 존재하며, Null, Undefined 에는 이 메서드가 존재하지 않는다. Number에서 toString() 메서드에 진법을 나타내는 숫자를 매개변수로 사용할 수 있다.

javascript
var num = 10;
console.log(num.toString()); // "10"
console.log(num.toString(2)); // "1010"
console.log(num.toString(8)); // "12"
console.log(num.toString(10)); // "10"
console.log(num.toString(16)); // "a"
var num = 10;
console.log(num.toString()); // "10"
console.log(num.toString(2)); // "1010"
console.log(num.toString(8)); // "12"
console.log(num.toString(10)); // "10"
console.log(num.toString(16)); // "a"

String() 함수는 toString() 메서드를 호출할 값이 null 이나 undefined 일가능성이 있을 때 사용한다. String() 함수는 다음 규칙을 따른다.

  • 값에 toString() 메서드가 존재한다면 이를 매개변수 없이 호출하여 그 결과를 반환한다.

  • 값이 null 이면 "null" 을 반환한다.

  • 값이 undefined 이면 "undefined"를 반환한다.

javascript
var value1 = 10;
var value2 = true;
var value3 = null;
var value4;

console.log(String(value1)); // "10"
console.log(String(value2)); // "true"
console.log(String(value3)); // "null"
console.log(String(value4)); // "undefined"
var value1 = 10;
var value2 = true;
var value3 = null;
var value4;

console.log(String(value1)); // "10"
console.log(String(value2)); // "true"
console.log(String(value3)); // "null"
console.log(String(value4)); // "undefined"

Object

객체는 데이터와 기능의 집합이다. 객체는 new 연산자 다음에 새로 만들 객체 타입의 이름을 써서 만들 수 있다. 생성자에 매개변수를 넘기지 않는다면 괄호를 생략해도되지만, 권장하지는 않는다.

javascript
var o1 = new Object();
var o2 = new Object(); // 유효하지만 권장하지 않는다.
var o1 = new Object();
var o2 = new Object(); // 유효하지만 권장하지 않는다.

Object 타입의 인스턴스는 Object 타입의 프로퍼티와 메서드를 전부 상속한다. Object 인스턴스는 다음 프로퍼티와 메서드를 가진다.

  • constructor : 해당 객체를 만드는데 쓰인 함수. 위에서는 Object() 함수가 생성자이다.

  • hasOwnProperty(propertyName) : 해당 프로퍼티가 객체 인스턴스에 고유하며, 프로토타입에서 상속하지 않았음을 확인한다. propertyName 은 반드시 문자열이어야한다.

  • isPrototypeOf(object) : 해당 객체가 다른 객체의 프로토타입인지 확인한다.

  • propertyIsEnumerable(propertyName) : 해당 프로퍼티를 for-in 문에서 나열할 수있는지 확인한다.

  • toLocaleString() : 객체를 지역에 맞게 표현한 문자열을 반환한다.

  • toString() : 객체를 문자열로 변환해 반환한다.

  • valueOf() : 객체를 나타내는 문자열이나 숫자, Boolean을 반환한다. toString() 과 같은 값을 반환할 때가 많다.

☝️ 이는 ECMA-262에서 정의하는 객체의 명세인데, 이를 자바스크립트 객체에 모두적용할 필요는 없다. 브라우저 환경에서 존재하는 객체는 호스트 객체이며, 호스트구현에서 정의하는 대로 만들어진다. 호스트 객체는 ECMA-262를 따르지 않아도 되므로 Object를 상속하지 않아도 된다.

연산자

단항 연산자

단 하나의 값에만 적용되는 연산자

증감 연산자

++age , age++ 처럼 피연산자 앞이나 뒤에 써서 해당 변수에 1을 증감하는 연산자이다. 앞에 쓰면 문장을 평가하기 전에 변수의 값을 바꾸고, 뒤에 쓰면 문장을 평가한다음에 변수의 값을 바꾼다. 증감 연산자는 정수 뿐만아니라 문자열이나 Boolean, 부동소수점, 객체에도 쓸 수 있다.

  • 유효한 숫자 형태의 문자열에 적용하면 문자열을 숫자로 바꾼 후 증감하고 해당 변수를 숫자로 바꾼다.

  • 유효한 숫자 형태의 문자열이 아니라면 변수의 값은 NaN 이 된다.

  • false 인 Boolean에 적용하면 해당 값을 0으로 바꾼 후 증감하고 해당 변수를 숫자로 바꾼다.

  • true 인 Boolean에 적용하면 해당 값을 1로 바꾼 후 증감하고 해당 변수를 숫자로바꾼다.

  • 부동소수점에 적용하면 그대로 증감한다.

  • 객체에 적용하면 해당 객체의 valueOf 메서드를 먼저 호출해서 증감을 적용할 값을 얻는다. 결과가 NaN 이라면 toString() 메서드를 호출한 후 규칙을 다시 적용한다. 해당 변수는 객체에서 숫자로 바뀐다.

javascript
var s1 = '2';
var s2 = 'z';
var b = false;
var f = 1.1;
var o = {
	valueOf: function () {
		return -1;
	},
};

s1++;
s2++;
b++;
f--;
o--;

console.log(s1, s2, b, f, o); // 3 NaN 1 0.10000000000000009 -2
var s1 = '2';
var s2 = 'z';
var b = false;
var f = 1.1;
var o = {
	valueOf: function () {
		return -1;
	},
};

s1++;
s2++;
b++;
f--;
o--;

console.log(s1, s2, b, f, o); // 3 NaN 1 0.10000000000000009 -2

단항 플러스와 단항 마이너스

단항 플러스는 숫자형 값에 대해 아무 효력을 지니지 않는다. (부호 값을 그대로 유지 ) 단항 마이너스는 숫자형 값의 부호를 바꾸는 용도로 사용한다. 그런데 단항 플러스와 마이너스를 숫자가 아닌 값에 적용하면 형 변환 함수인 Number() 와 마찬가지로동작하고, 그 이후에 부호를 적용 한다.

비트 연산자

ECMAScript에서 모든 숫자는 64비트 형식으로 저장되는데, 비트 연산자는 이러한 64비트 표현을 직접 조작하는 대신 32비트 정수로 변환하여 비트 연산자를 적용한 후 결과를 다시 64비트로 바꾼다. 부호가 있는 정수는 32비트 중 맨 앞의 비트로 부호를 나타내고 나머지 31비트로 숫자의 값을 나타낸다. 예를 들어 18은 00000000000000000000000000010010이다.

음수는 '2의 보수' 형식을 사용한다. 2의 보수는 다음의 세 단계를 통해 계산한다.

  • 절대 값의 2진 표현을 구한다. 예를 들어 -18을 찾으려면 먼저 18의 이진 표현을 구한다.

  • 그 이진 표현의 모든 0을 1로 바꾸고 1은 모두 0으로 바꾼다.

  • 결과에 1을 더한다.

이렇게 얻은 -18의 이진 표현은 11111111111111111111111111101110 이다.

음수 2진 문자열로 얻으려고 하면 절대값의 2진 표현에 마이너스 부호를 붙인 값을 얻는다.

javascript
var num = -18;
console.log(num.toString(2)); // "-10010"
var num = -18;
console.log(num.toString(2)); // "-10010"

그리고 비트 연산에서는 숫자 타입의 특별한 값인 NaNInfinity 를 0과 마찬가지로 취급한다.

또한 숫자가 아닌 값에 비트 연산자를 적용하면 먼저 값에 Number() 함수를 적용해숫자로 바꾼 다음 비트 연산을 적용한다. 결과 값은 숫자가 된다.

| 연산자 | 사용방법 | 설명 | | ----------------------- | -------- | ---------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | | 비트 AND | a & b | 피연산자를 비트로 바꿨을 때 각각 대응하는 비트가 모두 1이면 그 비트값에 1을 반환 | | 비트 OR | a | b | 피연산자를 비트로 바꿨을 때 각각 대응하는 비트가 모두 1이거나 한 쪽이 1이면 1을 반환 | | 비트 XOR | a ^ b | 피연산자를 비트로 바꿨을 때 대응하는 비트가 서로 다르면 1을 반환 | | 비트 NOT | ~ a | 피연산자의 반전된 값을 반환 | | 왼쪽 시프트 | a << b | a 를 b (< 32) 비트만큼 왼쪽으로 시프트, 맨 오른쪽 비트는 0으로 채워짐 | | 부호 유지 오른쪽 시프트 | a >> b | a 를 b (< 32) 비트만큼 오른쪽으로 시프트, 맨 왼쪽 비트는 부호대로 채워진다 | | 부호 버림 오른쪽 시프트 | a >>> b | a 를 b (< 32) 비트만큼 오른쪽으로 시프트, 맨 왼쪽 비트는 0으로 채워짐 (부호가 유지되지 않는다) |

논리 연산자

논리 연산자는 보통 Boolean 타입의 값과 함께 쓰인다. 그런데 Boolean 외에 다른 값과 함께 사용하면 Boolean이 아닌 것을 반환할 수 있다.

| 연산자 | 구문 | 설명 | | --------------- | -------------- | ----------------------------------------------------------------------------------------------- | ----- | --- | ----- | --------------------------------------------------------------------------------------- | | 논리 AND ( && ) | expr1 && expr2 | expr1 을 true 로 변환할 수 있는 경우 expr2 을 반환하고, 그렇지 않으면 expr1 을 반환합니다. | | 논리 OR ( | | ) | expr1 | | expr2 | expr1 을 true 로 변환할 수 있으면 expr1 을 반환하고, 그렇지 않으면 expr2 를 반환합니다. | | 논리 NOT ( ! ) | !expr | 단일 피연산자를 true 로 변환할 수 있으면 false 를 반환합니다. 그렇지 않으면 true 를 반환합니다. |

truefalse 로 변환될 수 있는 다른 데이터 타입의 값들에 대해서는 위에서 살펴보았다. 논리 NOT 연산자를 사용하는 예제를 통해 복습해본다.

javascript
console.log(!false); // true
console.log(!'blue'); // false
console.log(!0); // true
console.log(!NaN); // true
console.log(!''); // true
console.log(!12345); // false
console.log(!false); // true
console.log(!'blue'); // false
console.log(!0); // true
console.log(!NaN); // true
console.log(!''); // true
console.log(!12345); // false

이렇게 논리 NOT 연산자는 먼저 피연산자를 Boolean 값으로 변환한 다음 결과를 부정한다. 따라서 NOT 연산자를 두 번 쓰는 것으로 Boolean() 함수를 쓴 것과 마찬가지효과를 낼 수 있다.

단락 평가(short-circuit)

논리 연산자를 사용한 논리 표현식의 경우, 좌측부터 평가하면서 첫 번째 피연산자를평가한 순간 이미 연산자의 결과가 정해진다면 뒤의 피연산자를 평가하지 않는다. 이를 단락 평가라고 하며, 이렇게 함으로 평가 중 발생해야할 부작용이 나타나지 않는다 .

  • (거짓 표현식) && expr 는 거짓 표현식으로, expr 를 평가하지 않는다.

  • (참 표현식) || expr 는 참 표현식으로, expr 를 평가하지 않는다.

곱셈 관련 연산자

ECMAScript에는 곱셈, 나눗셈, 나머지의 세 가지 곱셈 관련 연산자가 있다. 이 연산자들은 C, Java 같은 언어의 연산자와 비슷하게 동작하지만 숫자가 아닌 값에 적용할 때자동으로 피연산자의 타입을 바꾸는 점이 다르다. 피연산자가 숫자가 아닌 경우, Number() 함수를 이용해 타입을 바꾼다.

곱셈

곱셈 연산자는 아스테리스크( * )를 써서 두 숫자를 곱하는데 쓰인다.

javascript
var result = 34 * 56;
var result = 34 * 56;

하지만 특별한 값에 대해서 다음의 행동을 취한다.

  • 결과가 숫자로 표현할 수 있는 범위를 넘으면 Infinity-Infinity 를 반환한다.

  • 피연산자 중 하나가 NaN 이면 결과도 NaN 이다.

  • Infinity 에 0을 곱하면 결과는 NaN 이다.

  • Infinity 에 0이 아닌 유한한 숫자를 곱하면 결과는 부호에 따라 Infinity 또는 -Infinity 이다.

  • 피연산자 중 하나가 숫자가 아니라면 Number() 를 적용하고 기타 규칙을 적용해숫자로 변환한다.

나눗셈

나눗셈 연산자는 슬래시( / ) 기호로 표현하며 첫 번째 피연산자를 두 번째 피연산자로 나눈다.

javascript
var result = 66 / 11;
var result = 66 / 11;

나눗셈 연산자도 특별한 값에 대해서는 다음의 행동을 취한다.

  • 결과가 숫자로 표현할 수 있는 범위를 넘으면 Infinity-Infinity 를 반환한다.

  • 피연산자 중 하나가 NaN 이면 결과도 NaN 이다.

  • InfinityInfinity 로 나눈 결과는 NaN 이다.

  • 0을 0으로 나눈 결과는 NaN 이다.

  • 0이 아닌 유한한 숫자를 0으로 나눈 결과는 부호에 따라 Infinity 또는 -Infinity 이다.

  • Infinity 에 숫자로 나눈 결과는 결과는 부호에 따라 Infinity 또는 -Infinity 이다.

  • 피연산자 중 하나가 숫자가 아니라면 Number() 를 적용하고 기타 규칙을 적용해숫자로 변환한다.

나머지

나머지 연산자는 퍼센트( % ) 기호로 나타내며 첫 번째 피연산자를 두 번째 피연산자로 나눈 나머지를 반환한다.

javascript
var result = 26 % 5; // 1
var result = 26 % 5; // 1

나머지 연산자도 특별한 값에 대해서는 다음의 행동을 취한다.

  • 두 피연산자가 모두 숫자라면 일반적인 나눗셈을 적용하고 나머지를 반환한다.

  • 좌항이 무한한 숫자이고 우항이 유한한 숫자라면 결과는 NaN 이다.

  • 좌항이 유한한 숫자이고 우항이 0이라면 결과는 NaN 이다.

  • InfinityInfinity 로 나눈 결과는 NaN 이다.

  • 좌항이 유한한 숫자이고 우항이 무한한 숫자라면 결과는 좌항이다.

  • 좌항이 0이고 우항이 0이 아닌 숫자라면 결과는 0이다.

  • 피연산자 중 하나가 숫자가 아니라면 Number() 를 적용하고 기타 규칙을 적용해숫자로 변환한다.

덧셈 관련 연산자

덧셈과 뺄셈 연산자는 단순한 수학 연산자이지만, 문자열간에도 사용할 수 있으므로주의해야 한다.

덧셈

  • 피연산자가 모두 문자열이라면 두 번째 문자열을 첫 번째 문자열에 합친다.

  • 피연산자 중 하나가 문자열이라면 다른 피연산자를 문자열로 변환하고 두 문자열을합친다.

javascript
var result1 = 5 + 5; // 10
var result2 = 5 + '5'; // "55"
var result1 = 5 + 5; // 10
var result2 = 5 + '5'; // "55"

뺄셈

  • 피연산자 중 하나가 String, Boolean, null, undefined 라면 Number() 함수를 호출해 숫자로 변환한 후 뺄셈한다.
javascript
var result1 = '5' - '3'; // 2
var result2 = 7 - '2'; // 5
var result1 = '5' - '3'; // 2
var result2 = 7 - '2'; // 5

관계 연산자

관계 연산자에는 미만( < ), 초과( > ), 이하( <= ), 이상( >= ) 연산자가 있다. 이들은 일반적인 수학 기호와 동일한 방식으로 두 값을 비교한다.

javascript
var result1 = 5 > 3; // true
var result2 = 5 < 3; // false
var result1 = 5 > 3; // true
var result2 = 5 < 3; // false

하지만 ECMAScript에서는 데이터 타입에 따라 변환하여 다음과 같이 동작한다.

  • 피연산자가 모두 숫자이면 숫자형 비교를 한다.

  • 피연산자가 모두 문자열이면, 문자열에서 서로 대응하는 문자의 문자 코드를 비교한다.

  • 피연산자 중 하나가 숫자라면 다른 피연산자를 숫자로 변환한 후 비교한다.

  • 피연산자 중 하나가 객체라면 valueOf() 메서드를 호출하고 이전 규칙에 따라 결과를 비교한다. valueOf 메서드가 없으면 toString() 메서드를 호출하고 이전규칙에 따라 그 값을 비교한다.

  • 피연산자 중 하나가 불리언이라면 숫자로 바꾼 후 비교한다.

관계 연산자의 피연산자가 모두 문자열일 때가 특히 주의를 요하는데, 관계 연산자를문자열에 적용하면 첫 번째 문자열의 문자 코드와 두 번째 문자열의 문자 코드를 대응하며 비교한다. 그런데 대문자의 문자 코드는 소문자의 문자 코드보다 작기 때문에 다음의 상황이 발생한다.

javascript
var result = 'Brick' < 'alphabet'; // true
var result = 'Brick' < 'alphabet'; // true

따라서 두 문자열의 알파벳 순서를 비교하려면 두 문자열의 대소문자를 통일한 다음비교해야 한다.

javascript
var result = 'Brick'.toLowerCase() < 'alphabet'.toLowerCase(); // false
var result = 'Brick'.toLowerCase() < 'alphabet'.toLowerCase(); // false

또한 숫자 형태의 문자열을 비교하는 경우에도 착각하기 쉽다.

javascript
var result = '23' < '3'; // true
var result = '23' < '3'; // true

이 코드는 문자열 "23"을 "3"보다 작다고 평가하는데, 문자열 간에 서로 대응하는 문자 코드끼리("2"와 "3"의 문자 코드) 비교하기 때문이다. 하지만 피연산자 중 하나가숫자라면 다음처럼 상식적인 결과가 나온다.

javascript
var result = '23' < 3; // false
var result = '23' < 3; // false

문자열을 숫자로 변환할 수 없을 때는 문자열이 NaN 이 되는데, NaN 과의 비교는항상 false 이다.

javascript
var result = 'a ' < 3; // "a"는 NaN으로 변환되므로 false
var result = 'a ' < 3; // "a"는 NaN으로 변환되므로 false

동일 연산자

문자열이나 숫자, Boolean 등은 두 값이 동일한지 알아보기가 매우 간단하지만, 객체가 결부되면 복잡해진다. ECMAScript에는 연산자를 두 벌로 분리하여, '동일'과 '비동일' 연산자는 비교하기 전에 타입을 변환하고, '일치'와 '불일치' 연산자는 타입 변환없이 비교하는 것으로 정했다.

동일과 비동일

동일 연산자는 == 이고 비동일 연산자는 != 이다. 두 연산자 모두 피연산자를 비교하기 전에 변환한다.

변환할 때는 다음 규칙을 따른다.

  • 피연산자가 Boolean일 때는 숫자형 값으로 변환한다.

  • 피연산자 하나가 문자열이고 하나가 숫자라면 문자열을 숫자로 바꿀 수 있는지 시도한다.

  • 피연산자 중 하나가 객체이고 다른 하나가 객체가 아니라면, 객체의 valueOf() 메서드를 호출해 원시 값으로 바꾼 후 이전 규칙을 따른다.

비교할 때는 다음 규칙을 따른다.

  • nullundefined 는 동일하다.

  • null , undefined 는 동일 여부를 평가할 때 결코 다른 값으로 변환하지 않는다 .

  • 피연산자 중 하나가 NaN 이라면 동일 연산자는 항상 false 를 반환하며 비동일연산자는 항상 true 를 반환한다. NaNNaN 과도 같지 않다.

  • 두 피연산자가 모두 객체라면 같은 객체인지 비교한다. 두 피연산자가 모두 같은 객체를 가리킨다면 true , 그렇지 않다면 false 를 반환한다.

☝️ 객체끼리 비교할 때는 객체에 대한 참조를 비교한다는 점을 이해해야 한다.

일치와 불일치

일치/불일치 연산자는 동일/비동일 연산자와 같은 일을 하지만 피연산자의 타입을 변환하지 않고 있는 그대로 비교한다. 일치 연산자는 === 이고, 불일치연산자는 !== 이다.

일치/불일치 연산자를 쓰면 데이터 타입을 변환하지 않고 비교하므로 이해하기 쉽고, 데이터 타입을 관리하기 쉬워진다.

3항 연산자

3항 연산자는 다음과 같이 boolean_expression을 평가한 결과에 따라 다른 값을 사용하도록 할 수 있다.

javascript
variable = boolean_expression ? true_value : false_value;
variable = boolean_expression ? true_value : false_value;

할당 연산자

할당은 = 으로 나타내며 단순히 값을 변수에 할당한다.

javascript
var num = 10;
var num = 10;

또한 사칙연산과 다른 연산자를 복합 할당으로 쓸 수 있다.

| 이름 | 단축 연산자 | 의미 | | --------------------------- | ----------- | ------------ | ----- | --- | | 할당 | x = y | x = y | | 덧셈 할당 | x += y | x = x + y | | 뺄셈 할당 | x -= y | x = x - y | | 곱셈 할당 | x *= y | x = x * y | | 나눗셈 할당 | x /= y | x = x / y | | 나머지 연산 할당 | x %= y | x = x % y | | 지수 연산 할당 | x **= y | x = x ** y | | 왼쪽 시프트 할당 | x <<= y | x = x << y | | 오른쪽 시프트 할당 | x >>= y | x = x >> y | | 부호없는 오른쪽 시프트 할당 | x >>>= y | x = x >>> y | | 비트 AND 할당 | x &= y | x = x & y | | 비트 XOR 할당 | x ^= y | x = x ^ y | | 비트 OR 할당 | x | = y | x = x | y |

쉼표 연산자

쉼표 연산자는 여러 문장을 한 문장으로 합칠 때 사용한다.

javascript
var num1 = 1,
	num2 = 2,
	num3 = 3;
var num1 = 1,
	num2 = 2,
	num3 = 3;

제어문

if 문

javascript
if (condition) statement1 else statement2
if (condition) statement1 else statement2

do-while 문

do-while 문은 루프의 종료 조건을 평가하기 전에 루프 본문을 실행한다. 즉 루프 본문은 최소 한 번 이상 실행된다.

javascript
do {
	statement;
} while (expression);
do {
	statement;
} while (expression);

while 문

while 문은 루프 본문을 실행하기 전에 종료 조건을 평가한다.

javascript
while (expression) statement;
while (expression) statement;

for 문

for 문 역시 루프 본문을 실행하기 전에 종료 조건을 평가하는데, 루프에 들어가기 전변수를 초기화하고, 루프 후 코드도 지정할 수 있다.

javascript
for (initialization; expression; post - loop - expression) statement;
for (initialization; expression; post - loop - expression) statement;

for-in 문

for-in 문은 엄격한 반복문으로 객체의 프로퍼티를 나열하는데 사용한다.

javascript
for (property in expression) statement;
for (property in expression) statement;

for-in 문은 객체의 모든 프로퍼티를 나열하는데, ECMAScript에서는 객체 프로퍼티에순서가 없으므로 어떤 순서로 프로퍼티 이름을 반환할 지 예측하 수 없다.

문장 레이블

문장에 레이블을 붙였다가 나중에 사용할 수 있다.

javascript
label: statement;
label: statement;

다음 예제를 참고한다.

javascript
start: for (var i = 0; i < count; i++) {
	console.log(i);
}
start: for (var i = 0; i < count; i++) {
	console.log(i);
}

이 예제에서 쓴 레이블 start를 나중에 break나 continue 문에서 참조할 수 있다.

break 문과 continue 문

break 문은 즉시 루프에서 빠져나가 루프 다음 문장을 실행한다. 반면에 continue 문은 루프를 즉시 빠져나가긴 하지만 루프 실행은 계속된다.

javascript
var num = 0;
for (var i = 1; i < 10; i++) {
	if (i % 5 == 0) {
		break;
	}
	num++;
}
console.log(num); // 4
var num = 0;
for (var i = 1; i < 10; i++) {
	if (i % 5 == 0) {
		break;
	}
	num++;
}
console.log(num); // 4
javascript
var num = 0;
for (var i = 1; i < 10; i++) {
	if (i % 5 == 0) {
		continue;
	}
	num++;
}
console.log(num); // 8
var num = 0;
for (var i = 1; i < 10; i++) {
	if (i % 5 == 0) {
		continue;
	}
	num++;
}
console.log(num); // 8

with 문

with 문은 코드의 스코프를 특정 객체에 고정한다.

javascript
with (expression) statement;
with (expression) statement;

with 문의 의도는 다음과 같이 특정 객체를 코드에서 자주 참조할 때 편리하게 하기위함이다.

javascript
var qs = location.search.substring(1);
var hostName = location.hostname;
var url = location.href;
var qs = location.search.substring(1);
var hostName = location.hostname;
var url = location.href;

이 코드에서 location 객체를 매 행마다 참조했는데 with 문을 쓰면 위 코드를 다음과같이 줄일 수 있다.

javascript
with (location) {
	var qs = search.substring(1);
	var hostName = hostname;
	var url = href;
}
with (location) {
	var qs = search.substring(1);
	var hostName = hostname;
	var url = href;
}

with 문 내부의 변수는 우선 지역 변수로 간주하고, 변수에 접근할 때 지역 변수에서식별자를 찾을 수 없으면 location 객체의 프로퍼티 중에서 해당이름을 찾는다.

하지만 스트릭트 모드에서는 with 문을 금지하며 문법 에러로 간주한다. 또한 with 문은 성능에 악영향이 있으며 디버그하기도 어려워 배포할 최종 코드에 with 문을 사용하는 것은 좋지 않다.

switch 문

switch 문도 다른 언어에서 차용한 제어문으로, 다른 언어의 문법과 비슷하다.

javascript
switch (expression) {
	case value:
		statement;
		break;
	case value:
		statement;
		break;
	case value:
		statement;
		break;
	case value:
		statement;
		break;
	default:
		statement;
}
switch (expression) {
	case value:
		statement;
		break;
	case value:
		statement;
		break;
	case value:
		statement;
		break;
	case value:
		statement;
		break;
	default:
		statement;
}

switch 문의 각 case는 '표현식이 value와 일치하면 statement를 실행하라'는 의미이다. break 키워드를 쓰지 않으 다음 case를 계속 평가한다. default는 case 중에 value로 평가되는 것이 없을 때 실행하는 문장을 가리킨다.

switch 문은 다른 언어에서 차용하긴 했지만 ECMAScript만의 고유한 특징을 갖고 있는데, 먼저 ECMAScript의 switch 문은 모든 데이터 타입에서 동작하므로 문자열과 객체에서도 사용할 수 있다. 또한 값은 상수일 필요가 없으며 변수나 표현식도 쓸 수 있다 .

javascript
switch ('hello world') {
	case 'hello' + ' world':
		console.log('Greeting was found.');
		break;
	case 'goodbye':
		console.log('Closing was found.');
		break;
	default:
		console.log('Unexpected message was found.');
}
switch ('hello world') {
	case 'hello' + ' world':
		console.log('Greeting was found.');
		break;
	case 'goodbye':
		console.log('Closing was found.');
		break;
	default:
		console.log('Unexpected message was found.');
}

이 예제에서는 switch 문에 문자열을 썼다. 첫 번째 case는 문자열을 합치는 표현식인데, 이 결과가 switch 문의 매개변수와 일치하므로 "Greeting was found."가 표시된다 .

javascript
var num = 25;
switch (true) {
  case num < 0:
    console.log("Less than 0.");
    break;
  case num >= 0 && num <= 10;
    console.log("Between 0 and 10.");
    break;
  case num > 10 && num <= 20;
    console.log("Between 10 and 20.");
    break;
  default:
    console.log("More than 20.");
}
var num = 25;
switch (true) {
  case num < 0:
    console.log("Less than 0.");
    break;
  case num >= 0 && num <= 10;
    console.log("Between 0 and 10.");
    break;
  case num > 10 && num <= 20;
    console.log("Between 10 and 20.");
    break;
  default:
    console.log("More than 20.");
}

이 코드에서 num은 switch 문 밖에서 정의되었고, 각 case 문은 Boolean 값을 반환하므로 switch 문의 매개변수 true와 비교하면서 진행한다.

함수

ECMAScript에서 함수는 function 키워드로 정의하며 그 뒤에 매개변수와 함수 본문을 순서대로 쓴다.

javascript
function functionName(arg0, arg1, ..., argN) {
  statements
}
function functionName(arg0, arg1, ..., argN) {
  statements
}

함수는 함수 이름으로 호출하며, 함수에 매개변수를 넘길 땐 괄호안에 넣고, 매개변수가 여러 개라면 쉼표로 구분한다. 모든 함수는 return 문 뒤에 반환할 값을 써서 값을 반환할 수 있는데 모든 함수가 꼭 값을 반환해야 하는 것은 아니다. 함수는 return 문을 만나면 즉시 실행을 멈추고 빠져나오므로 return 문 뒤의 코드는 실행되지 않는다.

함수 하나에 여러 return 문을 쓸 수도 있고, return 문 뒤에 반환 값을 쓰지 않아도 된다. 이렇게 하면 undefined 를 반환한다.

매개변수의 이해

ECMAScript 함수는 매개변수 숫자를 따지지 않으며 데이터 타입도 체크하지 않는다. 즉 함수에서 매개변수를 두 개 받도록 만들어도 매개변수를 한 개, 세 개 또는 아예넘기지 않아도 에러가 발생하지 않는다. 이렇게 되는 이유는 매개변수가 내부적으로배열으로 표현되기 때문이다. 함수는 배열에 어떤 값이 들어있는지 체크하지 않으므로빈배열이 들어와도 상관 없고 더 많이 들어와도 괜찮다. 함수는 arguments 라는 객체를 하나 갖는데, 이 객체를 통해 매개변수의 값에 접근할 수 있다.

arguments[0] 으로 첫 번째 매개변수 , arguments[1] 로 두 번째 매개변수에 접근할 수 있으며 매개변수의 개수는 length 프로퍼티를 통해 알 수 있다. arguments 의 동작이 배열과 유사하지만 Array 의 인스턴스가 아니다. (유사 배열 객체) arguments 객체를 사용하여 매개변수에 명시적인 이름을 정하지 않고 쓸 수 있다.

javascript
function sayHi() {
	console.log('Hello ' + arguments[0] + ',' + arguments[1]);
}
function sayHi() {
	console.log('Hello ' + arguments[0] + ',' + arguments[1]);
}

arguments 객체의 length 프로퍼티를 통해 매개변수가 몇 개 전달되었는지 알 수있고, 이를 활용해 매개변수 개수에 따라 반응하는 함수를 만들 수도 있다.

javascript
function doAdd() {
	if (arguments.length == 1) {
		console.log(arguments[0] + 10);
	} else if (arguments.length == 2) {
		console.log(arguments[0] + arguments[1]);
	}
}
function doAdd() {
	if (arguments.length == 1) {
		console.log(arguments[0] + 10);
	} else if (arguments.length == 2) {
		console.log(arguments[0] + arguments[1]);
	}
}

arguments 객체를 이름 붙은 매개변수와 함께 쓸 수 있는데, 이 때 arguments[0] 은 첫 번째 매개변수와 같은 값이고, arguments[1] 는 두 번째 매개변수와 같은 값으로 둘을 바꿔서 쓸 수 있다. arguments 프로퍼티 값을 바꿀 경우 이에 대응하는이름 붙은 매개변수에서 자동으로 반영된다.

javascript
function doAdd(num1, num2) {
	arguments[1] = 10;
	console.log(arguments[0] + num2);
}
function doAdd(num1, num2) {
	arguments[1] = 10;
	console.log(arguments[0] + num2);
}

이 함수는 항상 두 번째 매개변수의 값을 10으로 바꾸는데 이 때 이름 붙은 매개변수 num2 역시 arguments[1] 에 따라 10이 된다. 이 둘이 같은 메모리 공간을 쓰는 것은 아니다. 또한 이것은 동기화가 아니라 단반향 반영으로 arguments[1] 의 값을 바꾸면 num2 에 반영되지만 num2 를 바꾸면 arguments[1] 에 반영되지 않는다.

오버로딩

ECMAScript 함수는 다른 언어에서 사용하는 오버로딩이 없다. 예를 들어 Java에서는함수 이름이 같더라도 시그니처(매개변수의 타입과 개수)만 다르면 서로 다르게 동작하도록 정의할 수 있는데, JavaScript에서는 매개변수가 그저 배열일 뿐이며 그 값에제한이 없기 때문에, 함수 시그니처가 없다.

따라서 ECMAScript에서는 같은 이름으로 함수를 여러 번 정의하면 마지막 함수가 해당이름을 소유한다. 따라서 진정한 의미의 오버로딩이 불가능하고, 함수에 넘긴 매개변수의 타입과 숫자를 체크해서 그에 맞게 반응하는 방법으로 오버로딩을 흉내낼 수 있다.

요약

자바스크립트의 핵심 기능은 ECMA-262에서 ECMAScript라는 이름의 가상 언어로 정의했다. ECMAScript에는 입출력을 제외하고 기본적인 문법과 연산자, 데이터 타입, 객체등을 정의했다. ECMAScript의 기본 요소는 다음과 같다.

  • ECMAScript의 기본 데이터 타입은 Undefined, Null, Boolean, Number, String 이다.

  • ECMAScript는 정수와 부동소수점 숫자를 구분하지 않는다.

  • ECMAScript는 C 및 C와 비슷한 언어에서 사용하는 기본 연산자인 산술 연산자, 논리연산자, 관계 연산자, 동일(일치) 연산자, 할당 연산자 등을 제공한다.

  • JavaScript의 제어문은 대개 다른 언어에서 차용한 것이며 if 문, for 문, switch 문 등이 이에 해당한다.

ECMAScript의 함수는 다른 언어의 함수와 다르게 동작한다.

  • 함수는 언제든 무슨 값이든 반환할 수 있으므로 함수의 반환 값을 미리 명시할 필요가 없다.

  • 값을 반환하지 않는 함수는 undefined 를 반환한다.

  • ECMAScript의 함수는 시그니처가 없고, 매개변수가 사실 유사 배열 객체이며 어떤값이든 가질 수 있다.

  • 함수에 넘기는 매개변수 숫자에는 제한이 없으며 arguments 객체를 통해 접근할수있다.

  • 함수 시그니처가 없기 때문에 ECMAScript의 함수는 오버로딩이 불가능하다. 매개변수의 종류와 타입에 따라 달리 동작하도록 정의해서 오버로딩을 흉내낼 수는 있다.

참고자료

🔗 자바스크립트, 세미콜론을 써야 하나 말아야 하나 | 박연오

🔗 JavaScript Reserved Words

🔗 Symbol | PoiemaWeb

🔗 typeof

🔗 FEDevelopers/tech.description

🔗 Type Checking | PoiemaWeb

🔗 논리 연산자

Released under the MIT License.