제어문(control flow statement)이란?
프로그램의 흐름을 제어할 때 사용하는 실행문으로 조건문, 반복문 등이 포함되어 있다.
일반적인 코드는 위에서 아래 방향으로 순차적으로 실행되며 제어문을 사용할 경우 코드의 실행 흐름을 인위적으로 제어할 수 있다.
하지만 코드의 실행 순서가 변경된다는 것은 기존 위에서 아래로 진행되던 직관적인 코드의 흐름을 혼란스럽게 만든다. 따라서 제어문은 코드의 흐름을 이해하기 어렵게 만들어 가독성을 해친다는 단점을 가진다. 그리고 가독성이 안 좋은 코드는 오류를 발생시키는 원인이 된다.
나중에 살펴볼 forEach, map, filter, reduce
같은 고차 함수를 사용한 함수형 프로그래밍 기법에서는 제어문의 사용을 억제하여 복잡성을 해결하기 위해 노력한다.
블록문(block statement/compound statement)이란?
0개 이상의 문을 중괄호로 묶은 것으로, 코드 블록 또는 블록이라고 부르기도 한다.
자바스크립트는 블록문을 하나의 실행 단위로 취급한다.
블록문은 단독으로 사용할 수도 있으나 일반적으로는 제어문이나 함수를 정의할 때 사용한다.
더불어 문의 끝에는 세미콜론(;)을 붙이는 것이 일반적이나 블록문의 경우 언제나 문의 종료를 의미하는 자체 종결성을 갖고 있기 때문에 블록문 끝에는 세미콜론을 붙이지 않는 다는 것에 주의해야 한다.
{
var foo = 10;
}
var x = 1;
if (x < 10) {
x++;
}
function sum(x, y) {
return x + y;
}
조건문(conditional statement)이란?
조건식의 평가 결과에 따라 코드 블록의 실행을 결정하는 문
조건식은 불리언 값으로 평가될 수 있는 표현식이다.
if...else
if...else
문은 주어진 조건식의 평가 결과(논리적 참 또는 거짓)에 따라 실행할 코드 블록을 결정한다.
조건식의 평가 결과가 참일 경우 if
문의 코드 블록이 실행되고, 거짓일 경우 else
문의 코드 블록이 실행된다.
if (조건식) {
// 조건식이 참일 때 실행되는 부분
} else {
// 조건식이 거짓일 때 실행되는 부분
}
만약 조건식을 추가하여 조건에 따라 실행될 코드 블럭을 늘리고 싶다면 else if
를 사용한다.
if (조건식1) {
// 조건식1이 참일 때 실행되는 부분
} else if (조건식2) {
// 조건식가 참일 때 실행되는 부분
} else {
// 조건식이 모두 거짓일 때 실행되는 부분
}
else if
와 else
문은 모두 옵션이기 때문에 경우에 따라 사용할 수도 있고, 사용하지 않을 수도 있다.
if
문과 else
문은 2번 이상 사용할 수 없지만 else if
문은 여러 번 사용할 수 있다.
그리고 코드 블록 내 실행문이 1줄이라면 중괄호를 생략할 수도 있다.
var num = 2;
var kind;
if (num > 0) {
kind = '양수'
} else if (num < 0) {
kind = '음수'
} else {
kind = '0'
}
if (num > 0) kind = '양수'
else if (num < 0) kind = '음수'
else kind = '0'
대부분의 if...else
문은 삼항 조건 연산자로 바꿀 수 있다.
var x = 2;
var result;
if (x % 2) {
result = "홀수"
} else {
result = "짝수"
}
console.log(result); // 짝수
var x = 2;
var result = x % 2 ? '홀수' : '짝수';
console.log(result); // 짝수
만약 경우의 수가 3가지라면, 아래와 같이 바꿀 수 있다.
var num = 2;
var kind = num ? (num > 0 > '양수' : '음수') : '0';
console.log(kind); // 양수
삼항 조건 연산자는 값으로 평가되는 표현식을 만들기 때문에 삼항 조건 연산자에서 표현식은 값처럼 사용할 수 있다. 고로 변수에 할당할 수도 있다.
하지만 if...else
문은 표현식이 아닌 문이기 때문에 값처럼 사용할 수 없어 변수에 할당할 수 없다.
조건에 따라 단순히 값을 결정해야 한다면 삼항 조건 연산자를 사용하는 것이 가독성이 좋지만,
조건에 따라 실행해야 하는 내용이 복잡하여 여러 줄의 실행문을 사용해야 한다면 if...else
를 사용하는 것이 가독성이 더 좋다.
switch
switch
문은 주어진 표현식을 평가하여 그 값과 일치하는 표현식을 가지는 case
문으로 실행 흐름을 옮긴다.
case
문은 상황을 의미하는 표현식을 지정하고 콜론으로 마치며, 그 뒤에 실행문을 위치시킨다.
switch
문의 표현식과 일치하는 case
가 없다면 실행 순서는 default
로 이동한다.
default
문은 선택사항으로, 사용할 수도 있고 아닐 수도 있다.
switch (표현식) {
case 표현식1:
// 표현식1과 일치할 때 실행될 부분
break;
case 표현식2:
// 표현식2와 일치할 때 실행될 부분
break;
default:
// 일치하는 case가 없을 때 실행될 부분
}
if...else
문의 조건식은 불리언 값으로 평가되어야 하지만, switch 문의 표현식은 문자열이나 숫자인 경우가 많다. 다시 말하면, if...else
문은 논리적 참과 거짓으로 실행할 코드 블럭을 결정하고 switch
문은 다양한 상황(case)에 따라 실행할 코드 블록을 결정할 때 사용한다.
case
문 안에서 break
문을 사용하지 않을 때는 코드 블록을 탈출하지 못하기 때문에 다음 실행문으로 흐름이 이어져 계속 실행된다. switch
문이 끝날 때까지 모든 case
를 통과하는 것을 폴 스루(fall through)라고 하는데, 폴 스루가 발생하지 않으려면 break
키워도 잘 작성해줘야 한다.
var month = 11;
var monthName;
switch (month) {
case 1:
monthName = '1월';
break;
case 2:
monthName = '2월';
break;
case 3:
monthName = '3월';
break;
.
.
.
case 12:
monthName = '12월';
break;
default:
monthName = 'Invalid month';
}
default
문은 위치상 switch
문 안에서 가장 마지막에 위치하기 때문에 break
를 생략하는 것이 일반적이다. default
문의 실행이 종료되면 자연스럽게 switch
문을 빠져나가기 때문에 별도의 break
처리가 필요 없다.
break
문을 생략한 폴 스루가 유용한 경우도 있다.
다음은 여러 개의 case
문을 하나의 조건으로 활용하여 윤년인지 판별해 2월의 일수를 계산하는 예제이다.
var year = 2000;
var month = 2;
var days = 0;'
switch (month) {
case 1: case 3: case 5: case 7: case 8: case 10: case 12:
days = 31;
break;
case 4: case 6: case 9: case 11:
days = 30;
break;
case 2:
days = ((year % 4 === 0 && year % 100 !== 0) || (year % 400 === 0) ? 29 : 28;
break;
default:
console.log('Invalid month');
}
console.log(days); // 29
switch
문은 case, default, break
등의 다양한 키워드가 필요하고 폴 스루가 발생하는 등 문법의 복잡도가 높다. 따라서 C 언어를 기반으로 하는 프로그래밍 언어는 대부분 switch 를 지원하지만 파이썬과 같이 지원하지 않는 프로그래밍 언어도 있다.
만약 if...else
문으로 해결할 수 있다면 switch
보다 if...else
를 사용하는 것이 좋다.
하지만 조건문이 많아 switch
를 사용했을 때 가독성이 더 좋다면 switch
를 사용하는 것이 좋다.
반복문(loop statement)이란?
조건식의 평가 결과가 참인 경우 코드 블럭을 실행하는 문
그 후 조건식을 다시 평가하여 여전히 참이면 코드 블록을 다시 실행하며 조건이 거짓일 때까지 반복한다.
for
for
문은 조건식이 거짓으로 평가될 때까지 코드 블록을 반복 실행한다.
for (변수 선언문 또는 할당문; 조건식; 증감식) {
// 조건식이 참일 경우 반복할 실행문
}
for (let i = 0; i < 2; i++) {
console.log(i);
}
// 0
// 1
위 예제의 for
문은 i
변수가 0
으로 초기화된 상태에서 시작하여 i
가 2
보다 작을 때까지 코드 블록을 2번 반복 실행한다.
for
문을 실행하면 가장 먼저 변수 선언문let i = 0
이 실행된다. 변수 선언은 한 번만 실행된다.- 변수 선언문의 실행이 종료되면 조건식이 실행된다. 현재
i
변수의 값은 0이므로 조건식 평가 결과는 참이다.- 조건식 평가 결과가 참이므로 코드 블록이 실행된다.
- 코드 블록의 실행이 종료되면 증감식
i++
가 실행되어i
변수의 값은1
이 된다.- 증감식 실행이 종료되면 다시 조건식이 실행된다. 현재
i
변수의 값은 1이므로 조건식 평가 결과는 참이다.- 조건식 평가 결과가 참이므로 코드 블록이 다시 실행된다.
- 코드 블록의 실행이 종료되면 증감식
i++
가 실행되어i
변수의 값은2
가 된다.- 증감식 실행이 종료되면 다시 조건식이 실행된다. 현재
i
변수의 값은2
이므로 조건식 평가 결과는 거짓이다. 조건식 평가 결과가 거짓이므로for
문의 실행이 종료된다.
for
문의 변수 선언문, 조건식, 증감식은 모두 옵션이므로 반드시 사용할 필요는 없다.
단 어떠한 식도 사용하지 않으면 무한 루프가 되므로 주의하자.
for
문 내에서 for
문을 중첩해 사용할 수 있다. 이것을 중첩 for
문이라고 한다.
for (let i = 1; i <= 6; i++) {
for (let j = 1; j <=6 j++) {
if (i + j === 6) console.log(`[${i}, ${j}]`);
}
}
// [1, 5]
// [2, 4]
// [3, 3]
// [4, 2]
// [5, 1]
while
while
문은 주어진 조건식의 평가 결과가 참일 경우 코드 블록을 계속 반복 실행한다.
for
문은 반복 횟수가 명확할 때 주로 상요하는 반면, while
문은 주로 반복 횟수가 불명확할 때 사용한다.
while
문은 조건문의 평가 결과가 거짓이 되면 코드 블록을 실행하지 않고 종료한다.
만약 조건식의 평가 결과가 불리언 값이 아니면 불리언 값으로 타입을 강제 변환하여 논리적 참과 거짓을 구별한다.
while (조건식) {
// 조건식이 참일 동안 반복할 실행문
}
let count = 0;
while (count < 3) {
console.log(count);
count++;
}
// 0
// 1
// 2
조건식의 평가 결과가 언제나 참일 경우 무한루프가 된다.
무한루프에서 탈출하기 위해서는 코드 블록 내에서 if
문으로 탈출 조건을 만들고 break
문으로 탈출할 수 있다.
// 무한루프
while (true) { ... }
// 무한루프 탈출
let count = 0;
while (true) {
console.log(count);
count++;
if (count === 3) break;
}
// 0
// 1
// 2
do...while
do...while
문은 코드 블록을 먼저 실행한 뒤 조건식을 평가한다.
따라서 조건식의 결과가 참이 아니더라도 무조건 한 번 이상 코드 블록이 실행된다.
do {
// 조건식이 참일 경우 반복할 실행문
} while (조건식)
let count = 0;
do {
console.log(count);
count++;
} while (count < 3);
// 0
// 1
// 2
break
문앞서 switch
와 while
에서 살펴보았듯 break
문은 코드 블록을 탈출하는 역할을 한다.
더 정확히 표현하면 모든 코드 블록을 탈출하는 것이 아닌, 레이블 문, 반복문, switch
문의 코드 블록을 탈출한다. 이외 코드 블록에서 break
문을 사용할 경우 SyntaxError(문법 에러)
가 발생하게 된다.
if (true) {
break; // SyntaxError
}
레이블 문(lable statement)이란?
식별자가 붙은 문을 의미하며break, continue
문과 함께 사용할 수 있다.label: statement;
레이블 문은 프로그램의 실행 순서를 제어하는데 사용한다. switch, case, default
문도 레이블 문이라고 할 수 있다.
레이블 문을 탈출하기 위해서는 break
문에 레이블 식별자를 지정한다.
foo: {
console.log(1);
break foo;
console.log(2);
}
console.log('Done!');
// 1
// Done!
중첩 for
문의 내부 for
문에서 break
를 실행하면, 내부 for
문을 탈출하여 외부 for
문으로 진입한다.
이때 내부 for
문이 아닌 외부 for
문을 탈출하고 싶다면 레이블 문을 사용한다.
outer: for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
if (i + j === 3) break outer;
console.log(`inner [${i}, ${j}]`);
}
}
console.log('Done!');
// inner [0, 0]
// inner [0, 1]
// inner [0, 2]
// inner [1, 0]
// inner [1, 1]
// Done!
레이블 문은 중첩 for
문 외부로 탈출할 때 유용하지만 그 밖의 경우에는 권장하지 않는다.
레이블 문을 사용하면 프로그램의 흐름이 복잡해져 가독성이 나빠지고, 오류 발생 가능성이 높아지기 때문이다.
break
문은 레이블 문 뿐만이 아니라 반복문과 switch
문에서도 사용할 수 있는데, 이 경우 break
문에 레이블 식별자를 지정하지 않는다. break
문은 반복문을 더 이상 진행하지 않아도 될 때 불필요한 반복 횟수를 회피할 수 있어 유용하다.
let string = 'Hello world.';
let search = 'l';
let index;
for (let i = 0; i < string.length; i++) {
if (string[i] === search) {
index = i;
break;
}
}
console.log(index); // 2
String.prototype.indexOf()
메서드를 사용해도 위 예제와 동일하게 동작한다.
console.log(string.indexOf(search)); // 2
continue
문continue
문은 반복문의 코드 블록 실행을 현 지점에서 중단하고, 반복문의 증감식으로 실행 흐름을 이동시킨다.
break
문처럼 반복문을 탈출하지는 않는다.
let string = 'Hello World.';
let search = 'l';
let count = 0;
for (let i = 0; i < string.length; i++) {
if (string[i] !== search) continue;
count++;
}
console.log(count); // 3
String.prototype.match()
메서드를 사용해도 위 예제와 동일하게 동작한다.
const regexp = new RegExp(search, 'g');
console.log(string.match(regexp).length); // 3
위 예제의 for
문은 아래 코드와 동일하게 동작한다.
for (let i = 0; i < string.length; i++) {
if (string[i] === search) count++;
}
만약 if
문 내에서 실행해야 할 코드가 한 줄이라면 continue
문을 사용했을 때보다 간편하고 가독성도 좋다. 하지만 반대로 실행해야 할 코드가 길다면 들여쓰기가 한 단계 더 깊어지므로 continue
문을 사용하는 것이 가독성이 더 좋다.
// continue 문을 사용하지 않으면 if 문 내에 코드를 작성해야 한다.
for (let i = 0; i < string.length; i++) {
if (string[i] === search) {
count++;
// code
// code
// code
}
}
// continue 문을 사용하면 if 문 밖에 코드를 작성할 수 있다.
for (let i = 0; i < string.length; i++) {
if (string[i] !== search) continue;
count++;
// code
// code
// code
}