[TIL] #4 (2022.01.23)

어느 개발자·2022년 1월 23일
0

TIL

목록 보기
4/15
post-thumbnail

📝 TIL
코드스피츠 ES6+ 기초편 2회차 1/2 Flow Control

Label

Identifier

label은 같은 scope 내에서 두 개 이상 나올 수 없다.

JavaScript의 scope
1. 함수 scope
2. 블록 scope (ES6부터 지원)

그렇다면 label은 어떤 scope를 가질까?
label의 scope는 함수로 결정된다. 따라서 중괄호 만으로는 label의 scope를 막아줄 수 없다.

But, label은 label scope 라는 것을 만들 수 있다.

Label Scope

label 뒤에 바로 중괄호를 쳐서 만들 수 있다.

abc:{
  abc:3;
}
4;

원래 같은 함수 scope 내에서 같은 이름의 label을 선언할 수 없지만, label scope가 만들어지면 그 안에서 scope가 구분되어 위와 같이 사용할 수 있다.

scope shadowing 이 일어난다.

label scope는 빠져 나오기 위해 사용한다.

abc: {
  console.log('start');
  if (true) {
    break;
  } 
}
console.log('end')

SynteaxError가 발생한다.

break 문을 아래와 같이 수정하여 label scope를 빠져나오게 할 수 있다.

abc: {
  console.log('start');
  if (true) {
    break abc;
  } 
}
console.log('end')

이는 label을 사용하여 수동으로 flow control 에 성공한 것이다. 이것이 가장 원초적인 flow control 이다.

그럼 label 영역이 아닌 곳에서도 될까?
이를 가능하게 하려면 label range 라는 것을 만들어주어야 한다.

Label Range & Set

auto label

  • iteration
  • switch
  • undefined named label

[] 23:40 다시 듣기

console.log('0');
abc:
	if (true) {
	    break abc;
    }
console.log('1');
bbb:
console.log('2');

goto 구문처럼 사용하려면 (OR)
1. label scope 선언
2. iterational label
3. switch label

goto와의 차이는 label은 아래로만 흐를 수 있다.
label이 위에 있어도 위로는 갈 수 없다.

label만 잘 사용하면, 함수처럼 흐름을 제어할 수 있다.

Auto Label

for (var i = 0; i < 10; i++) {
  if (i == 5) break;
}
console.log('end');

break 뒤에 이름이 없는데 label scope 에서는 이름이 없으면 에러 났는데 얘는 왜 안 날까?

js 엔진이 아래와 같은 과정을 거쳐주었기 때문이다.

temp38:
for (var i = 0; i < 10; i++) {
  if (i == 5) break temp38;
}
console.log('end');

이것이 바로 auto label 기능이다.
for while``switch 문 등에서 발동된다.

auto label 이 만들어낸 이름을 undefined named label 이라고 한다.
그래서 label 이름을 명시하지 않아도 SyntaxError가 발생하지 않는 것이다.

As Comment

label을 주석으로 사용할 수도 있다.
아래와 같이 코드가 길어질 경우 주석을 읽기 힘들 수 있다.

c: console.log('end'); // c
b: console.log('000000000000000000000'); // b
a: console.log('000000000000000000000000000000000000000000000'); // a

앞 주석을 쓰기 위해서 위와 같이 쓸 수 있다.
오픈소스를 보면 이렇게 사용하는 경우가 많다.

temp38:
for (var i = 0; i < 10; i++) {
  if (i == 5) break temp38;
}
function a () {
}

temp38 label 블럭의 가장 마지막으로 보낸다.

Switch

Special Label Block

for문은 중문이 나오느라 중괄호가 나오므로 필수가 아닌데
switch는 필수이다. 언어 parser가 해석하기 위한 토큰으로의 중괄호이다.

switch는 중괄호의 영역을 특별하게 만든다. special label이 가능하도록 한다.
special label이란 case: default: 을 말한다.

Fall Through

case 뒤에 break 걸지 않으면 뒤에 case가 실행된다.
(swift 언어에서는 이를 단점으로 판단하여 break 걸지 않아도 case에서 끝나게 한다.)

label 관점에서 보면 case: 라는 special label이 있을 뿐이지, break 걸지 않으면 fall through 가 일어나는 것이 이상하지 않다.

break를 사용하면 undefined named label 로 이동한다.
왜? switch 문에 auto label 을 만들어주기 때문이다.

switch (true) {
// special label 을 넣을 수 있다.
 a: console.log('0')
}

이렇게 하면 syntaxError 발생한다.
저 안에서는 case label과 default 레이블만 사용할 수 있기 때문이다.

switch (true) {
  case true: console.log('a');
  case false: console.log('b');
}

break를 사용하지 않으면 fall through 가 일어난다.

default 레이블을 가장 마지막에 적어야 하는 것은 아니다.
그럼 default case 중 어떤 분기 먼저 실행할까?

switch (true) {
  default: console.log('c');
  case true: console.log('a');
  case false: console.log('b');
}

이것은 언어마다 다르다.
js는 special label 블럭을 위에서 아래로 처리한다.

java, c언어 등은 컴파일 타임에 전체에 대한 map을 짠다. 값이 들어오면 바로 미리 생성된 map에 바로 mapping 한다. 따라서, java, c언어 등에서는 값이 primative로 확정되어야 한다. 계산하는 값이 있으면 안 된다.

But, js는 런타임에 해석하기 때문에 식을 마음대로 쓸 수 있다.

chrome 작동을 보면 default는 기본적으로 값 매칭에서 우선순위가 낮지만, fall through는 해당된다.

switch (true) {
  default: console.log('c');
  case true: console.log('a'); break;
  case false: console.log('b');
}
console.log('end');

이제 우리는 break를 타고 console.log('end'); 로 이동할 것이라는 걸 예상할 수 있다.

temp17:
switch (true) {
  default: console.log('c');
  case true: console.log('a'); break temp17;
  case false: console.log('b');
}
console.log('end');

이제 눈에 이렇게 보이는 것이다. 이것이 switch 문 안의 auto label 이다.

Runtime Switch

자바스크립트의 case문은 runtime에 해석한다.

따라서 아래와 같이 사용할 수 있다.

  1. 값에 대한 라우팅
  2. 조건 평가에 대한 분기

값에 대한 라우팅

var a = true
switch (a) {
  default: console.log('c');
  case true: console.log('a'); break;
  case false: console.log('b');
}
console.log('end');

정적인 값만 할 필요는 없다. 런타임에 평가되기 때문에 동적으로 사용할 수 있다.

var a = true
switch (a) {
  default: console.log('c');
  case f1(a): console.log('a'); break;
  case f2(a): console.log('b');
}
console.log('end');

조건 평가에 대한 분기

값을 무력화시키고 조건에 대한 분기를 만드는 방법이다.

switch (true) {
    case f3():
    case f4():
}

switch 문을 책임 연쇄 패턴으로 사용할 수 있다.

책임 연쇄 패턴 (Chain-of-responsibility pattern) 이다.

예시

switch (true) {
    case network(): // 네트워크에서 가져올 수 있어?
    case localCache(): // localCache에서 가져올 수 있어?
  default: // 안내문
}

다중 케이스 일치
network()wifi lte offline 세 가지 상태를 가진다면

switch (true) {
	case network() === 'wifi':
	case network() === 'lte':
	case network() === 'offline':
    case localCache():
  default: // 안내문
}

타인의 소스를 볼 때 문법에 어떤 기능이 있는지 보자. negative 하게 보지 말 것

switch 구문이 런타임에 해석된다는 것은 한 줄씩 순차적으로 실행된다는 의미이다.

var c = 2;
switch (true) {
    case c++ > 5: console.log(c); break;
    case c++ > 5: console.log(c); break;
	case c++ > 5: console.log(c); break;
	case c++ > 5: console.log(c); break;
	case c++ > 5: console.log(c); break;
	case c++ > 5: console.log(c); break;
}

실행해보면 7을 출력한다. 이는 순차적으로 실행되기 때문에 가능한 것이다.

0개의 댓글