diff --git a/README.md b/README.md index 51b5f9c..c392ae7 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,10 @@ ## 모던 자바스크립트가 다루는 개념 +[Let Declaration](#let-declaration) + +[Const Declaration](#const-declaration) + [Spread Syntax](#spread-syntax) [Destructuring Assignment](#destructuring-assignment) @@ -51,29 +55,115 @@ [Named Parameter](#named-parameter) -[Truthy and Falsy](#truthy-and-falsy) +[Promise](#promise) + +[Async Function](#async-function) + +--- + +# Let Declaration + +### 정의 + +블록 레벨 유효 범위를 갖는 지역 변수를 선언합니다. + +### 특징 + +`let` 으로 선언한 변수는 재선언할 수 없습니다. + +`let` 으로 선언한 변수는 값을 재할당 할 수 있습니다. + +`let` 은 블록 레벨 유효 범위를 가집니다. 블록 레벨은 모든 코드 블록을 의미합니다. 예를 들어 `함수`, `if`, `switch`, `for`, `while`, `try-catch`, ... 내부에서 선언된 `let` 변수는 외부에서 접근할 수 없습니다. -[Array.prototype.map](#arrayprototypemap) +### 참고 -Array.prototype.filter +`let` 으로 선언된 변수들은 코드를 읽을 때 변함(`mutable`)을 기대하게 만듭니다. 이는 코드 리뷰어가 해당 변수가 언제 바뀌는지 계속해서 추적하게 만듭니다. 이는 굉장히 피곤한 일입니다. 따라서 변하지 않는 변수들은 **언제나** 상수(`const`)로 선언하세요. -Array.prototype.includes +### 1. 재선언 불가능 -Array.prototype.every +👉시나리오: `let` 이 `var` 과 다르게 재선언 할 수 없다는 걸 증명하세요. -Array.prototype.some +```js +var a +var a // 재선언 통과 + +let b +let b // -> SyntaxError: Identifier 'b' has already been declared 👍 +``` + +### 2. 재할당 -Array.prototype.reduce +👉시나리오: `let` 이 `const` 와 다르게 재할당 가능한 걸 증명하세요. -String.prototype.split +```js +let a = 1 +a = 2 // 재할당 가능 👍 + +const b = 1 +b = 2 // -> TypeError: Assignment to constant variable. +``` -String.prototype.join +### 3. 유효 범위 -Object.entries +👉시나리오: `let` 이 `var` 과 다르게 블록 유효 범위를 가지는걸 증명하세요. -Object.values +```js +if (true) { + var a = 1 + let b = 1 +} -Object.keys +console.log(a) // -> 1 +console.log(b) // -> ReferenceError: b is not defined 👍 +``` + +--- + +# Const Declaration + +### 정의 + +블록 레벨 유효 범위를 갖는 **상수**를 선언합니다. + +### 특징 + +`const` 는 선언과 동시에 할당해야 합니다. 즉 언제나 초깃값을 필요로 합니다. + +`const` 는 재할당 할 수 없습니다. + +`const` 는 `let` 과 동일하게 블록 레벨 유효 범위를 가집니다. + +### 1. 선언과 동시에 할당 + +👉시나리오: `const` 가 선언과 동시에 할당해야 함을 증명하세요. + +```js +const a = 1 +const b // -> SyntaxError: Missing initializer in const declaration +``` + +### 2. 재할당 불가능 + +👉시나리오: `const` 가 재할당 불가능함을 증명하세요. + +```js +const a = 1 +a = 2 // -> TypeError: Assignment to constant variable +``` + +### 3. 유효 범위 + +👉시나리오: `const` 가 `let` 과 동일하게 블록 레벨 유효 범위를 가지는 것을 증명하세요. + +```js +if (true) { + var a = 1 + const b = 1 +} + +console.log(a) // -> 1 +console.log(b) // -> ReferenceError: b is not defined 👍 +``` --- @@ -216,6 +306,25 @@ console.log('자동차', car) 배열을 구조 분해해봅시다. +👉 시나리오: 배열 `arr` 의 첫 번째와 세 번째 원소를 각각 `first` 와 `third` 변수에 할당 및 선언하세요. + +### 불—편 + +```js +const arr = [1, 2, 3, 4] + +const first = arr[0] // 좀 더 심플하게 안될까요? +const third = arr[2] // 인덱스로 접근하지 않으면서요 +``` + +### 편—안 ✅ + +```js +const arr = [1, 2, 3, 4] + +const [first, , third] = arr // 좋습니다. 👍 +``` + --- # Rest Parameters @@ -513,179 +622,230 @@ render({ --- -# Truthy and Falsy +# Promise -참과 거짓은 Boolean 을 기대하는 문맥에서 `true` `false` 로 평가되는 값입니다. +### 정의 -## 1. 거짓 값 +Promise 는 **비동기 작업**의 **상태**를 보관하고 추적합니다. -자바스크립트에는 총 7 가지 거짓 값이 존재합니다. +### 특징 (중요 🐉) -`false` +Promise 는 다음 중 하나의 **상태**를 가집니다. -`0` +- 대기(*pending)*: 이행하거나 거부되지 않은 초기 상태. +- 이행(*fulfilled)*: 연산이 성공적으로 완료됨. +- 거부(*rejected)*: 연산이 실패함. -`0n` +[출처: [MDN - Promise](https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Promise)] -`''` +Promise 는 주어진 콜백 함수를 **단 한 번** 실행하며 그 **결과**를 보관합니다. 이러한 특성 때문에 Promise 의 상태는 한 번 결정되면 수정되지 않습니다. -`null` +다시 말해 이미 소비(이행되거나 거부) 된 Promise 는 재사용 할 수 없습니다. -`undefined` +### 1. 성공 케이스 (fulfilled) -`NaN` +### 활용 1: 비동기 호출의 상태를 추적하기 -## 활용 1: 배열이 비어있나 확인하기 +👉시나리오: 3초 후 주문한 음식이 배달됩니다. 도착 시 이를 출력하세요. -배열은 `length` 프로퍼티를 가지고 있습니다. 이를 검사해 배열이 비어있는지 확인 가능합니다. +👉조건1: 배달이 도착하기 전에는 절대로 빨래를 시작하지 마세요. ### 불—편 ```js -const arr = [1, 2, 3] - -if (arr.length === 0) { // 좀 더 심플하게 안될까요? - console.log('배열이 비어있음!') +function delivery () { + setTimeout(() => { console.log('배달 도착') }, 3000) } + +// 배달을 요청합니다. (비동기 함수 호출) +// 그러나 배달의 현재 상태를 추적하지는 않습니다. +delivery() +console.log('빨래 시작') + +// 결과 👎 +// -> 빨래 시작 ... 배달이 도착하기도 전에 빨래를 시작했습니다. +// -> 배달 도착 ``` + ### 편—안 ✅ ```js -const arr = [1, 2, 3] - -if (!arr.length) { // 👍 좋습니다! 0 은 Falsy 기 때문에 배열이 비어있다면 조건을 만족합니다. - console.log('배열이 비어있음!') +function delivery () { + return new Promise((resolve) => { + setTimeout(() => { + console.log('배달 도착') + resolve() + }, 3000) + }) } -``` -## 활용 2: 안전히 메소드 호출하기 +delivery().then(() => { + console.log('빨래 시작') // 배달이 도착 한 후 빨래를 시작합니다. +}) -존재하지 않는 함수를 호출하면 `TypeError: ... is not a function` 를 `throw` 합니다. +// 결과 👍 +// -> 배달 도착 +// -> 빨래 시작 -이를 방지하고자 방어적인 코드를 작성해봅시다. +``` -### 불—편 +### 2. 실패 케이스 (rejected) -```js -const notArray = 5000 +👉시나리오: 3초 후 주문한 음식이 배달됩니다. 그러나 월요일은 휴무입니다. -notArray.forEach(it => console.log(it)) // -> TypeError: notArray.forEach is not a function -``` +👉조건1: 휴무일에는(배달 거부 시) 맥도날드에 방문하세요. ### 편—안 ✅ ```js -const iAmNotArray = 5000 - -if (iAmNotArray.forEach) { - iAmNotArray.forEach(it => console.log(it)) // 👍 +function delivery (day) { + return new Promise((resolve, reject) => { + if (day === '월요일') { + reject(new Error()) + } + + setTimeout(() => { + resolve() + }, 3000) + }) } -``` -## 2. 참 값 +delivery('월요일').then(() => { + console.log('음식 받기') +}).catch(() => { + console.log('맥도날드 방문하기') +}) -위에서 언급한 거짓 값을 제외하면 모두 참 값입니다. +// 결과 👍 +// -> 맥도날드 방문하기 +``` + -## 활용1: 객체 요소 검사 +### 3. 일회성 -`movie` 객체를 참조해 <아이언맨> 이 한글 자막을 지원하는지 확인해 봅시다. +Promise 는 비동기 함수 호출의 결과를 정확히 보관할 의무를 가집니다. 한 번 정해진 결과는 수정되지 않으며 이를 지키기 위해서 주어진 실행 함수(`executor`)를 단 한 번만 실행합니다. -### 편—안 ✅ +👉시나리오: Promise 가 일회성이라는 것을 증명하세요. ```js -const movie = { - ironman: { - ko: '아이언맨 4', - en: 'Iron Man 4' - } -} +// new Promise 를 한순간 이미 프로미스 소진 +const promise = new Promise((resolve) => { + console.log('안녕') + resolve() +}) -// 좋습니다. 👍 -if (movie.ironman.ko) { - console.log('한글 자막 개봉') -} +promise.then() +promise.then() +promise.then() + +// 결과 👍 +// -> 안녕 ... 단 한 번 출력됨 ``` --- -# Array.prototype.map +# Async Function ### 정의 -`map` 은 주어진 배열의 **모든 원소**에 규칙(`callback`)을 적용한 **새로운 배열**을 생성합니다. - -### 특징 (중요🐉) +Promise 객체를 반환하는 함수 -`map` 은 주어진 배열을 변형하지 않습니다. +### 특징 -`map` 은 첫 `callback` 함수가 실행되기 직전에 주어진 배열의 복사본을 생성합니다. 때문에 `callback` 함수를 실행하는 동안에 주어진 배열이 변형되어도 결과에 영향을 미치지 않습니다. +Async Function 은 `await` 키워드를 사용 가능케 해줍니다. `await` 키워드는 Async Function 에서만 유효합니다. -`map` 은 주어진 배열의 모든 원소에 `callback` 함수를 수행하는 것이 목표입니다. 따라서 중간에 `break or continue` 호출은 불가합니다. +`await` 키워드는 Async Function 이 끝나길(`return or throw`) 기다립니다. -다시 말해 `map` 은 반복문이 아닙니다. +Async Function 는 `return` 혹은 `throw` 문으로 끝낼 수 있습니다. -`map` 을 탈출하는 방법은 에러를 `throw` 하는 겁니다. 하지만 이 방법을 되도록 사용하지 마세요. `for` 문을 고려하는 게 더 나을지도 모릅니다. +Async/Await 을 사용하면 일련의 비동기 작업을 비교적 쉽게 동기 방식으로 호출 가능케 해줍니다. -### 예제 1 +### 참고 -👉 시나리오: 배열 `numbers` 의 모든 원소에 2 를 곱한 새로운 배열 `result` 를 만드세요. +예시 코드들은 간결한 코드를 보여주기 위해 `top-level-await` 기능이 탑재된 환경에서 진행했습니다. `top-level-await` 은 ECMAScript proposal stage 3 상태며 자세한 내용은 아래 링크를 참고해 주세요. -### 불—편 +[tc39/proposal-top-level-await - GitHub](https://github.com/tc39/proposal-top-level-await) -```js -const numbers = [1, 2, 3, 4, 5] -const result = [] +### 1. 비동기 작업하기 -// 장황합니다. 👎 -for (let i = 0; i < numbers.length; i++) { - result.push(numbers[i] * 2) -} +👉시나리오: 구글 검색 페이지를 크롤링 해오세요. 크롤링을 완료하는 것을 기다리지 말고 `이메일을 전송` 하세요. -console.log(result) // -> [ 2, 4, 6, 8, 10 ] -``` +👉조건: Async Function 을 활용하세요. ### 편—안 ✅ ```js -const numbers = [1, 2, 3, 4, 5] -const result = numbers.map(number => { return number * 2 }) +async function google () { + // ... + console.log('구글 크롤링 완료') +} + +google() // 비동기적으로 실행됩니다. +console.log('이메일 전송') -console.log(result) // -> [ 2, 4, 6, 8, 10 ] +// 결과 👍 +// -> 이메일 전송 +// -> 구글 크롤링 완료 ``` -### 예제 2 +### 2. 비동기 작업을 기다리기 -👉 시나리오: 배열 `cities` 의 모든 원소에 `광역시` 문자열을 뒤에 붙인 새로운 배열 `metropolitanCities` 을 만드세요. +👉시나리오: 구글 검색 페이지를 크롤링 해오세요. 크롤링을 완료한 뒤 `이메일을 전송` 하세요. -### 불—편 +👉조건: Async Function 을 활용하세요. +### 편—안 ✅ ```js -const cities = ['부산', '대구', '인천', '광주'] -const metropolitanCities = [] - -// 장황합니다. 👎 -for (let i = 0; i < cities.length; i++) { - metropolitanCities.push(cities[i] + '광역시') +async function google () { + // ... + console.log('구글 크롤링 완료') } -console.log(metropolitanCities) // -> [ '부산광역시', '대구광역시', '인천광역시', '광주광역시' ] +await google() // await 키워드가 동기적으로 실행하도록 만들어줍니다. 👍 +console.log('이메일 전송') + +// 결과 👍 +// -> 구글 크롤링 완료 +// -> 이메일 전송 ``` +### 3. 결과 다루기 + +### 활용 1: 성공 (return) + +👉시나리오: `getUser` 함수로 사용자 정보를 가져오세요. 결과를 받아 `name` 필드의 값을 출력하세요. + +👉조건: `await` 키워드로 `getUser` 함수가 반환하는 값을 받아오세요. ### 편—안 ✅ ```js -const cities = ['부산', '대구', '인천', '광주'] -const metropolitanCities = cities.map(city => { return `${city}광역시` }) // 👍 +async function getUser () { + const result = await dbQuery() + return result +} -console.log(metropolitanCities) // -> [ '부산광역시', '대구광역시', '인천광역시', '광주광역시' ] +const user = await getUser() // await 키워드가 getUser 함수의 반환을 기다립니다. 👍 + +console.log(user.name) ``` -### 좀 더 심플하게 ✅ +### 활용2: 실패 (throw) + +👉시나리오: 배달 음식을 주문하세요. 배달이 도착하면(`return`) 음식을 먹고, 만약 주문이 취소되면(`throw`) 방문 포장을 해오세요. + +### 편—안 ✅ ```js -const cities = ['부산', '대구', '인천', '광주'] -const metropolitanCities = cities.map(city => `${city}광역시`) // 👍 +async function delivery () { + // ... + throw new Error('주문 취소!') +} -console.log(metropolitanCities) // -> [ '부산광역시', '대구광역시', '인천광역시', '광주광역시' ] +try { + await delivery() // await 키워드가 덕분에 비동기 호출의 실패를 다룰수 있습니다. 👍 + // ... 배달 음식 먹기 +} catch (error) { + // ... 방문 포장 해오기 +} ```