3주차는 제어문(8장)과 타입 변환 및 단축 평가(9장)에 대한 내용이다.
제어문은 블록문을 시작으로 조건문과 반복문에 대한 설명이 나와있고,
타입 변환과 단축 평가는 암묵적, 명시적 타입 변환과 단축 평가의 정의와 논리 연산자, 옵셔널 체이닝 연산자 그리고 null 병합 연산자를 이용한 단축 평가에 대한 설명이 니와있다.
{}
가 블록 또는 스코프라 불리며 실행 단위로 취급되는 것은 알고 있었지만, 블록문이라고 부르기도 한다는 것을 처음 알게 되었다.
조건문은 예상했듯이 if
문과 switch ~ case
문이다.
두 조건문의 가장 명확한 차이는 조건식이 있는가
라고 생각한다.
if
문의 경우, if(조건식)
으로 사용하게 되는데 이 조건식
은 불리언 값으로 암묵적 변환되는 표현식
의 평가 결과가 담긴다.
switch
문의 경우, switch(표현식)
으로 사용하게 되는데, 이 표현식
은 불리언 값으로의 암묵적 변환이 일어나지 않고 case 표현식1
과 비교하는 방식으로 다음과 같이 작동한다.
switch("표현식"){
case "표현식1":
console.log("switch문의 표현식과 표현식1이 일치하면 실행될 문");
break;
case "표현식2":
console.log("switch문의 표현식과 표현식2가 일치하면 실행될 문");
break;
default "기본값":
console.log("swtich문의 표현식과 일치하는 것이 없다면 default로 실행될 문");
}
반복문은 for
문과 while
문이다.
반복문은 조건적으로 반복되는 문이며, 기본적인 구조는 다음과 같다.
for(변수 선언문 또는 할당문; 조건식; 증감식){
조건식이 참인 경우 반복 실행될 문;
}
실행 순서는 다음과 같다.
- 변수 선언 및 할당이 실행된다. 이 부분은 최초에 단 한 번만 실행된다.
- 조건식이 실행되면서 평가가 이루어진다.
- 평가 결과가 거짓이라면
for
문 실행이 종료되고, 참이라면 블록문{ 조건식이 참인 경우 반복 실행될 문 }
이 실행된다.- 블록문의 실행이 종료되면 증감식이 실행된다.
- 증감식이 종료되면
2.
번으로 돌아가며,3.
번에 의해 종료될 때까지 순서대로 실행된다.
간단히, 변수 선언 및 할당
> 조건식(참)
> 불록문
> 증감식
> 조건식
순서로 생각하면 될 것 같다.
이 다음으로 딥 다이브 책에 나오는 부분인데, 신기한 걸 발견했다.
for(;;) { ... }
가 무한히 실행되는 무한루프라고 되어있다. while(true) { ... }
무한루프는 많이 봤어도 for(;;)
는 처음 봤다. MDN에 따르면, 이를 empty
문이라고 한다.
while
문은 while(조건식) { 반복적으로 실행될 코드 }
와 같이 사용한다. 위에서도 언급했듯이, 조건식은 표현식의 결과가 불리언 값이 아니라면 암묵적으로 불리언 값으로 강제 변환된다.
반복문은 break
문을 사용해서 원하는 시점에서 탈출할 수 있다. for
, while
그리고 제어문인 switch
에서 사용한다.
반복문이 중첩되어있는 경우에 내부에서 break
를 사용하게 되면 그 즉시 내부 반복문을 탈출하여 외부 반복문을 마저 실행한다. 정확히는 레이블문
, 반복문(for, for...in, for..of, while, do..while)
, switch문
의 블록{}
을 탈출한다.
잠깐,
레이블문
이 뭐야?레이블문은
break
나continue
와 함께 사용할 수 있는 원하는 식별자로 구문 앞에 레이블을 추가할 수 있고 일반적으로 반복문에 레이블을 붙여 사용한다. 다음과 같이 반복문에 식별자를 붙일 수 있다.labelName: for(let i = 0; i < 5; i++){ ... }
레이블문을 활용하면 중첩 반복문의 내부에서의
break
한 번의 사용으로 외부 반복문의 탈출이 가능하다.outer: for(조건식) { inner: for(조건식) { break outer; } }
continue
문을 사용하면 사용 지점에서 반복문의 실행을 멈추고 증감식으로 실행 흐름을 이동시킨다. continue
문 또한 레이블문
과 같이 사용할 수 있다.
🚨 주의할 점
레이블문을 사용하면 반복문의 실행 흐름을 임의로 조작할 수 있다는 장점이 있지만, 프로그램의 흐름이 복잡해져서 가독성이 나빠지고 오류를 발생시킬 가능성이 높아지기 때문에 일반적으로 권장하지 않는다.
자바스크립트의 모든 값은 타입을 가지고 있다. 값을 담을 식별자 또한 타입을 가지고 있다. 가장 위에서 타입 변환을 두 가지로 분류해서 소개했었는데, 가장 먼저 암묵적 타입 변환
부터 살펴보자.
8장 요약의 조건식은 불리언 값이 아니라면 불리언 값으로 암묵적인 강제 타입 변환이 이루어진다. 이처럼 자바스크립트 엔진에 의해 개발자의 의도와는 상관없이 코드의 문맥을 고려해 암묵적으로
데이터 타입을 강제 변환
할 때가 있다.
가장 대표적인 예로 +
연산자는 피연산자 중 하나 이상이 문자열이라면 산술 연살자가 아닌 문자열 연결 연산자로 동작한다.
1 + '2' // "12"
0 + '' // "0"
-0 + '' // "0"
1 + '' // "1"
-1 + '' // "-1"
NaN + '' // "NaN"
Infinity + '' // "Infinity"
-Infinity + '' // "-Infinity"
true + '' // "true"
false + '' // "false"
null + '' // "null"
undefined + '' // "undefined"
(Symbol()) + '' // TypeError: Cannot convert a Symbol value to a string
({}) + '' // "[object Object]"
Math + '' // "[object Math]"
[] + '' // ""
[10, 20] + '' // "10,20"
(function(){}) + '' // "function(){}"
Array + '' // "function Array() { [native code] }"
이외에 ES6에서 도입된 템플릿 리터럴을 사용하면 표현식의 결과가 문자열 타입으로 암묵적 변환된다.
산술 연산자인 +
, -
, *
, /
는 모두 산술 연산자 표현식을 평가하기 위해 산술 연산자의 피연산자 중에서 숫자 타입이 아닌 피연산자를 숫자 타입으로 암묵적
타입 변환한다.
피연산자를 숫자 타입으로 변환할 수 없는 경우 표현식의 평가 결과는 NaN
이 된다.
산술 연산자 뿐만 아니라, 비교 연산자 또한 숫자 타입으로의 변환이 이루어지기도 한다.
"3" > 1 // true
비교 연산자는 불리언 값을 만든다. 크기를 비교하기 위해서는 문맥상 피연산자들을 숫자로 취급되어야 하고 이 때 숫자 타입이 아닌 피연산자는 숫자 타입으로 암묵적
변환이 일어나게 된다.
+
연산자 같은 경우 단항 연산자로 사용하면 피연산자가 숫자 타입이 아니면 숫자 타입의 값으로 암묵적 변환한다.
+'' // 0
+'0' // 0
+'1' // 1
+'string' // NaN
+true // 1
+false // 0
+null // 0
+undefined // NaN
+Symbol() // TypeError: Cannot convert a Symbol value to a number
+{} // NaN
+[] // 0
+[10, 20] // NaN
+(function(){}) // NaN
'0' * 1; // 0
true * 1; // 1
false * 1; // 0
조건식에서 많이 사용하는 암묵적
불리언 타입 변환이다.
조건식에서 false
, undefined
, null
, 0
, -0
, NaN
, ''
은 모두 Falsy(거짓으로 간주되는)
값이다. Falsy
값을 제외하면 모두 Truthy(참으로 간주되는)
값이다.
명시적으로 타입을 변환하는 것에는 여러 가지 방법이 있다.
암묵적
타입 변환을 이용하는 방법암묵적
타입 변환을 명시적
으로 이용하면 명시적
타입 변환을 하는 것이 된다. 암묵적
타입 변환은 앞에서 살펴보았으니 넘어가기로 한다.
String 생성자 함수
를 new 연산자
없이 호출하는 방법String(1); // "1"
String(NaN); // "NaN"
String(Infinity); // "Infinity"
String(true); // "true"
String(false); // "false"
Object.prototye.toString
메서드를 사용하는 방법(1).toString(); // "1"
(NaN).toString(); // "NaN"
(Infinity).toString(); // "Infinity"
(true).toString(); // "true"
(false).toString(); // "false"
Number 생성자 함수
를 new 연산자
없이 호출하는 방법Number('0'); // 0
Number('-1'); // -1
Number(true); // 1
Number(false); // 0
parseInt
, parseFloat
함수를 사용하는 방법(문자열에만 적용 가능)parseInt('1'); // 1
parseFloat('10.53'); // 10.53
Boolean 생성자 함수
를 new 연산자
없이 호출하는 방법Boolean('a'); // true
Boolean(''); // false
Boolean('false'); // true
Boolean(0); // false
Boolean(1); // true
Boolean(NaN); // false
Boolean(Infinity); // true
Boolean(null); // false
Boolean(undefined); // false
Boolean([]); // true
Boolean({}); // true
! 부정 논리 연산자
를 두 번 사용하는 방법!!'a'; // true
!!''; // false
!!'false'; // true
!!0; // false
!!1; // true
!!NaN; // false
!!Infinity; // true
!!null; // false
!!undefined; // false
!![]; // true
!!{}; // true
논리합 연산자와 논리곱 연산자를 사용한 표현식은 불리언 값이 아닐 때가 있는데 이 때는 두 피연산자 중 어느 한쪽으로 평가된다.
논리합 연산자와 논리곱 연산자 모두 좌항에서 우항으로 평가가 진행된다.
논리합 연산자는 true
또는 Truthy
값을 만나는 순간 그리고 논리곱 연산자는 false
또는 Falsy
값을 만나는 순간에 타입 변환하지 않고 피연산자를 반환한다.
이를 단축 평가 short-circuit evaluation
라 한다.
단축 평가는 표현식을 평가하는 도중에 평가 결과가 확정된 경우 나머지 평가 과정을 생략하는 것을 말한다.
단축 평가 표현식 | 평가 결과 |
---|---|
true ⎮⎮ anything | true |
false ⎮⎮ anything | true |
true && anything | true |
false && anything | true |
단축 평가는 다음과 같은 상황에서 유용하게 사용된다.
null
또는 undefined
가 아닌지 확인하고 프로퍼티를 참조할 때객체를 가리키기를 기대하는 변수의 값이 객체가 아니라 null
또는 undefined
인 경우 객체의 프로퍼티를 참조하면 타입 에러가 발생한다. 단축 평가는 이러한 에러 발생으로 인한 프로그램 강제 종료를 미연에 방지할 수 있다.
const obj = null;
let value = obj.value; // TypeError: Cannot read property 'value' of null
const obj = null;
let value = obj && obj.value; // null
ES11(ECMAScript2020)에서 도입된 옵셔널 체이닝 연산자
와 null 병합 연산자
는 좌항에 null
또는 undefined
가 올 수도 있는 상황에 사용하기 좋은 연산자이다.
옵셔널 체이닝 연산자
는 바로 위에서 언급한 논리곱 연산자를 이용한 단축 평가 대신 사용하기 좋다.
const obj = null;
let value = obj.value; // TypeError: Cannot read property 'value' of null
const obj = null;
let value = obj?.value; // null
null 병합 연산자
는 좌항의 피연산자가 null
또는 undefined
뿐만 아니라 Falsy
값이 올 때도 우항의 피연산자를 반환하는 논리합 연산자와 비교될 수 있는데, 빈 문자열''
이나 0
또한 유요한 값으로 취급해야할 때 null 병합 연산자
를 사용하기 좋다.
간단한 예시를 들어보자.
let text = '';
let 논리곱결과 = text || 'Hello world';
console.log(논리곱결과); // Hello world
let 병합결과 = text ?? 'Hello world';
console.log(병합결과); // ''
이번 3주차에서 empty
문과 label
문에 처음 알게 되었고, label
문과 break
, continue
과의 조합 또한 알게 되었다. 나머지 내용들은 어느정도 알고있었지만 복습할 수 있게 되어서 보람이 있었다.
하지만, 스터디 날이 되기 전에 empty
문과 label
문에 대해 깊이 관심을 가지지 않았어서 발표할 주제가 될만 했는데도 발표할 생각을 하지 못했고 결과적으로 발표를 하지 못했다.
스터디에서는 해당 주차에 학습한 할당량에 대해 발표와 토론을 하는데,
같은 주제를 놓고 이것저것 토론하는 것은 정말 좋은데 나를 포함한 모두가 각자 주제를 명확히 정해서 발표하는 것에 있어서 갈피를 못 잡고 있는 느낌이 든다.
이 문제를 보완하기 위해 스터디 주제와 관련이 없어도 알게 된 것에 대해 공유하는 방식으로 진행하기로 했다.