2. FLOW CONTROL STATEMENT
제어문 : 무엇을 제어하냐? 바로 FLOW를 제어한다는 것.
메모리에 적재되어 실행되면 그때부터 우리는 관여를 할 수 없는데 FLOW CONTROL은 여기에 관여를 하겠다는 것이다.
RECORD, COMPLETION RECORD
우선 자바스크립트의 기본적인 ‘문’처리 방식을 배울 필요가 있다.
자바스크립트는 ‘문’을 해석할 때 ‘식’하고는 다르게 해석한다.
‘식’은 하나의 ‘값’으로 처리될 뿐이다.
그런데 ‘문’은 하나의 실행 단위로 해석이 된다.
그래서 우리가 문을 10개를 작성하면 10개의 실행 단위가 생긴다.
그에비해 ‘식’은 아무리 많이 써봐야 ‘값’으로 수렴할 뿐이다.
자바스크립트는 우리가 ‘문’의 갯수를 늘리면 ‘문’의 갯수만큼 우리가 처리해야할 과제로 삼는다.
따라서 한줄한줄 ‘문’이 생길 때마다 자바스크립트는 이를 ‘과제’로 등록한다.
이 ‘과제’를 RECORD라고 부른다. 즉 자바스크립트는 문을 레코드로 등록하고 이 레코드를 하나하나 소비해나가며 과제를 해결한다.
자바스크립트 엔진의 주 작동 원리
‘문’을 레코드로 만들어 적재한 후 순차적으로 실행한다.
자바스크립트 예전 책 - 좋은 책이다. 하지만 현재 자바스크립트랑은 맞지 않는 책이다.
현재 ES6 자바스크립트는 예전 자바스크립트 3.1 엔진처럼 전혀 그렇게 돌아가지 않는다.
스펙 문서 자체가 다르다.
자바스크립트 엔진 구동 원리 같은 걸 깊이 파고들지 마라.
자바스크립트 엔진은 해마다 엔진 스펙에 변화가 있기 때문에 어떤 엔진 스펙을 깊게 알았다는 것이 강점이 되지 않는다.
내년도에 완전히 달라질 수 있다.
이런 기저층의 작동원리를 배우기 보다는 이 개념이 무엇을 의미하는지 추상적인 의미를 공부하는 것이 훨씬 좋다.
COMPLETION RECORD들은 RECORD를 뭘로 할지 FLOW에 관여할 수 있다.
COMPLETION RECORD이 하는 일 : 어떤 RECORD를 선택할지를 고민한다.
‘문’은 컴파일러에게 주는 ‘힌트’일 뿐이다.
IF문이나 FOR문을 쓰면 COMPLETION RECORD가 어떻게 작동할지를 알려주는 힌트가 될 뿐이다.
DIRECT FLOW CONTROL (직접 흐름 제어) - 역사 굉장히 오래됨
플로우 컨트롤을 하기 위한 별도의 언어 : if, switch, for, while…
LABEL (유일한 직접 흐름 제어 방법)
현재 자바스크립트에서 사용할 수 있는 DIRECT FLOW CONTROL은 LABEL이다.
다중 for 문을 썼을 때 바깥쪽 for 문으로 빠져나오고 싶을 때, break를 걸려면 LABEL이란 걸 써야한다.
- identifier : 자바스크립트 변수 이름 규칙과 한가지만 다르다. $를 LABEL 이름에 쓸 수 없다는 점.
LABEL 외에 실제로 존재하는 ‘문’이 하나도 없으면 에러가 발생한다.
LABEL은 내부적으로 ‘문’이 아니라는 뜻이다.
LABEL은 브라우저 해석기가 볼 때는 ‘문’이 아니라는 것.
LABEL은 레코드에 달아주는 일종의 TAG(꼬리표) 같은 녀석인 것이다.
원래 LABEL은 같은 스코프 안에 동일한게 두 개가 나오면 에러가 뜨는데, 안뜨네?
파이어폭스로 확인해도 안 뜬다.
원래는 오류 뜨는 게 맞음~!
LABEL의 SCOPE
BLOCK SCOPE?
FUNCTION SCOPE?
LABEL은 ‘문’도 아니고 ‘식’도 아닌데 무엇으로 SCOPE가 결정될까?
LABEL의 SCOPE는 FUNCTION으로 결정된다.
LABEL 스코프 만드는 방법
LABEL 뒤에 {}를 하면 된다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script>
// 이런식으로 LABEL SCOPE를 나눠주면 아래처럼 작성 가능
// 원래 이렇게 하면 바깥 LABEL의 스코프와 안쪽 LABEL의 스코프가 다르므로 스코프에 대한 쉐도윙이 일어난다.
// 함수 지역변수도 바깥 지역변수를 가릴 수 있는 것처럼 아래 내부 abc도 바깥 abc LABEL을 가릴 수 있다.
abc:{
abc:{
}
}
</script>
</body>
</html>
영상에서 사파리는 LABEL SCOPE를 아예 인정하지 않는다고 에러문구가 뜸.
LABEL은 그만큼 오래된 기법이다보니.. 스펙이 계속 바뀌기도하고 그런가 봄.
LABEL은 빠져나오기 위해 존재한다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script>
abc:{
console.log('start');
if (true) break abc;
console.log('end');
}
</script>
</body>
</html>
FLOW 제어에 성공하였다.
LABEL 스코프를 생성하고나면, LABEL 스코프에선 LABEL을 빠져나올 수 있는 권리가 주어진다.
LOOP 문은 아니라 continue 문은 사용하지 못하지만 break 문은 사용할 수 있다.
이것이 우리가 할 수 있는 가장 원초적인 FLOW CONTROL이다.
LABEL이 아닌 곳에서도 이것을 할 수 있을까?
그럴려면 LABEL을 나눠줘야된다.
이것을 LABEL RANGE라고 한다.
LABEL RANGE & SET
LABEL RANGE를 이해하려면 SET을 먼저 인식해야 한다.
어떻게해서 LABEL이 LABEL 영역을 가지는지를 알아야된다는 것이다.
위에선 LABEL SCOPE를 생성해주었지만 SCOPE를 생성해주지 않았을 때, 하나의 LABEL이 어디까지 영역을 잡느냐는 것이다.
- LABEL에 특별한 무언가가 없다면 다음 LABEL까지를 범위로 삼는다.
- LABEL 다음에 Iteration(for, while..)이 오거나 Switch가 오면 범위를 한정짓는다.
LABEL 다음에 for문이면 해당 LABEL의 범위는 그 for문까지~! 다음 LABEL이 없어도~!
break abc;를 했어도 다음으로 넘어가질 않는다. 위에서 LABEL RANGE는 특별한 경우가 없는 이상 다음 LABEL 까지라고 했다.
하지만 break
가 에러는 안 났지만 건너뛰진 않는다.
그래서 break
로 건너뛸 수 있게 하려면 반드시 다음 3가지 구문 중 한 가지를 사용하여 LABEL을 써야된다.
- LABEL SCOPE를 선언하라.
- Iteration LABEL이 되어라.
- Switch LABEL이 되어라.
이렇게 써야 break 또는 루프구문 한정 continue를 쓸 수 있다.
LABEL은 if문이나 for 문이 있기 전에 개발자들이 직접 FLOW를 컨트롤하던 흔적이다.
LABEL은 break, continue와 같은 문밖에 사용 못한다. 즉, 아래로 내려가는 FLOW 제어밖에 못한다.
LABEL만 가지고 LOOP 문은 못 만든다. 아래로만 흐르기 때문에.
LABEL SCOPE만 잘 생성해주면 LABEL만 가지고 건너뛰는 FLOW를 만들 수 있다. 함수처럼.
단지 반복적인 실행은 못한다. 위로는 못 올라가니깐.
break
위에 LABEL에선 break 뒤에 LABEL 이름을 붙여줘야 했다.
그런데 for나 while문에선 그런거 없이 왜 break 문만으로 빠져나갈 수 있는 걸까?
위 break 문은 왜 실행이 되는거야?
아까 위에서 break 문에 LABEL 이름을 안 주면 Syntax Error 가 발생했잖아?
LABEL문을 권장하는 이유
다른 문은 Run time에서 에러가 잡힌다.
그런데 LABEL은 바로 Syntax Error로 떨어진다.
Parsing할 때 다 검사한다는 것이다.
훨씬 더 에러를 빨리 찾을 수 있다.
자바스크립트 엔진이 위와 같이 알아서 LABEL 이름을 생성해서 넣어준 것이다.
우리가 이름을 직접 줘도 되지만, 이름을 생략하면 break 문 뒤에, 그리고 for문 앞에 이름이 임의로 들어가게 된다.
이것이 바로 AUTO LABEL 기능이다.
AUTO LABEL
이런 AUTO LABEL 기능은 ITERATION(FOR, WHILE), SWITCH 문에서 발동된다.
그래서 이런 AUTO LABEL이 만들어낸 이름을 UNDEFINED NAMED LABEL이라고 한다.
LABEL SCOPE는 자동으로 만들어지지 않는다. (명심!)
AS COMMENT
LABEL을 COMMENT로 사용하는 경우도 있다.
LABEL을 왜 주석 대신 사용하지?
console.log('abcd'); // a
console.log('adfjasl;dfjals;dfjsa;ldf'); // b
console.log('asdfkldjsdfdsfsdf;'); // c
주석이 지저분하게 달려있다.
a: console.log('abcd');
b: console.log('adfjasl;dfjals;dfjsa;ldf');
c: console.log('asdfkldjsdfdsfsdf;');
그래서 이렇게 앞 주석으로 사용하기 위해 LABEL을 사용하는 경우가 많다.
LABEL은 코드에 부하를 걸지 않는다.
어차피 만들어진 레코드에 달리는 태그이기 때문이다.
for (let i=0; i<10; i++) {
console.log(i);
}
function a() {
console.log('함수a');
}
a();
temp38:
for (let i=0; i<10; i++) {
if (i === 5) break temp38;
}
이것이 abc 언어의 유일한 예외.
의외로 강력한 LABEL 구문.
의외로 많이 쓰인다.
SWITCH
- SPECIAL LABEL BLOCK
// for문 다음에 나오는 중괄호는 단문, 중문 각각 경우에 따라 맞게 써준다.
for (let i=0; i<10; i++) {
console.log(i);
}
// switch 문의 중괄호는 반드시 있어야 한다.
// 단문, 중문의 의미가 아니다.
// 이것은 문법적인 token이다.
// 이건 함수의 몸체를 나타내는 것도 아니고 중문을 의미하는 것도 아니다.
// 단지 switch의 몸체를 나타내는 전용 토큰일 뿐이다.
// 이 switch 뒤에 오는 {} 은 스페셜 LABEL BLOCK을 만든다. case1, case2, ..., default를 쓸 수 있는..
// switch 문 : 특별한 레이블 영역을 선언할 수 있는 공간을 만들어주는 문법이다.
// 일반 LABEL과 똑같다. 소괄호안에 내용과 case 뒤에 내용을 서로 비교한다는 것 빼곤.
switch (true) {
}
- Fall Through
case
뒤에 break
문을 안 쓰면 밑에 case들이 계속해서 실행되는 현상이다.
switch
문에 case마다 break가 없으면 아래 case들이 실행되면 안되는데 실행된다고 해서 switch 문을 굉장히 낮게 평가하는 사람들이 있다.
하지만 그건 모르는 소리다.
LABEL의 기본 특성만 이해하면 이런 소리가 안 나온다.
Fall Through는 너무나도 당연한 작동인거고, switch case에서 break를 걸면 어디로 빠져나온다?
UNDEFINED NAMED LABEL로 빠져나오게 된다는 것이다.
switch문 앞에 AUTO LABEL 만들어준다고 했으니깐.
special label block에 이상한 label 구문을 써서 오류가 난다.
special label block 안에 쓸 수 있는 label은 case와 default 뿐이다.
// switch 문이 어려운 이유
// switch {} 안에 문들을 처리하는 방식이 '언어'마다 다르다.
// 자바스크립트는 위에서 아래로 처리한다.
switch (true) {
default: console.log('c');
case true: console.log('a');
case false: console.log('b');
}
현재 크롬 작동법을 보면 default
는 따로 처리하는 것 같다.
값평가에서 default는 우선순위가 낮다.
case에도 일치하는 값이 없으면 default 부터 아래로 실행된다.
case와 일치하는 부분이 생기면 그 부분부터 실행된다.
temp17 :
switch (true) {
default: console.log('c');
case true: console.log('a'); break temp17;
case false: console.log('b');
}
break 뒤에 LABEL 이름 안 붙이는 게 더 신기한 상황이다.
우리는 항상 for, while, switch 같은 문만 써와서 몰랐을 뿐이다.
RUNTIME SWITCH
자바스크립트의 case 문은 런타임에서 해석한다. 런타임에서 해석하기 때문에 자바스크립트에서는 switch 문을 두 가지 형식으로 쓸 수 있다.
- 값에 대한 라우팅 - 정적인 경우 유리 : 우리가 흔히 알고 있는 switch 문
var a = true; temp17: switch (a) { default: console.log('c'); case true: console.log('a'); break temp17; case false: console.log('b'); } console.log('end'); // 아래와 같이 값에 대한 평가를 동적으로 처리해도 됨 var a = true; temp17: switch (a) { default: console.log('c'); case f1(a): console.log('a'); break temp17; case f2(a): console.log('b'); } console.log('end');
- 조건 평가에 대한 분기 (chain of responsibility)
switch (true) { case network(): // network에서 승부볼 수 있어? 안되면 아래 case localCache(): // localChache에서 승부볼 수 있어? default: // 안내문.. } switch (true) { case network() === 'online': case network() === 'wifi': case network() === 'offline': case localCache(): // localChache에서 승부볼 수 있어? default: // 안내문.. }
switch
구문이 runtime
에서 실행된다는 이야기는 한 줄씩 실행된다는 이야기이다.
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;
}
run time에서 실행되기 때문에 c에 영향을 준다.
이게 언어마다 다르다.
런타임에서 실행되는 자바스크립트에선 이점을 유의하며 switch 문을 작성해야된다.
continue
와 break
는 그냥 쓰일 수 있는 것이 아니라 반드시 블럭 안에서만 쓰일 수 있고 continue
와 break
가 쓰이는 경우는 정확히 세가지이다.
- iteration set
- label set
- switch set
위 세가지 블럭 안에 들어가 있을 때만 작동한다.
그런데 아까 말한대로 switch
문은 블럭
이 아니다. switch label section이라고 불리는 녀석이다.
그래서 block과는 좀 다른 개념이지만.. 어쨌든 위 세가지 경우에만 continue
와 break
를 사용할 수 있다.
// 아래는 LABEL은 존재하지만 LABEL block이 존재하지 않는다.
// 따라서 continue와 break를 쓸 방법이 없다.
// 아래와 같은 LABEL은 주석 대신으로 쓸 수는 있다.
a:
const b = 3;
// 아래와 같이 주석 대신해서 쓸 수는 있다.
// LABEL을 활용하면 앞쪽 주석을 달 수 있다.
b: const c = 5;
조건문, 반복문
// 아래 소괄호 안에는 '식'이 들어온다.
// '식'이 truthy인지 falsy인지 판단하는 것이다.
// if문으로만 끝나는 것은 옵셔널 구문이다. 해도되고 안해도 그만인 문
if (식) 옵셔널
// if else 문은 필수적인 멘다토리 문이다. 반드시 결정된 로직.
if (식) else 필수적(멘다토리)
// 많은 사람들이 아래 식을 else if 문이라고 인식한다.
// 하지만 실제로 제어문엔 if ... else 문만 존재한다.
// 아래식 또한 if ... else 문이다.
// 아래 else 문 다음에 if 문 단문이 하나 들어온 것이다.
const c = 3;
if (c === 1) {
} else if (c === 2) {
} else if (c === 3) {
} else {
}
// 따라서 위의 식은 아래처럼 쓸 수 있다.
// 사실 아래와 같이 쓰는 게 코드를 이해하기 더 쉽고(오해를 불러일으키지 않고) 더 좋다. 명시적으로 알 수 있으므로.
const c = 3;
if (c === 1) {
} else {
if (c === 2) {
} else {
if (c === 3) {
} else {
}
}
}
// 이젠 아래와 같은식도 이해되지?
const c = 3;
if (c > 5) {
} else switch (c) {
}
// else if가 안 이상하다면 아래와 같은 식들도 전부 안 이상해야 정상이다.
const c = 3;
if (c > 5) {
} else for () {
}
else if 가 안 이상한 것처럼 else switch, else for 등등.. 이런 것들도 안 이상해야 정상이다.
그래서 else if 문이라는 것은 없다.
그런데 이 if else 문의 문제가 뭐냐면, if else 가 반복되는 경우에 else가 어디와 결합한다?
후방 결합을 한다. 이걸 주의해야한다.
// javascript if는 후방에 있는 else가 앞에 있는 if를 따라가도록 되어있다.
// 이러한 현상을 방지하려면 {} 로 묶어서 분리해주지 않으면 항상 후방 결합을 하도록 되어있다.
// 즉, 이런 뜻 같다. 아래의 식은 서로가 각각 1, 2, 3에 매칭되어 있지만 만약에 ==이 아니라 범위라면,
// 후방결합으로인해 겹치는 영역이 발생한다는 것이다. (이런 뜻 같음)
// 그럼 필연적으로 에러가 날수밖에 없다.
const c = 3;
if (c === 1) {
} else if (c === 2) {
} else if (c === 3) {
} else {
}
// 그래서 이런식으로 쓰는 것이 좋다.
// else if 구문은 사용하지 말고, 앞으로는 중괄호를 사용해서 작성한다.
const c = 3;
if (c === 1) {
} else {
if (c === 2) {
} else {
if (c === 3) {
} else {
}
}
}
else if를 병렬적으로 사용하면 위험성이 커진다.
else if는 원래도 어려운 코드이고, 남들이 봤을 때 이해하기 난감한 코드인데다가, else 후방조건이 제대로 붙어있는지를 검사하기가 굉장히 까다롭다.
컴파일 에러도 안나고, 런타임도 그대로 통과하기 때문에 그렇다.
그냥 알고리즘 자체가 잘못된 경우가 너무 많다.
그러면 else if는 언제 쓰는 것일까?
병렬로 나열하는 것이 아닌 ‘부분집합’일 때만 쓴다.
위의 소스보면 명확해지지않아?
아까까지 봤을 때는 else if 문들이 병렬인줄 알았는데, 위와 같이 제대로 보니까 뭐로 보인다?
안에 nested 되어있는 sub 집합인 것이다.
따라서 else if는 병행 조건일 때 쓰면 원래 안되는 것인데, 착시효과 때문에 병행 조건처럼 쓰는 것이다.
그래서 switch
랑 else if
랑 똑같다고 생각하는 것이다. 잘못된 생각이다.
병행조건(병렬조건?)일 땐 무조건 switch
를 쓴다.
내가 평가해야될 식들이 동등하다면 switch
문을 사용하고, 그런게 아니라 내가 평가해야될 식이 어떤 경우에 들어가서 되어야해. nested 되어있어.
그러면 if else 중괄호를 써야된다. 서브집합 관계
여지껏 switch 문이나 if else 문이나 그게그거인데 왜 제어문이 여러개가 있을까..라고 생각했었는데…
개발자들이 바보가 아닌데 이렇게 여러개가 있을 이유가 없지 역시..
switch와 if else 문의 차이점을 명백하게 알고 넘어가자.
const c = 3;
if (c === 1) {
} else {
if (c === 2) {
} else {
// 그럼 이런 경우는 어떤 경우일까?
// 이런 경우를 '논리적 부수효과'라고 부른다.
// 여러분들의 코드들은 대부분 이런 경우에서 망가진다.
// if ... else는 mandatory 로직이라고 했다. 절대로 빠져나갈 수 없는 필수적인 로직
if (c === 3) {
// if ... else 구문안에 if문이 덩그러니
// 이건 mandatory인지 optional인지 알 수 없다.
// 이 코드를 완전히 읽고 분석하기 전까지는..
}
}
}
// 추천하는 로직은 mandatory로 갔으면 전체 다 mandatory로 갈 것을 추천한다.
// 위와 같은 경우는 코드를 다 까보고 실행시켜야 이해할 수 있다. 그 전까지는 절대로 이해못한다.
// 기계는 괜찮지만 사람이 이해할 수 없다.
// mandatory로 시작했으면 mandatory로 끝나야지 중간에 optional이 들어오면 그 다음부턴 이 로직을 이해할 수 없게 된다.
코드를 수정할 수 있다는 뜻 - 이해하기 쉽고, 이 부분을 수정해도 다른 부분에 영향을 안 미친다는 뜻.
수정하나 들어왔는데 일정은 한달걸리고 우리팀 전부 야근해야돼 - 이건 개발자 잘못. 처음부터 제대로 알고 코딩하자.
switch
문은 굉장히 섬세하고 어렵다.
왜냐하면 병행조건을 나열하면 어떤 일이 생기지?
병행조건에 포함되지 않는 예외, 여집합이 반드시 생긴다.
그래서 switch
문에는 반드시 default
가 있다.
default
가 없는 switch
문은 사용하지 않는다.
왜?
병행조건에는 반드시 예외가 생길 수 있는 가능성이 있기 때문이다.
- if … else 문은 mandatory로 작성할거면 끝까지 mandatory로 작성해라.
- else 뒤에 if 문을 사용할 거면 그건 subset이라는 뜻이기 때문에 {}로 묶어줘라. 그래야 의미를 혼동하지 않는다.
반대로 subset일 때만 if … else를 사용해라. - 병행 조건일 때는 switch 문을 사용해라. 병행조건을 사용하면 반드시 여집합이 생기기 때문에 default 처리를 해라.
어려운 경우
if … else 문에 switch 문이 들어가 있을 때. if … else 문은 mandatory 문인데 병행조건인 switch 문이 들어가 있다.
이런 경우는 뭘까?
병행조건은 default 가 있는 이상 뭐로 빠질 수 있다? mandatory로 빠질 수 있다. case에 예외가 없으니깐.
오케이. 그렇게 짜여져있다면 잘 짜여져 있는 거야.
이렇게 판단할 수 있다는 거다.
if … else 문에 if 문만 왔어. mandatory에 optional이 왔네?
그런데 로직 돌렸는데 성공했어.
175가지 경우 중에 2가지 경우에 성공한 거.
더 돌리면 다 망가지고 다 에러난다.
이런 경우가 발생하면 보통 어떻게 해결한다? 계속 if 문을 늘려나간다. 영원히 늘려나가다가 나중에 와 이거 안되겠는데, 하면서 다시 처음부터 소스를 작성한다.
이렇게 되는 거다.
- mandatory -> mandatory / optional -> opational / 두 개를 섞으려면 로직분리를 굉장히 잘하던지 / 아니면 완전히 분리해서 함수로 보내던지.
mandatory인데 optional이 오는 경우.
mandatory 부분인데 무언가 어떤 function을 반드시 호출하고 끝나.
// 그럼 여기서 mandatory는 끝난 것.
그 function 안에서 옵셔널임.
이런 경우는 괜찮다.
function 안에서 optional로 보면 되는거니깐.
이런걸 격리라고 부른다.
우리가 익혀야될 기술은 격리이다.
반복문
// 아래는 '식'일까 '문'일까?
var a = 3;
// 아래가 에러나는 걸 보면 '선언문'이라는 것을 알 수 있다.
var k = var b = 4;
// ex는 식을 뜻하는 단어이다.
// var b = 4; 이런거 선언문이라면서, 왜 for () 안에 문을 넣을 수 있는 걸까?
// 소괄호 안에는 '식'이 들어간다고 하지 않았나?
// ------for문의 첫번째 인자는 선언문(다른 문은 안된다)이나 식이 들어갈 수 있다.-----
// 나머지 두개 식은 무조건 식만 들어갈 수 있다.
// falsy : '', 0, null, NaN, undefined, false
// falsy 아닌 것들은 다 truthy
// 2번째 인자가 truthy여야 for 문이 실행
// 마지막 3번째 인자는 for 중괄호의 마지막에서 반드시 실행된다.
for (ex; truthy; ex) {
}
// 아래 식도 이해가 간다.
// 마지막 3번째 인자는 식문이 전달되는 것이기 때문에 '공문'도 된다.
for (ex; truthy; ) {
}
// 첫번째 인자도 '문'이 올 수 있다. '공문'처리 가능
// 그런데 truthy여야만 돈다며? 왜 truthy를 비워도 무한 루프를 돌까?
// for의 예외 사항이다.
// for문 가운데에 공문이 오면 truthy로 평가된다는 사항이 공식 문서에 기재되어있다. (언어 스펙)
for (; truthy ;) {
}
// truthy 평가, 무한루프
// 상식적으론 falsy가 되어서 빠져나오는 것이 맞는데 아니다. 예외사항
// 자바스크립트 empty 값은 falsy로 평가되는데 for문만 예외사항으로 평가된다.
// 즉, for문을 쓸 때 가운데를 비우면 항상 truthy로 평가돼서 무한루프에 빠진다.
for (; ;) {
}
// while 문 공백으로 냅두면 죽어버린다. 반드시 truthy 값을 넣어줘야 작동한다.
// while 문 같은 경우는 for문에 비해 훨씬 간단하다. truthy값만 넣어주면 무한루프
while (truthy) {
}
// while 옆 소괄호에 반드시 truthy 값이 와야된다.
do {
}while (truthy)
// while 문에 truthy만 넣어주면 무한루프하기 때문에 보통 truthy 넣는 자리에 '런타임 평가항목'을 넣고
// {} 안에서 '런타임 평가항목'의 상태에 변화를 준다.
var a = -1;
while (a < 2) {
a++;
}
// while 문이 어려운 이유
// 아래와 같이 작성되어있으면 빠져나올 수 있는거야 아니면 무한루프야?
// 이게 현실이다.
// 따라서 우리는 아래와 같은 코드는 무조건 배제한다.
// 또한 아래처럼 짜면 에러날 확률이 백퍼다. 유지보수도 힘들고.
while (act.method().c) {
other.action();
}
// 이러면 while문이 무한루프인지 아닌지 판단하기 쉬움
// 아래와 같이 명시적으로 런타임 평가 상태에 변화를 주는 식이 while문 바디 안에 들어가있어야 한다.
// while, do while을 쓸 때 무조건 예외 없이 아래와 같이 런타임 평가 상태 변화식(조건식)을 식별할 수 있도록 짜야한다.
var a = act.method().c;
while (a) {
other.action();
a = act.method().c;
}
// 위와 같이 짰을 때 장점 하나 더
// 아래처럼 식에 안전장치를 걸어주기 더 쉽다.
// 테스트하기도 쉽고
var a = act.method().c;
while (a) {
let r = other.action();
a = act.method().c;
if (r === 'abc') a = false;
}
while 문은 {} 중문이 권장사항이다.
{} 중괄호가 필수는 아니나 조건식이 있고 다른 식이 있을 텐데, 이를 단문으로 컨트롤할 수 있겠어? 라는 뜻.
do while문도 {}가 필수가 아니다.
do ‘문’ while ‘식’
do 'aaa'; while ();
하지만 {} 쓰는게 권장사항이다.
앞으로 공부해야될 반복문들
- for … of
- for … in : forEach라고 많이 부른다.