모던 자바스크립트 Deep Dive (Ch.8 ~ Ch.11)

kozi·2023년 7월 15일
0

내용정리

목록 보기
4/7

8장 제어문

제어문은 조건문과 반복문에서 사용함.
일반적으로 코드는 위에서 아래 방향으로 순차적으로 실행되지만 제어문을 사용하면 실행 흐름을 인위적으로 제어할 수 있음.

제어문은 코드의 흐름을 이해하기 어렵게 해 가독성을 해치는 단점이 있음.
가독성이 좋지 않은 코드는 오류를 발생시키는 원인이 됨.

forEach, map, filter, reduce 같은 고차 함수를 사용한 함수형 프로그래밍 기법은 제어문 사용을 억제하여 복잡성 해결하려고 노력함.

8.1 블록문

0개 이상의 문을 중괄호로 묶은 것으로, 코드 블록 또는 블록이라고 부르기도 함.
자바스크립트는 블록문을 하나의 실행 단위로 취급.
단독으로 사용할 수 있으나 일반적으로 제어문이나 함수 정의 시 사용함.

블록문은 자체 종결성을 갖기 때문에 끝에 세미콜론을 붙이지 않음.

8.2 조건문

조건문은 주어진 조건식의 평가 결과에 따라 코드 블록(블록문)의 실행을 결정함.
조건식은 불리언 값으로 평가될 수 있는 표현식.

8.2.1 if ... else 문

true일 경우 if 문의 코드 블록이, false일 결우 else 문의 코드 블록이 실행됨.

조건식이 불리언 값이 아닌 값으로 평가되면 암묵적으로 불리언 값으로 강제 변환됨.

else if, else 문은 옵션.
if 문, else 문은 2번 이상 사용할 수 없지만 else if 문은 여러 번 사용 가능함.

코드 블록 내의 문이 하나뿐이라면 중괄호 생략 가능.

삼항 연산자는 경우의 수가 세 가지(양수, 음수, 영)라면 다음과 같이 쓸 수도 있음.

var kind = num ? (num > 0 ? '양수' : '음수') : '영'; // 0은 false 취급되므로

console.log(kind); // 양수

삼항 조건 연산자 표현식은 값처럼 사용 가능하므로 변수 할당 가능함.

가독성 측면에서,
조건에 따라 단순히 값 결정하여 변수 할당하는 경우 -> 삼항 조건 연산자
조건에 따라 실행 내용 복잡, 여러 줄의 문 필요 -> if ... else 문

8.2.2 switch 문

switch 문은 주어진 표현식을 평가한 값과 일치하는 표현식을 갖는 case 문으로 실행 흐름 옮김.

case 문은 상황을 의미하는 표현식을 지정, 콜론으로 마침.
그 뒤 실행할 문들을 위치시킴.

일치하는 case 문 없다면 default 문으로 이동.
default 문은 옵션.

if ... else 문은 참, 거짓으로 실행할 코드 블록 결정.
switch 문은 다양한 상황에 따라 실행할 코드 블록 결정할 때 사용함.

var month = 11;
var monthName;

switch (month) {
    ...
    case 10: monthName = 'October';
    case 11: monthName = 'November';
  	case 12: monthName = 'December';
  	default: monthName = 'Invalid month';
}

console.log(monthName); // Invalid month

case 11로 실행 흐름 이동했지만, 문 실행 후 switch 문을 탈출하지 않고 switch 문이 끝날 때까지
이후의 모든 case 문과 default 문을 실행했기 때문에 Invalid month 출력됨.
이를 폴스루(fall through)라고 함.

코드 블록에서 탈출시키기 위해 case 문에 해당하는 문의 마지막에 break 문을 사용해야 함.

default 문에는 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:
    // 윤년 계산 알고리즘
    // 1. 연도가 4로 나눠떨어지는 해는 윤년임.
    // 2. 연도가 4로 나눠떨어지더라도 연도가 100으로 나눠떨어지는 해는 평년임.
    // 3. 연도가 400으로 나눠떨어지는 해는 윤년임.
    days = ((year % 4 === 0) && year % 100 !== 0) || (year % 400 === 0)) ? 29 : 28;
    break;
  default:
    console.log('Invalid month');
}

console.log(days); // 29

C 언어 기반 프로그래밍 언어는 대부분 switch 문 지원하지만
python처럼 switch 문 지원하지 않는 프로그래밍 언어도 있음.

if ... else 문으로 해결할 수 있으면 if ... else 문 사용하는 편이 좋음.
하지만 조건이 너무 많아서 switch 문을 사용했을 때 가독성이 더 좋다면 switch 문을 사용하자.

8.3 반복문

조건식의 평가 결과가 참인 경우 코드 블록 실행함.
그 후 조건식 다시 평가하여 여전히 참인 경우 재실행함. 거짓일 때까지 반복.

8.3.1 for 문

for 문의 변수 선언문, 조건식, 증감식은 모두 옵션임.
어떤 식도 선언하지 않으면 무한루프가 됨.

// 무한루프
for (;;) { ... }

for 문은 중첩이 가능함.(중첩 for 문이라고 함.)

8.3.2 while 문

for 문은 반복 횟수가 명확할 때 주로 사용, while 문은 반복 횟수가 불명확할 때 주로 사용함.
조건식의 평가 결과가 언제나 참이면 무한루프가 됨.

// 무한루프
while (true) { ... }

무한루프 탈출 위해서 코드 블록 내에 if 문으로 탈출 조건 만들고 break 문으로 코드 블록 탈출함.

8.3.3 do ... while 문

코드 블록을 먼저 실행하고 조건식을 평가함.
따라서 코드 블록은 무조건 한 번 이상 실행됨.

var count = 0;

// count가 3보다 작을 때까지 코드 블록 계속 반복 실행함.
do {
  console.log(count); // 0 1 2
  count++;
} while (count < 3);

8.4 break 문

break 문은 레이블 문, 반복문(for, for ... in, for ... of, while, do ... while) 또는 switch 문의 코드 블록을 탈출함.
이외에 break 문 사용하면 SyntaxError(문법 에러) 발생함.

레이블 문이란 식별자가 붙은 문을 말함. (case 문, default 문도 레이블 문임)

// foo라는 레이블 식별자 붙은 레이블 문
foo: console.log('foo');

레이블 문 탈출하려면 break 문에 레이블 식별자 지정함.

foo: {
  console.log(1);
  break foo; // foo 레이블 블록문을 탈출.
  console.log(2);
}

console.log('Done');

//1
//Done
//이렇게 출력됨.

중첩 for 문의 내부 for 문에서 break 문 실행하면 내부 for 문 탈출, 외부 for 문으로 진입함.

외부 for 문 탈출할 때 레이블 문 사용함.

// outer라는 식별자 붙은 레이블 for 문
outer: for (var i = 0; i < 3; i++) {
  for (var j = 0; j < 3; j++) {
    // i + j === 2일 때 outer 식별자 붙은 레이블 for 문 탈출.
    if (i + j === 2) break outer;
    console.log(`inner [${i}, ${j}]`);
  }
}

레이블 문은 중첩 for 문 외부 탈출할 때 유용하지만 그 밖에는 일반적으로 권장하지 않음.
프로그램 흐름이 복잡해져서 가독성이 떨어지고 오류 가능성 높아지기 때문.

8.5 continue 문

코드 블록 실행을 현 지점에서 중단, 반복문의 증감식으로 실행 흐름 이동시킴.

if 문 내에 실행해야할 코드가 한 줄이라면, continue 안쓰는 편이 가독성 좋음.

if 문 내에서 실행할 코드 길다면, continue 문 사용하는 편이 가독성이 더 좋음.
(continue 문을 사용하면 들여쓰기를 한 단계 줄일 수 있으므로)

9장 타입 변환과 단축 평가

9.1 타입 변환이란?

자바스크립트의 모든 값은 타입이 있음.

개발자가 의도적으로 값의 타입을 변환하는 것을 명시적 타입 변환 또는 타입 캐스팅이라 함.

var x = 10;

var str = x.toString();
console.log(typeof str); // string

// x 변수의 값이 변경된 것은 아님.
console.log(typeof x); // number

개발자 의도와 상관없이 표현식 평가 도중 자바스크립트 엔진에 의해 암묵적으로 타입 자동 변환되기도 함.
이를 암묵적 타입 변환(타입 강제 변환)이라고 함.

기존 원시 값을 직접 변경하는 것은 아님. 원시 값은 변경 불가능한 값이므로 변경 불가함.
타입 변환은 새로운 원시 값 생성하는 것.

자바스크립트 엔진은 표현식을 에러 없이 평가하기 위해 피연산자의 값을 암묵적 타입 변환해 새로운 타입 값 만들어 단 한 번 사용하고 버림.

9.2 암묵적 타입 변환

// 피연산자가 모두 문자열 타입이어야 하는 문맥
'10' + 2 // -> '102'

// 피연산자가 모두 숫자 타입이어야 하는 문맥
5 * '10' // -> 50

// 피연산자 또는 표현식이 불리언 타입이어야 하는 문맥
!0 // -> true
if (1) { }

암묵적 타입 변환이 발생하면 문자열, 숫자, 불리언과 같은 원시 타입 중 하나로 타입을 자동 변환함.

9.2.1 문자열 타입으로 변환

문자열 타입이 아닌 값을 문자열 타입으로 암묵적 타입 변환 수행할 때 다음과 같이 동작함.

// 숫자 타입
0 + ''           // "0"
-0 + ''          // "0"
1 + ''           // "1"
-1 + '' 		 // "-1"
NaN + ''		 // "NaN"
Infinity + ''	 // "Infinity"
-Infinity + ''	 // "-Infinity"

// 불리언 타입
true + ''		 // "true"
false + ''		 // "false"

// null 타입
null + ''		 // "null"

// undefined 타입
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] }"

9.2.2 숫자 타입으로 변환

1 - '1' // 0
1 * '10' // 10
1 / 'one' // NaN

위 예제에서 사용한 연산자는 모두 산술 연산자임.
산술 연산자의 역할은 숫자 값 만드는 것.
따라서 산술 연산자의 모든 피연산자는 코드 문맥상 모두 숫자 타입이어야 함.

피연산자를 숫자 타입으로 변환할 수 없는 경우 산술 연산 수행할 수 없으므로 표현식 평가 결과는 NaN이 됨.

'1' > 0 // true
비교 연산자 역할은 불리언 값 만드는 것.
피연산자는 코드 문맥상 숫자 타입이어야 하므로 숫자 타입으로 암묵적 타입 변환함.

  • 단항 연산자는 다음과 같이 암묵적 타입 변환 수행함.
// 문자열 타입
+''				// 0
+'0'			// 0
+'1'			// 1
+'string'		// NaN

// 불리언 타입
+true			// 1
+false			// 0

// null 타입
+null			// 0

// undefined 타입
+undefined		// NaN

// 심벌 타입
+Symbol()		// TypeError: Cannot convert a Symbol value to a number

// 객체 타입
+{}				// NaN
+[]				// 0
+[10, 20]		// NaN
+(function(){})	// NaN

9.2.3 불리언 타입으로 변환

자바스크립트 엔진은 불리언 타입이 아닌 값을 Truthy 값(참으로 평가되는 값) 또는 Falsy 값(거짓으로 평가되는 값)으로 구분함.
불리언으로 평가되어야 할 문맥에서 Truthy 값은 true로, Falsy 값은 false로 암묵적 타입 변환됨.

false, undefined, null, 0, -0, NaN, ''
위는 false로 평가되는 falsy값들임.

Truthy/Falsy 값을 판별하는 isFalsy(), isTruthy() 함수가 있음.

9.3 명시적 타입 변환

명시적 타입 변경 방법은 다양함.
표준 빌트인 생성자 함수(String, Number, Boolean)을 new 연산자 없이 호출하는 방법,
빌트인 메서드를 사용하는 방법,
암묵적 타입 변환을 이용하는 방법이 있다.

9.3.1 문자열 타입으로 변환

문자열 타입이 아닌 값을 문자열 타입으로 변환하는 방법

  • String 생성자 함수를 new 연산자 없이 호출
  • Object.prototype.toString 메서드 사용
  • 문자열 연결 연산자 이용

9.3.2 숫자 타입으로 변환

숫자 타입이 아닌 값을 숫자 타입으로 변환하는 방법

  • Number 생성자 함수를 new 연산자 없이 호출하는 방법
  • parseInt, parseFloat 함수를 사용하는 방법(문자열만 숫자 타입으로 변환 가능)
  • '+' 단항 산술 연산자 이용
  • '*' 산술 연산자 이용

9.3.3 불리언 타입으로 변환

불리언 타입이 아닌 값을 불리언 타입으로 변환하는 방법

  • Boolean 생성자 함수를 new 연산자 없이 호출
  • ! 부정 논리 연산자를 두 번 사용하는 방법
!!'false' // true

9.4 단축 평가

9.4.1 논리 연산자를 사용한 단축 평가

'Cat' && 'Dog' // "Dog"

논리곱 연산자는 두 개의 피연산자가 모두 true로 평가될 때 true를 반환함.
논리곱 연산자는 좌항에서 우항으로 평가가 진행됨.

논리곱 연산자는 논리 연산의 결과를 결정하는 두 번째 피연산자, 즉 문자열 'Dog'를 그대로 반환함.

'Cat' || 'Dog' // "Dog"

논리합 연산자는 두 피연산자 중 하나만 true로 평가돼도 true를 반환함.
마찬가지로 좌항에서 우항으로 평가 진행됨.

논리 연산의 결과를 결정한 첫 번째 피연산자, 문자열 'Cat'을 그대로 반환함.

논리곱, 논리합 연산자는 논리 연산의 결과를 결정하는 피연산자를 타입 변환하지 않고 그대로 반환함. 이를 단축 평가라고 함.

단축 평가는 표현식을 평가하는 도중에 평가결과가 확정된 경우 나머지 평가 과정을 생략하는 것을 말함.

true || anything -> true
false || anything -> anything
true && anything -> anything
false && anything -> false

단축 평가를 사용하면 어떤 조건이 Truthy 값일 때 무언가를 해야하는 상황에서
논리곱 연산자 표현식으로 if문을 대체할 수 있음.

9.4.2 옵셔널 체이닝 연산자

ES11에서 도입된 옵셔널 체이닝 연산자 ?.는 좌항의 피연산자가 null 또는 undefined인 경우 undefined 반환, 그렇지 않으면 우항의 프로퍼티 참조를 이어감.
좌항 피연산자가 false로 평가되는 Falsy 값이라도 null 또는 undefined가 아니면 우항의 프로퍼티 참조를 이러감.

9.4.3 null 병합 연산자

마찬가지로 ES11에서 도입된 null 병합 연산자 ??는 좌항의 피연산자가 null 또는 undefined인 경우 우항의 피연산자를, 아닌 경우 좌항의 피연산자를 반환한다.
변수에 기본값을 설정할 때 유용함.

10장 객체 리터럴

10.1 객체란?

자바스크립트는 객체 기반의 프로그래밍 언어이고, 자바스크립트를 구성하는 거의 "모든 것"이 객체다.
원시 값을 제외한 나머지 값(함수, 배열, 정규 표현식 등)은 모두 객체다.

원시 타입은 단 하나의 값만 나타내지만 객체 타입은 다양한 타입의 값(원시 값 또는 다른 객체)을 하나의 단위로 구성한 복합적인 자료구조다.

원시 값은 변경 불가능한 값(immutable value)이지만,
객체는 변경 가능한 값(mutable value)이다.

객체는 0개 이상의 프로퍼티로 구성된 집합이며, 프로퍼티는 키와 값으로 구성된다.
프로퍼티 값이 함수일 경우, 일반 함수와 구분하기 위해 메서드라 부른다.

10.2 객체 리터럴에 의한 객체 생성

자바스크립트는 프로토타입 기반 객체지향 언어로, 클래스 기반 객체지향 언어와 달리 다양한 객체 생성 방법을 지원함.

  • 객체 리터럴
  • Object 생성자 함수
  • 생성자 함수
  • Object.create 메서드
  • 클래스(ES6)

이 중 가장 일반적이고 간단한 방법은 객체 리터럴을 사용하는 방법.

var empty = {}; // 빈 객체

10.3 프로퍼티

프로퍼티 키와 값으로 사용할 수 있는 값

프로퍼티 키: 빈 문자열 포함 모든 문자열 또는 심벌 값
프로퍼티 값: 자바스크립트에서 사용할 수 있는 모든 값

식별자 네이밍 규칙 따르지 않는 이름에는 반드시 따옴표 사용해야 함.

10.4 메서드

객체의 프로퍼티 값으로 할당된 함수를 메서드(method)라고 부른다.

메서드는 객체에 저장된 데이터에 접근할 수 있는 동시에 데이터를 조작하는 동작을 수행할 수 있다.

메서드 내부에서 사용된 this는 메서드를 호출한 객체, 즉 메서드를 호출한 객체의 컨텍스트에 바인딩된다.

10.5 프로퍼티 접근

객체는 프로퍼티 키를 사용해 프로퍼티 값에 접근할 수 있다.

프로퍼티 값에 접근하는 방법은 두 가지가 있다.

마침표 표기법(dot notation)

대괄호 표기법(bracket notation)

10.6 프로퍼티 값 갱신

객체는 동적으로 프로퍼티를 추가, 삭제, 갱신할 수 있다.

10.7 프로퍼티 동적 생성

객체는 동적으로 프로퍼티를 생성할 수 있다.

대괄호 표기법을 사용하면 식별자 네이밍 규칙을 준수하지 않는 이름도 프로퍼티 키로 사용할 수 있다.

10.8 프로퍼티 삭제

delete 연산자를 사용하면 객체의 프로퍼티를 삭제할 수 있다.

10.9 ES6에서 추가된 객체 리터럴의 확장 기능

ES6에서는 객체 리터럴의 기능이 확장되어 프로퍼티 축약 표현, 계산된 프로퍼티 이름, 메서드 축약 표현 등의 기능이 추가되었다.

10.9.1 프로퍼티 축약 표현

객체 리터럴의 프로퍼티 값으로 변수를 사용하는 경우 변수 이름과 프로퍼티 키가 동일한 이름일 때 축약 가능하다.

10.9.2 계산된 프로퍼티 이름

객체 리터럴 내부에서 대괄호([])를 사용하면 표현식을 작성할 수 있으며, 이를 계산된 프로퍼티 이름(computed property name)이라 한다.

10.9.3 메서드 축약 표현

ES6에서는 메서드를 정의할 때 function 키워드를 생략할 수 있는 메서드 축약 표현이 추가되었다.

11장 원시 값과 객체의 비교

원시 값과 객체는 서로 구별되는 데이터 타입이다.

원시 값은 변경 불가능한 값(immutable value)으로, 변수에 할당하면 변수 자체가 값으로 바뀐다.

반면 객체는 변경 가능한 값(mutable value)으로, 변수에 객체를 할당하면 변수는 참조 값(메모리 상의 주소)을 가리키게 된다.

따라서 원시 값은 값에 의한 전달(pass by value)이고, 객체는 참조에 의한 전달(pass by reference)이다.

11.1 원시 값

11.1.1 변경 불가능한 값

변수의 값은 변경할 수 있지만 변수 값에 대한 진술은 변경 불가능하다.

불변성을 갖는 원시 값을 할당한 변수는 재할당 이외에 변수 값을 변경할 방법이 없다.

11.1.2 문자열과 불변성

이미 생성된 문자열의 일부 문자를 변경해도 반영되지 않는다.

새로운 문자열 재할당하는 것은 가능함.

11.1.3 값에 의한 전달

할당받는 변수에 할당되는 변수의 원시 값이 복사되어 전달된다.

하지만 두 변수는 다른 메모리 공간에 저장된 별개의 값이다.

엄격하게 표현하면 변수에는 값이 아닌 메모리 주소가 전달된다.

변수와 같은 식별자는 값이 아니라 메모리 주소를 기억하고 있기 때문.

11.2 객체

프로퍼티 개수 정해져 있지 않고, 동적으로 추가되고 삭제됨.

확보해야 할 메모리 공간 크기 사전에 정해 둘 수 없음.

11.2.1 변경 가능한 값

객체를 할당한 변수가 기억하는 메모리 주소를 통해 메모리 공간에 접근하면 참조 값에 접근할 수 있다.

객체를 할당한 변수는 재할당 없이 객체를 직접 변경할 수 있다.

여러 개의 식별자가 하나의 객체를 공유할 수 있다.

11.2.2 참조에 의한 전달

객체를 가리키는 변수를 다른 변수에 할당하면 원본의 참조 값이 복사되어 전달된다. (참조에 의한 전달)

값에 의한 전달과 참조에 의한 전달은 식별자가 기억하는 메모리 공간에 저장된 값을 복사해서 전달한다는 면에서 동일함.

자바스크립트에는 참조에 의한 전달은 존재하지 않고 “값에 의한 전달”만이 존재한다고 말할 수 있음.

하지만 전달되는 값의 종류가 원시 값인지 참조 값인지 구별하는 의미에서

“값에 의한 전달”과 “참조에 의한 전달”로 구분하여 부르기로 하자.

profile
어제보다 잘하고 싶은 개발자 Kozi 입니다.

0개의 댓글