switch

switch

switch 문은 표현식을 평가해 그 값을 일련의 case 절과 대조하고, 값을 일치시키는 첫 번째 case 절 이후의 문들을 break 문을 만날 때까지 실행합니다. 어떤 case도 표현식의 값과 일치하지 않으면 switch 문의 default 절로 점프합니다.

출처: switch

본문

switch 문은 표현식을 평가해 그 값을 일련의 case 절과 대조하고, 값을 일치시키는 첫 번째 case 절 이후의 문들을 break 문을 만날 때까지 실행합니다. 어떤 case도 표현식의 값과 일치하지 않으면 switch 문의 default 절로 점프합니다.

구문 (Syntax)

switch (expression) {
  case caseExpression1:
    statements
  case caseExpression2:
    statements
  // …
  case caseExpressionN:
    statements
  default:
    statements
}
  • expression: 그 결과가 각 case 절과 대조되는 표현식입니다.
  • case caseExpressionN: expression과 대조하는 데 사용되는 case 절입니다. expression의 값이 어떤 caseExpressionN의 값과 일치하면, 그 case 절 이후의 첫 번째 문부터 switch 문의 끝이나 첫 번째 만나는 break까지 실행이 시작됩니다.
  • default: 제공되면 expression의 값이 어떤 case 절과도 일치하지 않을 때 이 절이 실행되는 default 절입니다. switch 문은 default 절을 하나만 가질 수 있습니다.

설명 (Description)

switch 문은 먼저 자신의 표현식을 평가합니다. 그런 다음 입력 표현식의 결과와 같은 값으로 평가되는 첫 번째 case 절을 찾아(엄격한 동등 비교, strict equality 사용) 그 절로 제어를 전달하고, 그 절을 따르는 모든 문을 실행합니다.

절의 표현식은 필요할 때만 평가됩니다. 이미 일치하는 항목을 찾았다면, fall-through로 방문될 예정이더라도 후속 case 절의 표현식은 평가되지 않습니다.

switch (undefined) {
  case console.log(1):
  case console.log(2):
}
// Only logs 1

일치하는 case 절이 없으면 프로그램은 선택적 default 절을 찾고, 있으면 그 절로 제어를 전달해 그 절을 따르는 문들을 실행합니다. default 절이 없으면 프로그램은 switch의 끝 다음 문에서 계속 실행됩니다. 관례상 default 절이 마지막 절이지만 반드시 그래야 하는 것은 아닙니다. switch 문은 default 절을 하나만 가질 수 있으며, 여러 default 절은 SyntaxError를 초래합니다.

break와 fall-through

switch 문 본문 내에서 break 문을 사용해 일찍 빠져나올 수 있으며, 종종 두 case 절 사이의 모든 문이 실행된 후에 그렇게 합니다. 실행은 switch 뒤의 첫 번째 문에서 계속됩니다.

break가 생략되면, 그 절의 표현식 값이 일치하는지 여부와 관계없이 실행이 다음 case 절로(심지어 default 절로도) 진행됩니다. 이 동작을 "fall-through"라고 합니다.

const foo = 0;
switch (foo) {
  case -1:
    console.log("negative 1");
    break;
  case 0: // Value of foo matches this criteria; execution starts from here
    console.log(0);
  // Forgotten break! Execution falls through
  case 1: // no break statement in 'case 0:' so this case will run as well
    console.log(1);
    break; // Break encountered; will not continue into 'case 2:'
  case 2:
    console.log(2);
    break;
  default:
    console.log("default");
}
// Logs 0 and 1

적절한 컨텍스트에서 다른 제어 흐름 문도 switch 문을 빠져나가는 효과가 있습니다. 예를 들어 switch 문이 함수 안에 있으면 return 문이 함수 본문의 실행을 종료하므로 switch 문도 종료합니다. switch 문이 루프 안에 있으면 continue 문이 switch 문을 멈추고 루프의 다음 반복으로 점프합니다.

어휘적 스코프 (Lexical scoping)

casedefault 절은 레이블과 같습니다. 제어 흐름이 점프할 수 있는 가능한 위치를 나타냅니다. 그러나 그들 자체로는 어휘적 스코프를 만들지 않습니다(자동으로 빠져나가지도 않습니다). 예를 들어:

const action = "say_hello";
switch (action) {
  case "say_hello":
    const message = "hello";
    console.log(message);
    break;
  case "say_hi":
    const message = "hi";
    console.log(message);
    break;
  default:
    console.log("Empty action received.");
}

이 예제는 "Uncaught SyntaxError: Identifier 'message' has already been declared" 오류를 출력합니다. 첫 번째 const message = 'hello';가 두 번째 const message = 'hi'; 선언과 충돌하기 때문입니다. 이는 두 const 선언이 switch 본문이 만든 같은 블록 스코프 안에 있기 때문입니다. 이 문제를 고치려면 case 절에서 let이나 const 선언이 필요할 때마다 그것을 블록으로 감싸세요.

const action = "say_hello";
switch (action) {
  case "say_hello": {
    const message = "hello";
    console.log(message);
    break;
  }
  case "say_hi": {
    const message = "hi";
    console.log(message);
    break;
  }
  default: {
    console.log("Empty action received.");
  }
}

예제: switch 사용하기

다음 예제에서 exprBananas로 평가되면 프로그램은 값을 'Bananas' case와 대조해 관련 문을 실행합니다. break를 만나면 프로그램은 switch를 빠져나가 switch 뒤의 문을 실행합니다. break가 생략되면 'Cherries' case의 문도 실행되었을 것입니다.

switch (expr) {
  case "Oranges":
    console.log("Oranges are $0.59 a pound.");
    break;
  case "Apples":
    console.log("Apples are $0.32 a pound.");
    break;
  case "Bananas":
    console.log("Bananas are $0.48 a pound.");
    break;
  case "Cherries":
    console.log("Cherries are $3.00 a pound.");
    break;
  case "Mangoes":
  case "Papayas":
    console.log("Mangoes and papayas are $2.79 a pound.");
    break;
  default:
    console.log(`Sorry, we are out of ${expr}.`);
}

예제: default 절을 두 case 절 사이에 두기

일치하는 항목이 없으면 실행은 default 절부터 시작해 그 뒤의 모든 문을 실행합니다.

const foo = 5;
switch (foo) {
  case 2:
    console.log(2);
    break; // it encounters this break so will not continue into 'default:'
  default:
    console.log("default");
  // fall-through
  case 1:
    console.log("1");
}

예제: fall-through 활용하기

이 방법은 case 절 아래에 break가 없으면 그 case가 기준을 충족하는지 여부와 관계없이 다음 case 절로 실행이 계속된다는 사실을 활용합니다.

다음은 단일 연산 순차 case 문의 예로, 네 개의 서로 다른 값이 정확히 동일하게 수행됩니다.

const Animal = "Giraffe";
switch (Animal) {
  case "Cow":
  case "Giraffe":
  case "Dog":
  case "Pig":
    console.log("This animal is not extinct.");
    break;
  case "Dinosaur":
  default:
    console.log("This animal is extinct.");
}

예제: if...else 체인의 대안

if...else 계열 대조를 자주 수행하게 될 수 있습니다.

if ("fetch" in globalThis) {
  // Fetch a resource with fetch
} else if ("XMLHttpRequest" in globalThis) {
  // Fetch a resource with XMLHttpRequest
} else {
  // Fetch a resource with some custom AJAX logic
}

이 패턴은 일련의 === 비교를 수행하는 것은 아니지만 여전히 switch 구조로 변환할 수 있습니다.

switch (true) {
  case "fetch" in globalThis:
    // Fetch a resource with fetch
    break;
  case "XMLHttpRequest" in globalThis:
    // Fetch a resource with XMLHttpRequest
    break;
  default:
    // Fetch a resource with some custom AJAX logic
    break;
}

switch (true) 패턴은 if...else의 대안으로 특히 fall-through 동작을 활용하고 싶을 때 유용합니다.

switch (true) {
  case isSquare(shape):
    console.log("This shape is a square.");
  // Fall-through, since a square is a rectangle as well!
  case isRectangle(shape):
    console.log("This shape is a rectangle.");
  case isQuadrilateral(shape):
    console.log("This shape is a quadrilateral.");
    break;
  case isCircle(shape):
    console.log("This shape is a circle.");
    break;
}

더 알아보기