├── 04장-변수 └── README.md ├── 05장-표현식과-문 ├── README.md └── img │ └── README │ └── image-20211002062333737.png ├── 06장-데이터-타입 └── README.md ├── 07장-연산자 └── README.md ├── 08장-제어문 └── README.md ├── 09장-타입-변환과-단축-평가 └── README.md ├── 10장-객체-리터럴 └── README.md ├── 11장-원시-값과-객체의-비교 └── README.md ├── 12장-함수 └── README.md ├── 13장-스코프 └── README.md ├── 14장-전역-변수의-문제점 └── README.md ├── 15장-let,const키워드와-블록-레벨-스코프 └── README.md ├── 16장-프로퍼티-어트리뷰트 └── README.md ├── 17장-생성자-함수에-의한-객체-생성 └── README.md ├── 18장-함수와-일급-객체 └── README.md ├── 19장-프로토타입(19.1~) └── README.md ├── 19장-프로토타입(19.8~) ├── README.md └── img │ └── README │ ├── 1.jpg │ ├── 2.jpg │ ├── 3.jpg │ └── 4.jpg ├── 20장-strict-mode └── README.md ├── 21장-빌트인-객체 └── README.md ├── 22장-this └── README.md ├── 23장-실행-컨텍스트 └── README.md ├── 24장-클로저 └── README.md ├── 25장-클래스(25.1~) └── README.md ├── 25장-클래스(25.7~) └── README.md ├── 26장-ES6-함수의-추가-기능 └── README.md ├── 33장-Symbol └── README.md ├── 34장-이터러블 └── README.md ├── 35장-스프레드-문법 └── README.md ├── 36장-디스트럭처링-할당 └── README.md ├── 37장-Map └── README.md ├── 37장-Set └── README.md ├── 38장-브라우저의-렌더링-과정 └── README.md ├── 39장-DOM(39.5~) └── README.md ├── 39장-DOM(~39.4) └── README.md ├── 40장-이벤트 └── README.md ├── 41장-타이머 └── README.md ├── 42장-비동기-프로그래밍 └── README.md ├── 43장-Ajax └── README.MD ├── 44장-REST-API ├── README.md └── img │ └── README │ ├── 1.jpg │ ├── 2.jpg │ ├── 3.jpg │ ├── 4.jpg │ ├── 5.jpg │ ├── image-20211109112411679.png │ └── image-20211109112759015.png ├── 45장-프로미스 └── README.md ├── 46장-1-제너레이터(46.1~) └── README.md ├── 46장-6-async-await(46.6~) └── README.md ├── 47장-에러-처리 └── README.md ├── 48장-모듈 └── READMD.md ├── 49장-Babel과-Webpack └── READMD.md └── README.md /04장-변수/README.md: -------------------------------------------------------------------------------- 1 | - # 4장. 변수 2 | 3 | ### 4.1 변수란 무엇인가? 왜 필요한가? 4 | 5 | --- 6 | 7 | - 컴퓨터는 `CPU`를 사용해 `연산`하고, `메모리`를 사용해 데이터를 `기억`한다. 8 | 9 | > `메모리`는 데이터를 저장할 수 있는 메모리 셀의 집합체다. 10 | > 11 | > 메모리 셀 하나의 크기는 1바이트(8비트)이며, 컴퓨터는 메모리 셀의 크기, 즉 1바이트 단위로 데이터를 저장하거나 읽어들인다. 12 | > 13 | > 각 셀은 고유의 메모리 주소를 갖는다. 이 메모리 주소는 메모리 공간의 위치를 나타내며, 0부터 시작해서 메모리의 크기만큼 정수로 표현된다. 14 | 15 |
16 | 17 | - 메모리 주소를 통해 값에 직접 접근하는 것은 치명적 오류를 발생시킬 가능성이 높은 매우 위험한 일이다. 18 | - ex) 만약, 실수로 운영체제가 사용하고 있는 값을 변경하면 시스템을 멈추게 하는 치명적인 오류가 발생할 수도 있다. 19 | 20 | - 따라서 자바스크립트는 개발자의 직접적인 메모리 제어를 허용하지 않는다. 21 | 22 |
23 | 24 | - 프로그래밍 언어는 기억하고 싶은 값을 메모리에 저장하고, 저장된 값을 읽어 들여 재사용하기 위해 `변수`라는 메커니즘을 제공한다. 25 | 26 | - `변수`는 하나의 값을 저장하기 위해 확보한 메모리 공간 자체 또는 그 메모리 공간을 식별하기 위해 붙인 이름을 말한다. 즉, 값의 위치를 가리키는 상징적인 이름이다. 27 | - `변수`는 프로그래밍 언어의 컴파일러 또는 인터프리터에 의해 값이 저장한 메모리 공간의 주소로 치환되어 실행된다. 28 | - 값이 저장될 `메모리 주소`는 코드가 실행될 때 메모리의 상황에 따라 임의로 결정된다. 따라서, 동일한 컴퓨터에서 동일한 코드를 실행해도 코드가 실행될 때마다 값이 저장될 메모리 주소는 변경된다. 29 | 30 |
31 | 32 | - 메모리 공간에 저장된 값을 식별할 수 있는 고유한 이름을 `변수 이름` 이라 한다. 33 | - 변수에 저장된 값을 `변수 값`이라 한다. 34 | - 변수에 값을 저장하는 것을 `할당`이라 하고 35 | - 변수에 저장된 값을 읽어 들이는 것을 `참조`라 한다. 36 | - `변수 이름`은 사람을 위해 사람이 이해할 수 있는 언어로 값이 저장된 메모리 공간에 붙인 상징적인 이름이다. 변수 이름을 사용해 참조를 요청하면 자바스크립트 엔진은 `변수 이름`과 `매핑된 메모리 주소`를 통해 `메모리 공간`에 접근해서 `저장된 값`을 반환한다. 37 | 38 |
39 | 40 | ### 4.2 식별자 41 | 42 | --- 43 | 44 | - `식별자`는 어떤 값을 구별해서 식별할 수 있는 고유한 이름을 말한다. 45 | - 식별자는 메모리 공간에 저장되어 있는 어떤 값을 구별해서 식별해낼 수 있어야 한다. 이를 위해, 식별자는 어떤 값이 저장되어 있는 `메모리 주소`를 기억(저장)해야 한다. 46 | - 메모리 상에 존재하는 어떤 값을 식별할 수 있는 이름은 모두 `식별자`라고 부른다. 47 | - `선언`에 의해 자바스크립트 엔진에 식별자의 존재를 알린다. 48 | 49 |
50 | 51 | ### 4.3 변수 선언 52 | 53 | --- 54 | 55 | - `변수 선언`이란 `변수를 생성하는 것`을 말한다. 56 | 57 | - 값을 저장하기 위한 `메모리 공간`을 `확보`하고, 변수 이름과 확보된 메모리 공간의 주소를 `연결`해서 값을 저장할 수 있게 준비하는 것이다. 58 |
59 | 60 | - 변수를 사용하려면 반드시 선언이 필요하다. 변수를 선언할 때는 var, let, const 키워드를 사용한다. 61 | - ex) var 키워드는 뒤에 오는 변수 이름으로 새로운 변수를 선언할 것을 지시하는 키워드이다. 62 | 63 | > `키워드`: 자바스크립트 코드를 해석하고 실행하는 자바스크립트 엔진이 수행할 동작을 규정한 일종의 명령어 64 | 65 |
66 | 67 | - 변수 선언에 의해 확보된 `메모리 공간`은 비어 있을 것으로 생각할 수 있으나 `확보된 메모리 공간`에는 자바스크립트 엔진에 의해 `undefined`라는 값이 암묵적으로 `할당`되어 `초기화`된다. 68 | 69 | > `초기화`: 변수가 선언된 이후 최초로 값을 할당하는 것 70 | 71 |
72 | 73 | - 자바스크립트 엔진은 변수 선언을 다음과 같은 2단계에 거쳐 수행한다. 74 | - **선언 단계** - 변수 이름을 등록해서 자바스크립트 엔진에 변수의 존재를 알린다. 75 | - **초기화 단계** - 값을 저장하기 위한 메모리 공간을 확보하고 암묵적으로 undefined를 할당해 초기화한다. 76 | 77 |
78 | 79 | - var 키워드를 사용한 변수 선언은 선언 단계와 초기화 단계가 동시에 진행된다. 80 | 81 | - 초기화 단계를 거치지 않으면 확보된 메모리 공간에는 이전에 다른 애플리케이션이 사용했던 값이 남아있을 수 있다. 이러한 값을 `쓰레기 값`이라 한다. 82 | 83 | - 선언하지 않은 식별자에 접근하면 `ReferenceError(참조에러)`가 발생한다. 84 | 85 |
86 | 87 | ### 4.4 변수 선언의 실행 시점과 변수 호이스팅 88 | 89 | --- 90 | 91 | - 아래 코드에서 참조에러가 발생하지 않고 `undefined`가 출력이 되는 이유는 변수 선언이 소스코드가 한 줄씩 순차적으로 실행되는 시점, 즉 런타임이 아니라 그 이전 단계에서 먼저 실행되기 때문이다. 92 | 93 | ```javascript 94 | console.log(score); // undefined 95 | var score; // 변수 선언문 96 | ``` 97 | 98 |
99 | 100 | 101 | - 자바스크립트 엔진은 소스코드를 한 줄씩 순차적으로 실행하기에 앞서 먼저 소스코드의 평가 과정을 거치면서 소스코드를 실행하기 위한 준비를 한다. 102 | - 이때, 소스코드 실행을 위한 준비 단계인 소스코드의 평가 과정에서 자바스크립트 엔진은 변수 선언을 포함한 모든 선언문(변수 선언문, 함수 선언문등)을 소스코드에서 찾아내 먼저 실행한다. 103 | - 그리고, 소스코드의 평가 과정이 끝나면 비로소 변수 선언을 포함한 모든 선언문을 제외하고 소스코드를 한 줄식 순차적으로 실행한다. 104 | - 즉, 자바스크립트 엔진은 변수 선언이 어디에 있든 상관없이 다른 코드보다 먼저 실행된다. 105 | - 이처럼 변수 선언문이 코드의 선두로 끌어 올려진 것처럼 동작하는 자바스크립트 고유의 특징을 변수 `호이스팅`이라 한다. 106 | 107 |
108 | 109 | ### 4.5 값의 할당 110 | 111 | --- 112 | 113 | - 변수에 값을 할당할 때는 `할당 연산자 =`를 사용한다. 할당 연산자는 우변의 값을 좌변의 변수에 할당한다. 114 | 115 | ```javascript 116 | var score; // 변수 선언 117 | score = 80 // 값의 할당 118 | ``` 119 |
120 | 121 | - 자바스크립트 엔진은 변수 선언과 값의 할당을 하나의 문으로 단축 표현해도 변수 선언과 값의 할당을 2개의 문으로 나누어 각각 실행한다. 122 | 123 | - 변수 선언은 소스코드가 순차적으로 실행되는 시점인 런타임 이전에 먼저 실행되지만 `값의 할당`은 소스코드가 순차적으로 실행되는 시점인 런타임에 실행된다. 124 | 125 | ```javascript 126 | console.log(score); // undefined 127 | 128 | var score; 129 | score = 80; 130 | 131 | console.log(score); // 80 132 | ``` 133 | 134 | - 변수에 값을 할당할 때는 이전 값 `undefined`가 저장되어 있는 메모리 공간을 지우고 그 메모리 공간에 할당 값 80을 새롭게 저장하는 것이 아니라 새로운 메모리 공간을 확보하고 그곳에 할당 값 80을 저장한다. 135 | 136 |
137 | 138 | ### 4.6 값의 재할당 139 | 140 | --- 141 | 142 | - `재할당`이란 이미 값이 할당되어 있는 변수에 새로운 값을 또다시 `할당`하는 것을 말한다. 143 | - var 키워드로 선언한 변수는 선언과 동시에 `undefined`로 초기화되기 때문에 엄밀히 말하자면 변수에 처음으로 값을 할당하는 것도 사실은 `재할당`이다. 144 | - 값을 재할당할 수 없어서 변수에 저장된 값을 변경할 수 없다면 변수가 아니라 `상수`다. 145 | - `상수`는 단 `한 번만` 할당할 수 있는 `변수`다. 146 | - `변수에 값을 재할당`할때는 처음에 변수에 값을 할당할 때처럼 이전 값이 저장되어 있던 메모리 공간에 새로운 값을 저장하는 것이 아니라 `새로운 메모리 공간`을 확보하고 그 `메모리 공간에 새로운 값을 저장하는 것`이다. 147 | - 이후, 어떤 식별자와도 연결되어 있지 않은 불필요한 값들은 `가비지 컬렉터`에 의해 메모리에서 자동 해제된다. 단, 메모리에서 언제 해제될지는 예측할 수 없다. 148 | 149 |
150 | 151 | ### 4.7 식별자 네이밍 규칙 152 | 153 | --- 154 | 155 | - 식별자는 다음과 같은 `네이밍 규칙`을 준수해야한다. 156 | 157 | - 식별자는 특수문자를 제외한 문자, 숫자, 언더스코어(_), 달러 기호($)를 포함할 수 있다. 158 | - 단, 식별자는 특수문자를 제외한 문자, 언더스코어(_), 달러 기호($)로 시작해야한다. 숫자롤 시작하는 것은 허용하지 않는다. 159 | - 예약어는 식별자로 사용할 수 없다. 160 | - 예약어는 프로그래밍 언어에서 사용되고 있거나 사용될 예정인 단어를 말한다. (ex. await, continue, if, break등등) 161 | 162 | - 변수는 쉼표로 구분해 `하나의 문`에서 `여러 개`를 `한번`에 선언할 수 있다. 단, 가독성이 나빠지므로 권장하지는 않는다. 163 | 164 | ```javascript 165 | var person, $elem, _name, first_name 166 | ``` 167 | 168 | 169 |
170 | 171 | - ES5부터 유니코드 문자를 허용하므로 알파벳 외의 한글이나 일본어 식별자도 사용할 수 있다. 172 | 173 | - 다음 식별자는 명명 규칙에 위배되므로 변수 이름으로 사용할 수 없다. 174 | 175 | ```javascript 176 | var first-name; // SyntaxError: Unexpected token 177 | var 1st; // SyntaxError: Invalid or unexpected token 178 | var this; // SyntaxError: Unexpected token this 179 | ``` 180 | 181 | 182 |
183 | 184 | - 자바스크립트는 `대소문자`를 구별한다. 185 | 186 | - 자바스크립트에서는 변수나 함수의 이름에는 `카멜 케이스`를 사용하고, 생성자 함수, 클래스의 이름에는 `파스칼 케이스`를 사용한다. 187 | 188 | ```javascript 189 | // 카멜 케이스(변수나 함수의 이름) 190 | var firstName; 191 | 192 | // 파스칼 케이스(생성자 함수, 클래스의 이름) 193 | var FirstName; 194 | ``` 195 | 196 | 197 | -------------------------------------------------------------------------------- /05장-표현식과-문/README.md: -------------------------------------------------------------------------------- 1 | # 5장 표현식과 문 2 | 3 | ## 5.1 값 4 | 5 | - 값 : **식(표현식)**이 **평가**되어 생성된 **결과** 6 | 7 | ```javascript 8 | // 10 + 20은 평가되어 숫자 값 30을 생성한다. 9 | 10 + 20; // 30 10 | ``` 11 | 12 | - 변수 : **하나의 값**을 저장하기 위해 확보한 **메모리 공간 자체** 또는 그 메모리 공간을 식별하기 위해 붙인 이름. 13 | 14 | ## 5.2 리터럴 15 | 16 | - 사람이 이해할 수 있는 문자 또는 약속된 기호를 사용해 값을 생성하는 표기법 17 | - 자바스크립트 엔진은 코드가 실행되는 시점인 런타임에 리터럴을 평가해 값을 생성한다. 18 | 19 | | 리터럴 | 예시 | 20 | | ------------------ | ------------------------------- | 21 | | 정수 리터럴 | 100 | 22 | | 부동소수점 리터럴 | 10.5 | 23 | | 2진수 리터럴 | 0b01000001 | 24 | | 8진수 리터럴 | 0o101 | 25 | | 16진수 리터럴 | 0x41 | 26 | | 문자열 리터럴 | 'Hello', "World" | 27 | | 불리언 리터럴 | true, false | 28 | | null 리터럴 | null | 29 | | undefined 리터럴 | undefined | 30 | | 객체 리터럴 | {name: 'Lee', address: 'Seoul'} | 31 | | 배열 리터럴 | [1,2,3] | 32 | | 함수 리터럴 | function(){} | 33 | | 정규 표현식 리터럴 | /[A-Z]+/g | 34 | 35 | ## 5.3 표현식 36 | 37 | - 표현식(expression)은 **값으로 평가**될 수 있는 문(statement)이다. 38 | - 표현식은 리터럴, 식별자(변수, 함수 등의 이름), 연산자, 함수 호출 등의 조합으로 이뤄질 수 있다. 39 | 40 | ```javascript 41 | // 예시 05-04 42 | var score = 100; 43 | //리터럴 100은 자바스크립트 엔진에 의해 평가되어 값을 생성하므로 그 자체로 표현식이다. 44 | 45 | // 예시05-05 46 | var score = 50 + 50; 47 | //50 + 50은 리터럴과 연산자로 이뤄져있다. 48 | //50 + 50도 평가되어 숫자 값 100을 생성하므로 표현식이다. 49 | 50 | // 예시05-06 51 | score; 52 | //score 변수 식별자를 참조했다. 53 | //식별자 참조는 값을 생성하지는 않지만 값으로 평가되므로 표현식이다. 54 | 55 | 56 | // 예시 05-07 57 | // 1.리터럴 표현식 58 | 10 59 | 'Hello' 60 | 61 | // 2.식별자 표현식(선언이 이미 존재한다고 가정) 62 | sum 63 | person.name 64 | arr[1] 65 | 66 | // 3.연산자 표현식 67 | 10 + 20 68 | sum = 10 69 | sum !== 10 70 | 71 | // 4.함수/메서드 호출 표현식(선언이 이미 존재한다고 가정) 72 | square() 73 | person.getName() 74 | ``` 75 | 76 | 77 | 78 | - 표현식과 표현식이 평가된 값은 동치(equivalent)다. 79 | - 표현식은 값처럼 사용할 수 있다. 값이 위치할 수 있는 자리에는 표현식도 위치할 수 있다. 80 | 81 | 82 | 83 | ## 5.4 문 84 | 85 | - 문(statememt) : 프로그램을 구성하는 기본 단위이자 최소 실행 단위. (명령문이라고도 한다.) 86 | - 토큰 : 문법적으로 더 이상 나눌 수 없는 코드의 기본 요소. 87 | - ex) 키워드, 식별자, 연산자, 리터럴, 세미콜론, 마침표 등 88 | 89 | ```javascript 90 | var sum = 1 + 2; 91 | //위의 한 문장 전체는 '문' 92 | //각각 var / sum / = / 1 / + / 2 / ; / 는 토큰이다. 93 | ``` 94 | 95 | - 문의 종류 : 선언문, 할당문, 조건문, 반복문 96 | 97 | ```javascript 98 | // 변수 선언문 99 | var x; 100 | 101 | // 표현식 문(할당문) 102 | x = 5; 103 | 104 | // 함수 선언문 105 | function foo () {} 106 | 107 | // 조건문 108 | if (x > 1) { console.log(x); } 109 | 110 | // 반복문 111 | for (var i = 0; i < 2; i++) { console.log(i); } 112 | ``` 113 | 114 | ## 5.5 세미콜론과 세미콜론 자동 삽입 기능 115 | 116 | - 세미콜론(;)은 문의 종료를 나타낸다. 117 | - 코드 블록(if문, for문, 함수 등의 코드 블록)은 문의 종료를 의미하는 자체 종결성을 갖기 때문에 세미콜론을 붙이지 않는다. 118 | 119 | 120 | 121 | - 세미콜론 자동 삽입 기능(ASI-Automatin semicolon insertion)이 암묵적으로 수행되지만, 개발자의 예측과 일치하지 않는 경우가 간혹 있기 때문에 세미콜론을 붙이는 것을 권장한다. 122 | 123 | - ESLint에서는 세미콜론 사용을 기본으로 설정함 124 | - TC39(ECMAScript 기술위원회)는 세미콜론 사용을 권장하는 분위기 125 | 126 | ## 5.6 표현식인 문과 표현식이 아닌문 127 | 128 | - 문에는 표현식인 문과 표현식이 아닌 문이 있다. 129 | 130 | | 표현식인 문 | 표현식이 아닌 문 | 131 | | ------------------------ | ------------------------ | 132 | | 값으로 평가될 수 있는 문 | 값으로 평가될 수 없는 문 | 133 | | 변수에 할당 가능 | 변수 할당시 에러가 발생 | 134 | | | | 135 | 136 | ```javascript 137 | // 변수 선언문은 표현식이 아닌 문이다. 138 | var x; 139 | 140 | // 할당문은 그 자체가 표현식이지만 완전한 문이기도 하다. 즉, 할당문은 표현식인 문이다. 141 | x = 100; 142 | 143 | // 표현식이 아닌 문은 값처럼 사용할 수 없다. 144 | var foo = var x; // SyntaxError: Unexpected token var 145 | 146 | // 표현식인 문은 값처럼 사용할 수 있다 147 | var foo = x = 100; 148 | console.log(foo); // 100 149 | ``` 150 | 151 | 152 | 153 | #### 완료값 154 | 155 | - 크롬 개발자 도구에서 표현식이 아닌 문을 실행하면 언제나 undefined를 출력한다. 이를 완료 값이라고 한다. 156 | - 완료값은 표현식의 평가 결과가 아니다. 따라서 다른 값과 같이 변수에 할당할 수 없고 참조할 수도 없다. 157 | - 크롬 개발자 도구에서 표현식인 문을 실행하면 언제나 평가된 값을 반환한다. 158 | 159 | ![image-20211002062333737](img/README/image-20211002062333737.png) -------------------------------------------------------------------------------- /05장-표현식과-문/img/README/image-20211002062333737.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ritajeong/javascript-study/2d7cbc387fb4bff05963e8bbe93b601a945f8cbe/05장-표현식과-문/img/README/image-20211002062333737.png -------------------------------------------------------------------------------- /06장-데이터-타입/README.md: -------------------------------------------------------------------------------- 1 | # 6장 데이터 타입 2 | 3 | | 구분 | 데이터타입 | 설명 | 4 | | -------- | ---------------- | ------------------------------------------------- | 5 | | 원시타입 | 숫자 (Number) | 정수와 실수 구분없이 하나의 숫자 타입만 존재 | 6 | | | 문자열 (String) | 문자열 | 7 | | | 불리언 (Boolean) | 논리적 참(true)과 거짓(false) | 8 | | | undefined | var 키워드로 선언된 변수에 암묵적으로 할당되는 값 | 9 | | | null | 값이 없다는 것을 의도적으로 명시할 때 사용하는 값 | 10 | | | 심벌 (Symbol) | ES6에서 추가된 7번째 타입 | 11 | | 객체타입 | | 객체,함수,배열 등 | 12 | 13 | 14 | 15 | ## 6.1 숫자 타입 16 | 17 | - #### 배정밀도 64비트 부동소수점 형식을 따른다. (모든 수를 실수로 처리한다.) 18 | 19 | - 정수, 실수, 2진수, 8진수, 16진수 리터럴은 모두 **메모리**에 배정밀도 64비트 부동소수점 형식의 2진수로 저장된다. 20 | 21 | - 자바스크립트는 2진수, 8진수, 16진수를 표현하기 위한 데이터 타입을 제공하지 않기 때문에 이들 값을 참조하면 모두 **10진수로 해석**된다. 22 | 23 | ```javascript 24 | var integer = 10; // 정수 25 | var double = 10.12; // 실수 26 | var negative = -20; // 음의 정수 27 | 28 | var binary = 0b01000001; // 2진수 29 | var octal = 0o101; // 8진수 30 | var hex = 0x41; // 16진수 31 | 32 | // 표기법만 다를 뿐 모두 같은 값이다. 33 | console.log(binary); // 65 34 | console.log(octal); // 65 35 | console.log(hex); // 65 36 | console.log(binary === octal); // true 37 | console.log(octal === hex); // true 38 | ``` 39 | 40 | - 정수로 표시되는 수끼리 나누더라도 실수가 나올 수 있다. 41 | 42 | ```javascript 43 | // 숫자 타입은 모두 실수로 처리된다. 44 | console.log(1 === 1.0); // true 45 | console.log(4 / 2); // 2 46 | console.log(3 / 2); // 1.5 47 | ``` 48 | 49 | - 숫자타입은 추가적으로 세 가지 특별한 값도 표현할 수 있다. 50 | 51 | ```javascript 52 | // 숫자 타입의 세 가지 특별한 값 53 | console.log(10 / 0); // Infinity : 양의 무한대 54 | console.log(10 / -0); // -Infinity : 음의 무한대 55 | console.log(1 * 'String'); // NaN : 산술 연산 불가 (not-a-number) 56 | ``` 57 | 58 | ## 6.2 문자열 타입 59 | 60 | - 자바스크립트의 문자열은 **원시타입**이다. 61 | - **변경 불가능한 값**(immutable value) : 문자열이 생성되면 그 문자열을 변경할 수 없다. 62 | - 문자열은 작은따옴표(''), 큰따옴표(""), 백틱(``)으로 텍스트를 감싼다. (일반적으로 작은따옴표 사용) 63 | - 이유1 : 따옴표로 감싸지 않으면 자바스크립트 엔진은 키워드나 식별자 같은 토큰으로 인식한다. 64 | - 이유2 : 스페이스와 같은 공백문자를 포함 시킬 수 있다. 65 | 66 | ## 6.3 템플릿 리터럴 67 | 68 | - ES6에 도입된 새로운 문자열 표기법 69 | - 백틱을 사용한다. `` 70 | - 기능 : 멀티라인 문자열, 표현식 삽입, 태그드 템플릿 등 71 | 72 | 73 | 74 | ### 6.3.1 멀티라인 문자열 75 | 76 | - 일반 문자열 내에서는 줄바꿈(개행)이 허용되지 않고, 이스케이프 시퀀스를 사용해야한다. 77 | 78 | ```javascript 79 | // 1. 일반 문자열 내에서 줄바꿈 허용X 80 | var str = 'Hello 81 | world.'; 82 | // SyntaxError: Invalid or unexpected token 83 | 84 | var template = `Template literal`; 85 | console.log(template); // Template literal 86 | 87 | 88 | // 2. 일반 문자열에서 이스케이프 시퀀스를 사용 89 | var template = ''; 90 | 91 | console.log(template); 92 | /* 93 | 96 | */ 97 | ``` 98 | 99 | - 템플릿 리터럴 내에서는 줄바꿈이 허용되며, 모든 공백도 있는 그대로 적용된다. 100 | 101 | ```javascript 102 | //3. 백틱으로 감싼 템플릿 리터럴 103 | var template = ``; 106 | 107 | console.log(template); 108 | /* 109 | 112 | */ 113 | ``` 114 | 115 | ### 6.3.2 표현식 삽입 116 | 117 | 1. ##### ES5에서 문자열 연결 118 | 119 | - 문자열 연산자 + : 피연산자 중 하나 이상이 문자열인 경우 문자열 연결 연산자로 작동한다. (그 외에는 덧셈 연산자로 동작) 120 | 121 | ```javascript 122 | var first = 'Ung-mo'; 123 | var last = 'Lee'; 124 | 125 | // ES5: 문자열 연결 126 | console.log('My name is ' + first + ' ' + last + '.'); // My name is Ung-mo Lee. 127 | ``` 128 | 129 | 2. ##### ES6의 표현식 삽입 130 | 131 | - 백틱 내에서 ${}로 표현식을 감싼다. 표현식의 평가 결과가 문자열이 아니더라도 문자열로 타입이 강제로 변환되어 삽입된다. 132 | 133 | ```javascript 134 | var first = 'Ung-mo'; 135 | var last = 'Lee'; 136 | 137 | // ES6: 표현식 삽입 138 | console.log(`My name is ${first} ${last}.`); // My name is Ung-mo Lee. 139 | ``` 140 | 141 | - 주의 : 표현식 삽입은 반드시 템플릿 리터럴(백틱) 내에서 사용해야한다. 142 | 143 | ```javascript 144 | //백틱 내에서 표현식 삽입 145 | console.log(`1 + 2 = ${1 + 2}`); // 1 + 2 = 3 146 | 147 | //작은 따옴표로 잘못 표기했을 경우 148 | console.log('1 + 2 = ${1 + 2}'); // 1 + 2 = ${1 + 2} 149 | //${ 1 + 2 } 자체가 문자열로 취급된다 150 | ``` 151 | 152 | ## 6.4 불리언 타입 153 | 154 | - 불리언 타입에는 논리적 참, 거짓을 나타내는 true, false가 있다. 155 | 156 | ## 6.5 undefined 타입 157 | 158 | - undefined 타입의 값은 undefined 가 유일하다. 159 | - var 키워드로 선언한 변수는 암묵적으로 undefined로 초기화된다. (4장 변수, 41p 참고) 160 | 161 | ## 6.6 null 타입 162 | 163 | - 변수에 값이 없다는 것을 의도적으로 명시할 때 사용한다. 164 | - 이전에 할당되어있던 값에 대한 참조를 명시적으로 제거한다. 이후 가비지 콜렉션이 수행된다. 165 | 166 | ## 6.7 심벌 타입 167 | 168 | - ES6에서 추가된 7번째 타입 169 | - 변경 불가능한 원시타입 170 | - Symbol 함수를 호출해 생성한다. 171 | - 외부에 노출되지 않는다. 172 | - 다른 값과 중복되지 않는 유일무이한 값이다. 173 | - 주로 이름이 충돌할 위험이 없는 객체의 유일한 프로퍼티 키를 만들기 위해 사용한다. 174 | 175 | ```javascript 176 | // 심벌 값 생성 177 | var key = Symbol('key'); 178 | console.log(typeof key); // symbol 179 | 180 | // 객체 생성 181 | var obj = {}; 182 | 183 | // 이름이 충돌할 위험이 없는 유일무이한 값인 심벌을 프로퍼티 키로 사용한다. 184 | obj[key] = 'value'; 185 | console.log(obj[key]); // value 186 | ``` 187 | 188 | ## 6.8 객체 타입 189 | 190 | - 자바스크립트는 객체 기반의 언어. 거의 모든 것이 객체이다. 191 | 192 | - 11장의 "원시값과 객체의 비교"에서 살펴보자. 193 | 194 | ## 6.9 데이터 타입의 필요성 195 | 196 | - 값을 저장할 때 확보해야하는 메모리 공간의 크기를 결정하기 위해 197 | - 값을 참조할 때 한 번에 읽어 들여야 할 메모리 공간의 크기를 결정하기 위해 198 | - 메모리에서 읽어 들인 2진수를 어떻게 해석할지 결정하기 위해 199 | 200 | ### 6.9.1 데이터 타입에 의한 메모리 공간의 확보와 참조 201 | 202 | - 값은 메모리에 저장하고 참조할 수 있어야한다. 203 | 204 | - 메모리에 값을 저장하려면 먼저 확보해야할 메모리 공간의 크기를 결정해야한다. 205 | 206 | - 1. 변수에 값 할당하기 207 | 208 | var score =100; 을 실행한다고 가정하자. 209 | 210 | 자바스크립트는 숫자타입을 배정밀도 64비트 부동소수점 형식을 사용하므로 8바이트의 메모리 공간을 확보한다. 211 | 212 | 그 공간에 100을 2진수로 저장한다. 213 | 214 | - 2. 값을 참조하기 215 | 216 | 식별자 score를 통해 숫자 타입의 값 100이 저장되어있는 메모리 공간의 주소를 찾아갈 수 있다. (정확히는 메모리 공간의 선두 메모리 셀의 주소) 217 | 218 | 이제 선두 메모리 주소로부터 한 번에 읽어 들여야할 메모리 셀의 크기를 알아야한다. 219 | 220 | score는 숫자타입으로 인식되므로 선두부터 8바이트 단위로 메모리 공간에 저장된 값을 읽어들인다. 221 | 222 | ### 6.9.2 데이터 타입에 의한 값의 해석 223 | 224 | - 메모리에서 읽어들인 값은 데이터 타입에 따라 다르게 해석될 수 있다. 225 | - 예를 들어 메모리에 0100 0001로 저장된 값은 숫자로 해석하면 65, 문자열로 해석하면 'A'다. 226 | - 앞선 예제에서 score 변수에 할당된 값은 숫자타입의 값이므로 score변수를 참조하면 **메모리 공간의 주소에서 읽어들인 2진수**를 **숫자**로 해석한다. 227 | 228 | ## 6.10 동적 타이핑 229 | 230 | ### 6.10.1 동적 타입 언어와 정적 타입 언어 231 | 232 | - #### 정적 타입 언어 233 | 234 | - **`선언`에 의해 타입이 결정됨** 235 | 236 | - 변수의 타입을 변경할 수 없다. 237 | 238 | - 변수에 선언한 타입에 맞는 값만 할당할 수 있다. 239 | 240 | - 컴파일 시점에 타입체크를 수행한다. (선언한 데이터 타입에 맞는 값을 할당했는지 검사하는 처리) 241 | 242 | - 통과하지 못할 경우 : 에러 발생. 프로그램의 실행 자체를 막는다. 243 | - 이점 : 타입의 일관성을 강제함. 더욱 안정적인 코드의 구현을 통해 런타임시 발생하는 에러를 줄인다. 244 | 245 | - 예시 : C, C++, Java, Kotlin, Go, Haskell, Rust, Scala 246 | 247 | - #### 동적 타입 언어-자바스크립트 248 | 249 | - **`할당`에 의해 타입이 결정됨** 250 | 251 | - var, let, const 키워드를 사용해 변수를 선언한다. (변수를 선언할 때 타입을 선언하지 않는다.) 252 | 253 | - 어떠한 데이터의 값이라도 자유롭게 할당할 수 있다.(미리 선언한 데이터 타입의 값만 할당할 수 있는 건 아니다.) 254 | 255 | ```javascript 256 | var foo; 257 | console.log(typeof foo); // undefined 258 | 259 | foo = 3; 260 | console.log(typeof foo); // number 261 | 262 | foo = 'Hello'; 263 | console.log(typeof foo); // string 264 | 265 | foo = true; 266 | console.log(typeof foo); // boolean 267 | 268 | foo = null; 269 | console.log(typeof foo); // object 270 | 271 | foo = Symbol(); // 심벌 272 | console.log(typeof foo); // symbol 273 | 274 | foo = {}; // 객체 275 | console.log(typeof foo); // object 276 | 277 | foo = []; // 배열 278 | console.log(typeof foo); // object 279 | 280 | foo = function () {}; // 함수 281 | console.log(typeof foo); // function 282 | ``` 283 | 284 | 285 | 286 | - `typeof` 연산자로 변수를 연산하면 **변수에 할당된 값의** **데이터 타입**을 반환한다. 287 | 288 | - 예시 : 자바스크립트, Python, PHP, Ruby, Listp, Perl 289 | 290 | ### 6.10.2 동적 타입 언어와 변수 291 | 292 | #### 단점 293 | 294 | - 복잡한 프로그램에서는 변화하는 변수 값을 추적하기 어려울 수 있다. 295 | 296 | - 값의 변경에 의해 타입도 언제든지 변경될 수 있다. 따라서 값을 확인하기 전에는 타입을 확신할 수 없다. 297 | - 개발자의 의도와 상관없이 엔진에 의해 암묵적으로 타입이 자동으로 변환되기도 한다. 298 | - 유연성은 높지만 신뢰성이 떨어진다. 299 | 300 | #### 주의사항 301 | 302 | - 변수는 꼭 필요한 경우에 한해 제한적으로 사용한다. 303 | - 변수보다는 상수를 사용해 값의 변경을 억제한다. -> 15.3절 const 304 | - 변수의 유효범위(스코프)는 최대한 좁게 만든다. -> 13장 스코프 305 | - 전역변수는 최대한 사용하지 않도록 한다. -> 14장 전역변수의 문제점 306 | - 변수 이름은 변수의 목적이나 의미를 파악할 수 있게 짓는다. 307 | 308 | -------------------------------------------------------------------------------- /07장-연산자/README.md: -------------------------------------------------------------------------------- 1 | # 7장 연산자 2 | 3 | - 연산자는 하나 이상의 표현식을 대상으로 산술, 할당, 비교, 논리, 타입, 지수 연산 등을 수행해 하나의 값을 만든다. 4 | - 이때 연산의 대상을 **피연산자**라 한다. 5 | - 피연산자는 값으로 평가될 수 있는 표현식이어야 한다. 6 | 7 | ```js 8 | // 산술 연산자 9 | 5 * 4 // -> 20 10 | // 문자열 연결 연산자 11 | 'My name is ' + 'Lee' // -> 'My name is Lee' 12 | // 할당 연산자 13 | color = 'red' // -> 'red' 14 | // 비교 연산자 15 | 3 > 5 // -> false 16 | // 논리 연산자 17 | true && false // -> false 18 | // 타입 연산자 19 | typeof 'Hi' // -> string 20 | ``` 21 | 22 | - 피연산자는 "값"이라는 명사의 역할을 한다면 연산자는 "피연산자를 연산하여 새로운 값을 만든다"라는 동사의 역할을 한다고 볼 수 있다. 23 | - 따라서 피연산자는 값으로 평가할 수 있어야 하며, 연산자는 값으로 평가된 피연산자를 연산해 새로운 값을 만든다. 24 | 25 | ## 7.1 산술 연산자 26 | 27 | - 산술 연산자는 피연산자를 대상으로 수학적 계산을 수행해 새로운 숫자 값을 만든다. 28 | - 산술 연산이 불가능한 경우, `NaN` 을 반환한다. 29 | 30 | ### 7.1.1 이항 산술 연산자 31 | 32 | - 이항 산술 연산자는 2개의 피연산자를 산술 연산하여 숫자 값을 만든다. 33 | 34 | | 이항 산술 연산자 | 의미 | 부수 효과 | 35 | | :--------------: | :----: | :-------: | 36 | | + | 덧셈 | X | 37 | | - | 뺄셈 | X | 38 | | * | 곱셈 | X | 39 | | / | 나눗셈 | X | 40 | | % | 나머지 | X | 41 | 42 | ### 7.1.2 단항 산술 연산자 43 | 44 | - 단항 산술 연산자는 1개의 피연산자를 산술 연산하여 숫자 값을 만든다. 45 | 46 | | 단항 산술 연산자 | 의미 | 부수 효과 | 47 | | :--------------: | :-------------------------------------------------: | :-------: | 48 | | ++ | 증가 | O | 49 | | -- | 감소 | O | 50 | | + | 어떠한효과도 없다. 음수를 양수로 반전하지도 않는다. | X | 51 | | - | 양수를 음수로, 음수를 양수로 반전한 값을 반환한다. | X | 52 | 53 | - 증가/감소(++/--) 연산자는 피연산자의 값을 변경하는 부수효과가 있다. 54 | - 증가/감소 연산을 하면 피연산자의 값을 변경하는 암묵적 할당이 이뤄진다. 55 | 56 | ```js 57 | var x = 1; 58 | 59 | // ++ 연산자는 피연산자의 값을 변경하는 암묵적 할당이 이뤄진다. 60 | x++; // x = x + 1; 61 | console.log(x); // 2 62 | 63 | // -- 연산자는 피연산자의 값을 변경하는 암묵적 할당이 이뤄진다. 64 | x--; // x = x - 1; 65 | console.log(x); // 1 66 | ``` 67 | 68 | ### 7.1.3 문자열 연결 연산자 69 | 70 | - `+` 연산자는 피연산자 중 하나 이상이 문자열인 경우 문자열 연결 연산자로 동작한다. 71 | 72 | ```js 73 | // 문자열 연결 연산자 74 | '1' + 2; // -> '12' 75 | 1 + '2'; // -> '12' 76 | 77 | // 산술 연산자 78 | 1 + 2; // -> 3 79 | 80 | // true는 1로 타입 변환된다. 81 | 1 + true; // -> 2 82 | 83 | // false는 0으로 타입 변환된다. 84 | 1 + false; // -> 1 85 | 86 | // null은 0으로 타입 변환된다. 87 | 1 + null; // -> 1 88 | 89 | // undefined는 숫자로 타입 변환되지 않는다. 90 | +undefined; // -> NaN 91 | 1 + undefined; // -> NaN 92 | ``` 93 | 94 | ## 7.2 할당 연산자 95 | 96 | - 할당 연산자는 우항에 있는 피연산자의 평가 결과를 좌항에 있는 변수에 할당한다. 97 | 98 | - 할당 연산자는 좌항의 변수에 값을 할당하므로 변수 값이 변하는 부수효과가 있다. 99 | 100 | | 할당 연산자 | 예 | 동일 표현 | 부수 효과 | 101 | | :---------: | :----: | :-------: | :-------: | 102 | | = | x = 5 | x = 5 | O | 103 | | += | x += 5 | x = x + 5 | O | 104 | | -= | x -= 5 | x = x - 5 | O | 105 | | *= | x *= 5 | x = x * 5 | O | 106 | | /= | x /= 5 | x = x / 5 | O | 107 | | %= | x %= 5 | x = x % 5 | O | 108 | 109 | - **할당문은 값으로 평가되는 표현식인 문으로서 할당된 값으로 평가된다.** 110 | - 따라서 다음과 같이 할당문을 다른 변수에 할당할 수도 있다. 이러한 특징을 활용해 여러 변수에 동일한 값을 연쇄 할당할 수 있다. 111 | 112 | ```js 113 | var a, b, c; 114 | 115 | // 연쇄 할당. 오른쪽에서 왼쪽으로 진행. 116 | // ① c = 0 : 0으로 평가된다 117 | // ② b = 0 : 0으로 평가된다 118 | // ③ a = 0 : 0으로 평가된다 119 | a = b = c = 0; 120 | 121 | console.log(a, b, c); // 0 0 0 122 | ``` 123 | 124 | ## 7.3 비교 연산자 125 | 126 | - 비교 연산자는 좌항과 우항의 피연산자를 비교한 다음 그 결과를 불리언 값으로 반환한다. 127 | 128 | ### 7.3.1 동등/일치 비교 연산자 129 | 130 | - 동등 비교 연산자(==/!=)는 느슨한 비교를 하지만 일치 비교 연산자(===/!==)는 엄격한 비교를 한다. 131 | 132 | | 비교 연산자 | 의미 | 사례 | 설명 | 부수 효과 | 133 | | :---------: | :---------: | :-----: | :----------------------: | :-------: | 134 | | == | 동등 비교 | x == y | x와 y의 값이 같음 | X | 135 | | === | 일치 비교 | x === y | x와 y의 값과 타입이 같음 | X | 136 | | != | 부동등 비교 | x != y | x와 y의 값이 다름 | X | 137 | | !== | 불일치 비교 | x !== y | x와 y의 값과 타입이 다름 | X | 138 | 139 | - **동등 비교 연산자는 좌항과 우항의 피연산자를 비교할 때 먼저 암묵적 타입 변환을 통해 타입을 일치시킨 후 같은 값인지 비교한다.** 140 | - 동등 비교 연산자는 예측하기 어려운 결과를 만들어낸다. 따라서 일치 비교 연산자를 사용하는 것이 좋다. 141 | 142 | ### 7.3.2 대소 관계 비교 연산자 143 | 144 | | 대소 관계 비교 연산자 | 예제 | 설명 | 부수 효과 | 145 | | :-------------------: | :----: | :-------------------: | :-------: | 146 | | > | x > y | x가 y보다 크다 | X | 147 | | < | x < y | x가 y보다 작다 | X | 148 | | >= | x >= y | x가 y보다 크거나 같다 | X | 149 | | <= | x <= y | x가 y보다 작거나 같다 | X | 150 | 151 | ## 7.4 삼항 조건 연산자 152 | 153 | - 삼항 조건 연산자는 조건식 평가 결과에 따라 반환할 값을 결정한다. 자바스크립트의 유일한 삼항 연산자이며, 부수 효과는 없다. 154 | 155 | ```js 156 | var x = 2; 157 | 158 | // 2 % 2는 0이고 0은 false로 암묵적 타입 변환된다. 159 | var result = x % 2 ? '홀수' : '짝수'; 160 | 161 | console.log(result); // 짝수 162 | ``` 163 | 164 | ```js 165 | var x = 2, result; 166 | 167 | // 2 % 2는 0이고 0은 false로 암묵적 타입 변환된다. 168 | if (x % 2) result = '홀수'; 169 | else result = '짝수'; 170 | 171 | console.log(result); // 짝수 172 | ``` 173 | 174 | ```js 175 | var x = 10; 176 | 177 | // if...else 문은 표현식이 아닌 문이다. 따라서 값처럼 사용할 수 없다. 178 | var result = if (x % 2) { result = '홀수'; } else { result = '짝수'; }; 179 | // SyntaxError: Unexpected token if 180 | ``` 181 | 182 | ```js 183 | var x = 10; 184 | 185 | // 삼항 조건 연산자 표현식은 표현식인 문이다. 따라서 값처럼 사용할 수 있다. 186 | var result = x % 2 ? '홀수' : '짝수'; 187 | console.log(result); // 짝수 188 | ``` 189 | 190 | ## 7.5 논리 연산자 191 | 192 | - 논리 연산자는 우항과 좌항의 피연산자를 논리 연산한다. 193 | 194 | | 논리 연산자 | 의미 | 부수 효과 | 195 | | :---------: | :---------: | :-------: | 196 | | \|\| | 논리합(OR) | X | 197 | | && | 논리곱(AND) | X | 198 | | ! | 부정(NOT) | X | 199 | 200 | ```js 201 | // 논리합(||) 연산자 202 | true || true; // -> true 203 | true || false; // -> true 204 | false || true; // -> true 205 | false || false; // -> false 206 | 207 | // 논리곱(&&) 연산자 208 | true && true; // -> true 209 | true && false; // -> false 210 | false && true; // -> false 211 | false && false; // -> false 212 | 213 | // 논리 부정(!) 연산자 214 | !true; // -> false 215 | !false; // -> true 216 | 217 | // 암묵적 타입 변환 218 | !0; // -> true 219 | !'Hello'; // -> false 220 | 221 | // 단축 평가 222 | 'Cat' && 'Dog'; // -> 'Dog' 223 | ``` 224 | 225 | ## 7.6 쉼표 연산자 226 | 227 | - 쉼표(`,`) 연산자는 왼쪽 피연산자부터 차례대로 피연산자를 평가하고 마지막 피연산자의 평가가 끝나면 마지막 피연산자의 평가 결과를 반환한다. 228 | 229 | ```js 230 | var x, y, z; 231 | 232 | x = 1, y = 2, z = 3; // 3 233 | ``` 234 | 235 | ## 7.7 그룹 연산자 236 | 237 | - 소괄호(`()`)로 피연산자를 감싸는 그룹 연산자는 자신의 피연산자인 표현식을 가장 먼저 평가한다. 238 | - 따라서 그룹 연산자를 사용하면 연산자의 우선 순위를 조절할 수 있다. 그룹 연산자는 연산자 우선순위가 가장 높다. 239 | 240 | ```js 241 | 10 * 2 + 3; // -> 23 242 | 243 | // 그룹 연산자를 사용하여 우선순위를 조절 244 | 10 * (2 + 3); // -> 50 245 | ``` 246 | 247 | ## 7.8 typeof 연산자 248 | 249 | - typeof 연산자는 피연산자의 데이터 타입을 문자열로 반환한다. 250 | 251 | ```js 252 | typeof '' // -> "string" 253 | typeof 1 // -> "number" 254 | typeof NaN // -> "number" 255 | typeof true // -> "boolean" 256 | typeof undefined // -> "undefined" 257 | typeof Symbol() // -> "symbol" 258 | typeof null // -> "object" 259 | typeof [] // -> "object" 260 | typeof {} // -> "object" 261 | typeof new Date() // -> "object" 262 | typeof /test/gi // -> "object" 263 | typeof function () {} // -> "function" 264 | ``` 265 | 266 | ## 7.9 지수 연산자 267 | 268 | - ES7에서 도입된 지수 연산자는 좌항의 피연산자를 밑(base)으로, 우항의 피연산자를 지수(exponent)로 거듭 제곱하여 숫자 값을 반환한다. 269 | 270 | ```js 271 | 2 ** 2; // -> 4 272 | 2 ** 2.5; // -> 5.65685424949238 273 | 2 ** 0; // -> 1 274 | 2 ** -2; // -> 0.25 275 | ``` 276 | 277 | - 지수 연산자가 도입되기 이전에는 `Math.pow()` 메서드를 사용했다. 278 | 279 | ```js 280 | Math.pow(2, 2); // -> 4 281 | Math.pow(2, 2.5); // -> 5.65685424949238 282 | Math.pow(2, 0); // -> 1 283 | Math.pow(2, -2); // -> 0.25 284 | ``` 285 | 286 | ## 7.10 그 외의 연산자 287 | 288 | | 연산자 | 개요 | 참고 | 289 | | :--------: | :---------------------------------------------------------: | :------------------------: | 290 | | ?. | 옵셔널 체이닝 연산자 | 9.4.2 옵셔널 체이닝 연산자 | 291 | | ?? | null 병합 연산자 | 9.4.3 null 병합 연산자 | 292 | | deletenew | 프로퍼티 삭제 | 10.8 프로퍼티 삭제 | 293 | | new | 생성자 함수를 호출할 때 사용하여 인스턴스를 생성 | 17.2.6 new 연산자 | 294 | | instanceof | 좌변의 객체가 우변의 생성자 함수와 연결된 인스턴스인지 판별 | 19.10 instanceof 연산자 | 295 | | in | 프로퍼티 존재 확인 | 19.13.1 in 연산자 | 296 | 297 | ## 7.11 연산자의 부수 효과 298 | 299 | - 대부분의 연산자는 다른 코드에 영향을 주지 않는다. 하지만 일부 연산자는 다른 코드에 영향을 주는 부수 효과가 있다. 300 | - 부수 효과가 있는 연산자는 다음과 같다. 301 | - 할당 연산자 `=` 302 | - 증가/감소 연산자 `++`, `--` 303 | - delete 연산자 304 | 305 | ```js 306 | var x; 307 | 308 | // 할당 연산자는 변수 값이 변하는 부수 효과가 있다. 309 | // 이는 x 변수를 사용하는 다른 코드에 영향을 준다. 310 | x = 1; 311 | console.log(x); // 1 312 | 313 | // 증가/감소 연산자(++/--)는 피연산자의 값을 변경하는 부수 효과가 있다. 314 | // 피연산자 x의 값이 재할당되어 변경된다. 이는 x 변수를 사용하는 다른 코드에 영향을 준다. 315 | x++; 316 | console.log(x); // 2 317 | 318 | var o = { a: 1 }; 319 | 320 | // delete 연산자는 객체의 프로퍼티를 삭제하는 부수 효과가 있다. 321 | // 이는 o 객체를 사용하는 다른 코드에 영향을 준다. 322 | delete o.a; 323 | console.log(o); // {} 324 | ``` 325 | 326 | ## 7.12 연산자 우선순위 327 | 328 | | 우선순위 | 연산자 | 329 | | :------: | ------------------------------------------------------------ | 330 | | 1 | `()` | 331 | | 2 | new(매개변수 존재), `.` , `[]`(프로퍼티 접근), `()` (함수 호출), `?.` (옵셔널 체이닝 연산자) | 332 | | 3 | new(매개변수 미존재) | 333 | | 4 | `x++`, `x--` | 334 | | 5 | `!x`, `+x,` -`x`, `++x`, `--x`, typeof, delete | 335 | | 6 | `**` (이항 연산자 중에서 우선순위가 가장 높음) | 336 | | 7 | `*`, `/,` `%` | 337 | | 8 | `+`, `-` | 338 | | 9 | <. <=, >, >=, in, instanceof | 339 | | 10 | `==,` `!=`, `===`, `!==` | 340 | | 11 | `??` (null 병합 연산자) | 341 | | 12 | `&&` | 342 | | 13 | `||` | 343 | | 14 | 삼항 연산자 | 344 | | 15 | 할당 연산자(=, +=, -= ...) | 345 | | 16 | `,` | 346 | 347 | ## 7.13 연산자 결합 순서 348 | 349 | | 결합 순서 | 연산자 | 350 | | ------------ | ------------------------------------------------------------ | 351 | | 좌항 -> 우항 | `+`, `-`. `/`, `%`, `<`, `<=`, `&&`, `||`, `.`, `[]`, `()` , `??`, `?.`, in, instanceof | 352 | | 우항 -> 좌항 | `++` ,` --`, 할당 연산자, `!x`, `+x`, `-x` ,` +=x` ,` --x`, typeof, delete, 삼항 연산자 | 353 | 354 | -------------------------------------------------------------------------------- /08장-제어문/README.md: -------------------------------------------------------------------------------- 1 | # 8장 제어문 2 | 3 | ## 8.1 블록문 4 | 5 |
6 | 7 | - 블록문은 0개 이상의 문을 중괄호({})로 묶은 것이다. 8 | - 블록, 코드 블록이라 부르기도한다. 9 | - 단독으로 사용할 수도 있으나 일반적으로 제어문이나 함수 정의를 위해 사용합니다. 10 | - 블록문은 항상 문의 종료를 의미하기 때문에 일반적으로 세미콜론을 붙이지 않는다. 11 | 12 | <내 생각> 13 | - 변수에 익명함수를 할당할 때와 같은 경우에도 블록문 만으로 문의 끝을 단정 지을 수 있는지는 의문이다. 14 | 15 |
16 | 17 | ## 8.2 조건문 18 | 19 | - 조건문은 일반적으로 다음과 같이 생겼다. 20 | ```js 21 | 조건문_예약어 (조건식) { 22 | ... 23 | } 24 | ``` 25 | - 조건식은 말그대로 조건을 나타내는 표현식이다. 26 | - 조건식을 평가한 값에 따라 블록문이 실행될지 여부가 결정된다. 27 | 28 |
29 | 30 | ### **1. if, else 문** 31 | 32 | - if, else문에서는 조건식을 boolean값으로 평가한다. 33 | - 조건식의 평가값이 boolean이 아니면 암묵적 형변환이 일어난다. 34 | - 조건식의 평가값이 true이면 if문의 블록이 실행되고 false이면 else문의 블록이 실행된다. 35 | ```js 36 | if (조건식) { 37 | // 조건식이 true면 여기 38 | } else { 39 | // 조건식이 false면 여기 40 | } 41 | ``` 42 | - else문 없이 if문만을 단독으로 사용할 수도 있다. 43 | - 이때는 조건식의 결과가 블록을 실행할지 말지만 결정하는 것처럼 보인다. 44 | - 만약 블록문 안에 문이 하나라면 블록을 생략할 수 있다. 45 | ```js 46 | const num = 1; 47 | 48 | if (num === 1) return; 49 | ``` 50 | - else if문을 사용해 여러개의 조건에 대해서도 분기할 수 있다. 51 | ```js 52 | if (조건식1) { 53 | ... 54 | } else if (조건식2) { 55 | ... 56 | } else { 57 | 58 | } 59 | ``` 60 | - if, else문은 삼항 연산자로 대체할 수 있다. 표현식이 아닌 문을 값처럼 사용할 수 없다는 점을 고려하여 상황에 따라 선택하면 되겠다. 61 | 62 |
63 | 64 | ### **2.switch 문** 65 | 66 |
67 | 68 | - switch문은 다음과 같이 생겼다. 69 | ```js 70 | switch (조건식) { 71 | case 표현식1: 72 | ... 73 | case 표현식2: 74 | ... 75 | default: 76 | ... 77 | } 78 | ``` 79 | - switch문은 항상 case문과 함께 사용된다. 80 | - case문의 표현식의 평가값들 중 조건식의 평가값과 일치하는 것이 있다면 해당하는 case문으로 실행 흐름을 옮긴다. 81 | - 조건식의 평가값과 일치하는 값이 없다면 default문으로 실행 흐름이 옮겨진다. 82 | - default문은 생략할 수 있다. 83 | - 이때, 주의할 점이 있다. switch문이 하는 일은 단순히 실행 흐름을 옮기는 것 뿐이기 때문에 실행되는 case문 이후 나타나는 모든 코드가 순차적으로 실행된다. 이를 fall through라고 한다. 84 | ```js 85 | switch (1) { 86 | case 0: 87 | 0번 코드 88 | case 1: 89 | 1번 코드 90 | case 2: 91 | 2번 코드 92 | } 93 | 94 | // 1번 코드, 2번 코드 모두 실행된다. 95 | ``` 96 | - 따라서 특정 case문만 실행하고 싶다면 반드시 case문 안에 break문을 적어주어야 한다. 97 | ```js 98 | switch (1) { 99 | case 0: 100 | 0번 코드 101 | case 1: 102 | 1번 코드 103 | break; 104 | case 2: 105 | 2번 코드 106 | } 107 | 108 | // 1번 코드만 실행된다. 109 | ``` 110 | 111 |
112 | 113 | ## 8.3 반복문 114 | 115 |
116 | 117 | - 반복문에서는 조건에 따라 블록 실행을 반복할지가 결정된다. 118 | 119 |
120 | 121 | ### 1. for문 122 | - for문은 다음과 같이 구성된다. 123 | ```js 124 | for (변수 선언문 or 할당문; 조건식; 증감문;) { 125 | ... 126 | } 127 | ``` 128 | - for문에서는 세미콜론으로 구분되는 세가지의 문이 사용된다. 129 | - 이 세가지 문은 각각 생략될 수 있으나 조건식, 증감문을 생략할 경우 무한 루프가 발생할 수 있으니 조심해야한다. 130 | - for문의 실행 순서는 다음과 같다. 131 | 1. 변수 선언, 할당문 실행 132 | 2. 조건식 평가 133 | 3. 조건식 평가값에 따라 분기 134 | - 조건식의 평가값이 true라면 135 | - 블록문 실행 136 | - 증감문을 실행 137 | - for문의 실행 순서를 처음부터 "반복" 138 | - 조건식의 평가값이 false라면 139 | - for문의 실행을 멈춘다. 140 | - 변수 선언, 할당 문에서 let으로 선언한 변수는 조건식이 참일 때 실행되는 블록을 스코프로 가진다. 141 | - 당연한 얘기지만 for문은 중첩해 사용할 수 있다. 142 | 143 |
144 | 145 | ### 2. while문 146 | 147 |
148 | 149 | - while 문은 for문에 비해 그 형태와 동작이 단순하다. 150 | ```js 151 | while (조건식) { 152 | ... 153 | } 154 | ``` 155 | - while문의 실행 순서는 다음과 같다. 156 | 1. 조건식 평가 157 | 2. 조건식 평가값에 따라 분기 158 | - 조건식의 평가값이 true라면 159 | - 블록문을 실행 160 | - while문의 실행 순서를 처음부터 "반복" 161 | - 조건식의 평가값이 false라면 162 | - while문의 실행을 멈춘다. 163 | 164 |
165 | 166 | ### 3. do, while문 167 | 168 |
169 | 170 | - while문의 경우 조건식을 먼저 평가하고 그 결과에 따라 블록문의 실행 여부를 결정한다. 171 | - do, while문은 블록문을 먼지 실행하고 조건식을 평가한다. 172 | - 따라서 조건식이 항상 false로 평가되더라도 적어도 한 번은 블록문이 실행된다. 173 | ```js 174 | do { 175 | console.log("hey"); // "hey" 한 번 출력됨 176 | } while (false); 177 | ``` 178 | 179 |
180 | 181 | ### 4. break문 182 | 183 |
184 | 185 | - 앞서 switch문에서 먼저 살펴봤던 break문과 같다. 186 | - 특정 상황에서 break문을 실행하면 해당 break문이 속한 블록문의 실행을 멈춘다. 187 | - 따라서 break이 등장하기 이전 코드만 실행되고 그 이후부터 블록문이 끝날 때 까지의 코드는 실행되지 않는다. 188 | - 이때, '특정 상황'이란 라벨문, 반복문, switch문을 의미한다. 189 | - 반복문의 경우 반복 자체도 멈춘다. 190 | - 특정 상황 외 블록 안에서 break을 사용하면 Syntax error가 발생한다. 191 | ```js 192 | while(1) { 193 | // 여기 코드는 실행됨 194 | break; 195 | // 여기 코드는 실행되지 않음 196 | } 197 | ``` 198 | 199 |
200 | 201 | ### 5. continue문 202 | 203 | - continue문은 반복문 실행시 코드 블록을 중단 시킨다. 204 | - 이때, 반복문을 완전히 멈추는 것은 아니다. 그 당시 실행중인 블록문만을 중단시키며 반복문은 기존 플로우대로 실행된다. 205 | ```js 206 | for (let i = 0; i < 3; i++) { 207 | if (i === 1) { 208 | continue; 209 | } 210 | 211 | console.log(i); // 0, 2 212 | } 213 | ``` -------------------------------------------------------------------------------- /09장-타입-변환과-단축-평가/README.md: -------------------------------------------------------------------------------- 1 | # 9장 타입 변환과 단축 평가 2 | 3 | ## 9.1 타입 변환이란? 4 | 5 | - 자바스크립트의 모든 값은 타입이 있다. 6 | - 값의 타입은 개발자의 의도에 따라 다른 타입으로 변환할 수 있다. 7 | - 개발자가 의도적으로 값의 타입을 변환하는 것을 **명시적 타입 변환** 또는 **타입 캐스팅**이라 한다. 8 | 9 | ```js 10 | var x =10; 11 | 12 | var str = x.toString(); // 명시적 타입 변환 13 | console.log(typeof str, str) // => string 10 14 | ``` 15 | 16 | - 개발자의 의도와는 상관없이 표현식을 평가하는 도중에 자바스크립트 엔진에 의해 암묵적으로 타입이 자동 변환되기도 한다. 17 | - 이를 **암묵적 타입 변환** 또는 **타입 강제 변환**이라 한다. 18 | 19 | ```js 20 | var x = 10; 21 | 22 | var str = x + ''; // 암묵적 타입 변환 23 | console.log(typeof str, str) // => string 10 24 | ``` 25 | 26 | - 명시적 타입 변환이나 암묵적 타입 변환이 기존 원시 값을 직접 변경하는 것은 아니다. 27 | - 원시 값은 변경 불가능한 값이므로 변경할 수 없다. 28 | - 타입 변환이란 기존 원시 값을 사용해 다른 타입의 **새로운 원시값을 생성**하는 것이다. 29 | - 개발자는 자신의 코드에서 암묵적 타입 변환이 발생하는지, 발생한다면 어떤 타입의 어떤 값으로 변환 되는지, 그리고 타입 변환된 값으로 표현식이 어떻게 평가될 것인지 예측 가능해야 한다. 30 | 31 | ## 9.2 암묵적 타입 변환 32 | 33 | - 자바스크립트 엔진은 표현식을 평가할 때 개발자의 의도와는 상관없이 코드의 문맥을 고려해 암묵적으로 데이터 타입을 강제 변환할 때가 있다. 34 | 35 | ```js 36 | // 피연산자가 모두 문자열 타입이어야 하는 문맥 37 | '10' + 2 // => '102' 38 | 39 | // 피연산자가 모두 숫자 타입이어야 하는 문맥 40 | 5 * '10' // => 50 41 | 42 | // 피연산자 또는 표현식이 불리언 타입이어야 하는 문맥 43 | !0 // => true 44 | if(1) {} 45 | ``` 46 | 47 | - 암묵적 타입변환이 발생하면 문자열, 숫자, 불리언과 같은 원시 타입 중 하나로 타입을 자동 변환한다. 48 | 49 | ### 9.2.1 문자열 타입으로 변환 50 | 51 | ```js 52 | 1 + "2" // => "12" 53 | ``` 54 | 55 | - 위 예제의 `+` 연산자는 피연산자 중 하나 이상이 문자열이므로 **문자열 연결 연산자**로 동작한다. 56 | - 자바스크립트 엔진은 문자열 연결 연산자 표현식을 평가하기 위해 문자열 연결 연산자의 피연산자 중에서 문자열 타입이 아닌 피연산자를 문자열 타입으로 암묵적 타입 변환하다. 57 | 58 | ```js 59 | // 숫자 타입 60 | 0 + '' // -> "0" 61 | -0 + '' // -> "0" 62 | 1 + '' // -> "1" 63 | -1 + '' // -> "-1" 64 | NaN + '' // -> "NaN" 65 | Infinity + '' // -> "Infinity" 66 | -Infinity + '' // -> "-Infinity" 67 | 68 | // 불리언 타입 69 | true + '' // -> "true" 70 | false + '' // -> "false" 71 | 72 | // null 타입 73 | null + '' // -> "null" 74 | 75 | // undefined 타입 76 | undefined + '' // -> "undefined" 77 | 78 | // 심벌 타입 79 | (Symbol()) + '' // -> TypeError: Cannot convert a Symbol value to a string 80 | 81 | // 객체 타입 82 | ({}) + '' // -> "[object Object]" 83 | Math + '' // -> "[object Math]" 84 | [] + '' // -> "" 85 | [10, 20] + '' // -> "10,20" 86 | (function(){}) + '' // -> "function(){}" 87 | Array + '' // -> "function Array() { [native code] }" 88 | ``` 89 | 90 | ### 9.2.2 숫자 타입으로 변환 91 | 92 | ```js 93 | 1 - '1' // -> 0 94 | 1 * '10' // -> 10 95 | 1 / 'one' // -> NaN 96 | ``` 97 | 98 | - 위 예제에서 사용한 연산자는 모두 **산술 연산자**다. 99 | - 산술 연산자의 역할은 숫자 값을 만드는 것이다. 따라서 산술 연산자의 모든 피연산자는 코드 문맥상 숫자 타입이어야 한다. 100 | - 자바스크립트 엔진은 산술 연산자 표현식을 평가하기 위해 산술 연산자의 피연산자 중에서 숫자 타입이 아닌 피연산자를 숫자 타입으로 암묵적 타입 변환한다. 101 | - 이 때 피연산자를 숫자 타입으로 변환할 수 없는 경우는 산술 연산을 수행할 수 없으므로 표현식의 평가 결과는 `NaN` 이 된다. 102 | 103 | ```js 104 | '1' > 0 // -> true 105 | ``` 106 | 107 | - 비교 연산자의 역할은 불리언 값을 만드는 것이다. `>` 비교 연산자는 피연산자의 크기를 비교하므로 모든 피연산자는 코드 문맥상 모두 숫자 타입이어야 한다. 108 | - 자바스크립트 엔진은 비교 연산자 표현식을 평가하기 위해 비교 연산자의 피연산자 중에서 숫자 타입이 아닌 피연산자를 숫자 타입으로 암묵적 타입 변환한다. 109 | 110 | ```js 111 | // 문자열 타입 112 | +'' // -> 0 113 | +'0' // -> 0 114 | +'1' // -> 1 115 | +'string' // -> NaN 116 | 117 | // 불리언 타입 118 | +true // -> 1 119 | +false // -> 0 120 | 121 | // null 타입 122 | +null // -> 0 123 | 124 | // undefined 타입 125 | +undefined // -> NaN 126 | 127 | // 심벌 타입 128 | +Symbol() // -> ypeError: Cannot convert a Symbol value to a number 129 | 130 | // 객체 타입 131 | +{} // -> NaN 132 | +[] // -> 0 133 | +[10, 20] // -> NaN 134 | +(function(){}) // -> NaN 135 | ``` 136 | 137 | ### 9.2.3 불리언 타입으로 변환 138 | 139 | ```js 140 | if ('') console.log(x) 141 | ``` 142 | 143 | - If 문이나 for 문과 같은 제어문 또는 삼항 조건 연산자의 조건식은 불리언 값으로 평가되어야 하는 표현식이다. 144 | - 자바스크립트 엔진은 조건식의 평가 결과를 불리언 타입으로 암묵적 타입 변환한다. 145 | 146 | ```js 147 | if ('') console.log('1'); 148 | if (true) console.log('2'); 149 | if (0) console.log('3'); 150 | if ('str') console.log('4'); 151 | if (null) console.log('5'); 152 | 153 | // 2 4 154 | ``` 155 | 156 | - 자바스크립트 엔진은 불리언 타입이 아닌 값을 **Truthy 값(참으로 평가되는 값)** 또는 **Falsy 값(거짓으로 평가되는 값)**으로 구분한다. 157 | - Falsy로 평가되는 값은 다음과 같다. 158 | - `false` 159 | - `undefined` 160 | - `null` 161 | - `0`, `-0` 162 | - `NaN` 163 | - `''` (빈 문자열) 164 | - Falsy 값 외의 모든 값은 true로 평가되는 Truthy 값이다. 165 | 166 | ## 9.3 명시적 타입 변환 167 | 168 | - 개발자의 의도에 따라 명시적으로 타입을 변경하는 방법은 다음과 같다. 169 | 1. 표준 빌트인 생성자 함수를 호출하는 방법(String, Number, Boolean) 170 | 2. 빌트인 메서드를 사용하는 방법 171 | 3. 암묵적 타입 변환을 이용하는 방법 172 | 173 | ### 9.3.1 문자열 타입으로 변환 174 | 175 | - 문자열 타입이 아닌 값을 문자열 타입으로 변환하는 방법은 다음과 같다. 176 | 1. String 생성자 함수를 new 연산자 없이 호출하는 방법 177 | 2. Object.prototype.toString 메서드를 사용하는 방법 178 | 3. 문자열 연결 연산자를 이용하는 방법 179 | 180 | ```js 181 | // 1. String 생성자 함수를 new 연산자 없이 호출하는 방법 182 | // 숫자 타입 => 문자열 타입 183 | String(1); // -> "1" 184 | String(NaN); // -> "NaN" 185 | String(Infinity); // -> "Infinity" 186 | // 불리언 타입 => 문자열 타입 187 | String(true); // -> "true" 188 | String(false); // -> "false" 189 | 190 | // 2. Object.prototype.toString 메서드를 사용하는 방법 191 | // 숫자 타입 => 문자열 타입 192 | (1).toString(); // -> "1" 193 | (NaN).toString(); // -> "NaN" 194 | (Infinity).toString(); // -> "Infinity" 195 | // 불리언 타입 => 문자열 타입 196 | (true).toString(); // -> "true" 197 | (false).toString(); // -> "false" 198 | 199 | // 3. 문자열 연결 연산자를 이용하는 방법 200 | // 숫자 타입 => 문자열 타입 201 | 1 + ''; // -> "1" 202 | NaN + ''; // -> "NaN" 203 | Infinity + ''; // -> "Infinity" 204 | // 불리언 타입 => 문자열 타입 205 | true + ''; // -> "true" 206 | false + ''; // -> "false" 207 | ``` 208 | 209 | ### 9.3.2 숫자 타입으로 변환 210 | 211 | - 숫자 타입이 아닌 값을 숫자 타입으로 변환하는 방법은 다음과 같다. 212 | 1. Number 생성자 함수를 new 연산자 없이 호출하는 방법 213 | 2. parseInt, parseFloat 함수를 사용하는 방법(문자열만 변환 가능) 214 | 3. `+` 단항 산술 연산자를 이용하는 방법 215 | 4. `*` 산술 연산자를 이용하는 방법 216 | 217 | ```js 218 | // 1. Number 생성자 함수를 new 연산자 없이 호출하는 방법 219 | // 문자열 타입 => 숫자 타입 220 | Number('0'); // -> 0 221 | Number('-1'); // -> -1 222 | Number('10.53'); // -> 10.53 223 | // 불리언 타입 => 숫자 타입 224 | Number(true); // -> 1 225 | Number(false); // -> 0 226 | 227 | // 2. parseInt, parseFloat 함수를 사용하는 방법(문자열만 변환 가능) 228 | // 문자열 타입 => 숫자 타입 229 | parseInt('0'); // -> 0 230 | parseInt('-1'); // -> -1 231 | parseFloat('10.53'); // -> 10.53 232 | 233 | // 3. + 단항 산술 연산자를 이용하는 방법 234 | // 문자열 타입 => 숫자 타입 235 | +'0'; // -> 0 236 | +'-1'; // -> -1 237 | +'10.53'; // -> 10.53 238 | // 불리언 타입 => 숫자 타입 239 | +true; // -> 1 240 | +false; // -> 0 241 | 242 | // 4. * 산술 연산자를 이용하는 방법 243 | // 문자열 타입 => 숫자 타입 244 | '0' * 1; // -> 0 245 | '-1' * 1; // -> -1 246 | '10.53' * 1; // -> 10.53 247 | // 불리언 타입 => 숫자 타입 248 | true * 1; // -> 1 249 | false * 1; // -> 0 250 | ``` 251 | 252 | ### 9.3.3 불리언 타입으로 변환 253 | 254 | - 불리언 타입이 아닌 값을 불리언 타입으로 변환하는 방법은 다음과 같다. 255 | 1. Boolean 생성자 함수를 new 연산자 없이 호출하는 방법 256 | 2. ! 부정 논리 연산자를 두 번 사용하는 방법 257 | 258 | ```js 259 | // 1. Boolean 생성자 함수를 new 연산자 없이 호출하는 방법 260 | // 문자열 타입 => 불리언 타입 261 | Boolean('x'); // -> true 262 | Boolean(''); // -> false 263 | Boolean('false'); // -> true 264 | // 숫자 타입 => 불리언 타입 265 | Boolean(0); // -> false 266 | Boolean(1); // -> true 267 | Boolean(NaN); // -> false 268 | Boolean(Infinity); // -> true 269 | // null 타입 => 불리언 타입 270 | Boolean(null); // -> false 271 | // undefined 타입 => 불리언 타입 272 | Boolean(undefined); // -> false 273 | // 객체 타입 => 불리언 타입 274 | Boolean({}); // -> true 275 | Boolean([]); // -> true 276 | 277 | // 2. ! 부정 논리 연산자를 두번 사용하는 방법 278 | // 문자열 타입 => 불리언 타입 279 | !!'x'; // -> true 280 | !!''; // -> false 281 | !!'false'; // -> true 282 | // 숫자 타입 => 불리언 타입 283 | !!0; // -> false 284 | !!1; // -> true 285 | !!NaN; // -> false 286 | !!Infinity; // -> true 287 | // null 타입 => 불리언 타입 288 | !!null; // -> false 289 | // undefined 타입 => 불리언 타입 290 | !!undefined; // -> false 291 | // 객체 타입 => 불리언 타입 292 | !!{}; // -> true 293 | !![]; // -> true 294 | ``` 295 | 296 | ## 9.4 단축 평가 297 | 298 | ### 9.4.1 논리 연산자를 사용한 단축 평가 299 | 300 | - 논리합(||) 또는 논리 곱(&&) 연산자 표현식의 평가 결과는 불리언 값이 아닐 수도 있다. 301 | 302 | ```js 303 | 'Cat' && 'Dog' // -> "Dog" 304 | ``` 305 | 306 | - 논리곱 연산자는 두 개의 피연산자가 모두 true로 평가될 때 true를 반환한다. 논리곱 연산자는 좌항에서 우항으로 평가가 진행된다. 307 | - 첫 번째 피연산자는 Truthy 값이므로 true로 평가된다. 하지만 이 시점까지는 위 표현식을 평가할 수 없다. 308 | - 두 번째 피연산자가 위 논리곱 연산자 표현식의 평가 결과를 결정한다. 309 | - 이때 논리곱 연산자는 **논리 연산의 결과를 결정하는 두 번째 피연산자. 즉 문자열 'Dog'를 그대로 반환한다.** 310 | 311 | ```js 312 | 'Cat' || 'Dog' // -> "Cat" 313 | ``` 314 | 315 | - 첫번째 피연산자 'Cat'은 Truthy 값이므로 true로 평가된다. 이 시점에 두 번째 피연산자까지 평가해 보지 않아도 위 표현식을 평가할 수 있다. 316 | - 이때 논리합 연산자는 **논리 연산의 결과를 결정한 첫 번째 피연산자, 즉 문자열 'Cat'을 그대로 반환한다.** 317 | - 논리곱 연산자와 논리합 연산자는 논리 연산의 결과를 결정하는 피연산자를 타입 변환하지 않고 그대로 반환한다. 이를 **단축 평가**라 한다. 318 | - **단축 평가는 표현식을 평가하는 도중에 평가 결과가 확정된 경우 나머지 평가 과정을 생략하는 것을 말한다.** 319 | 320 | ```js 321 | // 논리합(||) 연산자 322 | 'Cat' || 'Dog' // -> "Cat" 323 | false || 'Dog' // -> "Dog" 324 | 'Cat' || false // -> "Cat" 325 | 326 | // 논리곱(&&) 연산자 327 | 'Cat' && 'Dog' // -> "Dog" 328 | false && 'Dog' // -> false 329 | 'Cat' && false // -> false 330 | ``` 331 | 332 | - 단축 평가 이용사례 333 | 334 | ```js 335 | var elem = null; 336 | // elem이 null이나 undefined와 같은 Falsy 값이면 elem으로 평가되고 337 | // elem이 Truthy 값이면 elem.value로 평가된다. 338 | var value = elem && elem.value; // -> null 339 | ``` 340 | 341 | ```js 342 | // 단축 평가를 사용한 매개변수의 기본값 설정 343 | function getStringLength(str) { 344 | str = str || ''; 345 | return str.length; 346 | } 347 | 348 | getStringLength(); // -> 0 349 | getStringLength('hi'); // -> 2 350 | 351 | // ES6의 매개변수의 기본값 설정 352 | function getStringLength(str = '') { 353 | return str.length; 354 | } 355 | 356 | getStringLength(); // -> 0 357 | getStringLength('hi'); // -> 2 358 | ``` 359 | 360 | ### 9.4.2 옵셔널 체이닝 연산자 361 | 362 | - ES11(ECMAScript2020)에서 도입된 옵셔널 체이닝 연산자 `?.` 는 좌항의 피연산자가 `null` 또는 `undefined` 인 경우 `undefined` 를 반환하고 그렇지 않으면 우항의 프로퍼티 참조를 이어간다. 363 | 364 | ```js 365 | var elem = null; 366 | 367 | // elem이 null 또는 undefined이면 undefined를 반환하고, 그렇지 않으면 우항의 프로퍼티 참조를 이어간다. 368 | var value = elem?.value; 369 | console.log(value); // undefined 370 | ``` 371 | 372 | ### 9.4.3 null 병합 연산자 373 | 374 | - ES11(ECMAScript2020)에서 도입된 null 병합 연산자 `??` 는 좌항의 피연산자가 `null` 또는 `undefined` 인 경우 우항의 피연산자를 반환하고, 그렇지 않으면 좌항의 피연산자를 반환한다. 375 | - 변수의 기본값을 설정할 때 유용하게 사용할 수 있다. 376 | 377 | ```js 378 | // 좌항의 피연산자가 null 또는 undefined이면 우항의 피연산자를 반환하고, 그렇지 않으면 좌항의 피연산자를 반환한다. 379 | var foo = null ?? 'default string'; 380 | console.log(foo); // "default string" 381 | ``` 382 | 383 | -------------------------------------------------------------------------------- /10장-객체-리터럴/README.md: -------------------------------------------------------------------------------- 1 | # 10장 객체 리터럴 2 | 3 | ## 10.1 객체란? 4 | 5 |
6 | 7 | - 자바스크립트에서는 원시값을 제외하고 모든 것이 객체다. 8 | - 객체는 타입이라기보다 여러 값을 취급하는 자료구조에 가깝다. 9 | - 객체는 프로퍼티들로 구성되어 있으며 각 프로퍼티는 키, 값의 형태로 관리된다. 10 | - 일반적으로 키는 문자열로 취급되지만 symbol타입을 키로 사용할 수도 있다. 11 | - 프로퍼티에는 자바스크립트에서 취급하는 모든 타입의 값이 들어갈 수 있다. 12 | - 자바스크립트의 객체는 일급 객체이다. 13 | 14 | > 일급객체란? 15 | > 16 | > 18장에서 다루긴하지만 간략하게 언급하자면 일급객체는 다음 3가지 조건을 충족하는 객체를 의미한다. 17 | > 18 | > 1. 값으로써 할당될 수 있다. 19 | > 2. 함수에 인자로 사용될 수 있다. 20 | > 3. 함수의 반환값으로 사용되 수 있다. 21 | 22 | ## 10.2 객체 리터럴에 의한 객체 생성 23 | 24 | ### 리터럴이란? 25 | 책에서는 리터럴을 두고 '사람이 이해할 수 있는 문자 또는 약속된 기호를 사용하여 값을 생성하는 표기법'이라 정의한다. 쉽게 말하면 '직접 적어서 표현한' 값이라고 할 수 있겠다. 마트에 가서 콜라 한 병을 결제한다고 생각해보자. 우리는 결제수단으로 삼성페이, 실물 카드, 1000원 지폐 3장 등 여러가지 방법을 사용할 수 있다. 그러나 이중 1000원짜리 지폐만이 리터럴로 취급될 수 있다. 카드는 고정적인 값을 가지지 않는다. 쉽게 말해 알바 입장에서는 카드로 실제로 결제가 이루어지기 전까지는 콜라 한 병에 대한 값을 지불할 능력이 있는지 알 수 없다. 이는 카드라는 대상이 고정적인 가치를 지니지 않다는 사실을 내포한다. 그러나 현금은 어떠한가. 현금 3천원은 어제도 3천원이었고, 오늘도 3천원이며 별일 없으면 내일도 3천원일 것이다. 이러한 현금처럼 리터러를 고정적인 값을 가지며 해당하는 타입의 값에 있어 최소 단위가 된다. 1, 1.5, "hey" [1,2,3], { a: 1 } 등으로 표현되는 모든 리터럴 표현은 그 하나하나가 고정적인 하나의 값만을 의미한다. 26 | 27 | ### 객체 리터럴 28 | - 객체를 생성하는 가장 간단한 방법은 객체 리터럴이다. 29 | - 객체 리터럴은 중괄호({})로 표현하며 하나의 프로퍼티에 대해 콜론(:)을 기준으로 왼편에는 키, 오른편에는 값을 적는다. 30 | - 각 프로퍼티는 쉼표로 구분한다. 31 | 32 | ```js 33 | const obj = { 34 | a: "a", 35 | b: 123 36 | } 37 | ``` 38 | 39 | - 프로퍼티 없이 빈객체로도 선언할 수 있다. 40 | 41 |
42 | 43 | ## 10.3 프로퍼티 44 | 45 |
46 | 47 | - 앞서 말했듯 프로퍼티는 키와 값을 관리된다. 48 | - 키는 프로퍼티의 식별자 역할을 한다. 49 | - 객체의 키로는 심벌 혹은 문자열이 사용된다. 50 | - 이외 다른 타입을 사용하면 문자열로 암묵적 형변환 된다. 51 | - 키가 식별자 네이밍 규칙을 준수한다면 따옴표를 생략할 수 있다. 52 | - 대괄호([])를 사용하면 변수의 값을 키로 사용할 수 있다. 53 | 54 | ```js 55 | const obj = { 56 | firstName: "...", 57 | "last-name": "...", 58 | } 59 | ``` 60 | 61 | - 예약어도 프로퍼티의 키로 사용할 수는 있다. 그러나 권장되지 않는다. 62 | - 이미 존재하는 프로퍼티를 한 번 더 선언하면 나중에 선언된 프로퍼티가 기존 것을 덮어쓴다. 63 | 64 |
65 | 66 | ## 10.4 메서드 67 | 68 | - 자바스크립트의 프로퍼티 값으로는 모든 타입의 값이 사용될 수 있다. 69 | - 따라서 일급 객체인 자바스크립트의 객체도 프로퍼티 값이 될 수 있으며 함수도 프로퍼티의 값이 될 수 있다. 70 | - 프로퍼티의 값이 함수일 경우 이를 일반 함수와 구분하기 위해 '메서드'라고 부른다. 71 | 72 | ## 10.5 프로퍼티 접근 73 | 74 | - 프로퍼티에 대한 접근은 크게 두가지로 할 수 있다. 75 | - 점(.) 연산자에 의한 접근 76 | - 대괄호([]) 연산자에 의한 접근 77 | - 점 연산자로 접근할 경우 점에는 식별자 네이밍 규칙을 준수하는 단어만 올 수 있다. 78 | - 대괄호 안에는 식별자 혹은 리터럴을 넣을 수 있다. 79 | - 프로퍼티에 접근하여 값을 읽을 뿐아니라 값을 변경할 수도 있다. 80 | ```js 81 | const obj = { 82 | "first-value": 1, 83 | secondValue: 2 84 | } 85 | 86 | // 값을 변경 87 | obj.["first-value"] = 3; 88 | obj.secondValue = 4; 89 | 90 | // 값을 참조 91 | console.log(obj.["first-value"]); // 3 92 | console.log(obj.secondValue); // 4 93 | ``` 94 | - 객체 안에 없는 프로퍼티를 참조하면 undefined를 반환한다. 95 | - 객체 안에 없는 키에 값을 할당하면 새로운 프로퍼티가 생성된다. 96 | ```js 97 | const obj = {}; 98 | 99 | console.log(obj.a); // undefined 100 | obj.a = 1; 101 | console.log(obj.a); // 1 102 | ``` 103 | 104 |
105 | 106 | ## 10.8 프로퍼티 삭제 107 | 108 |
109 | 110 | - delete 연산을 통해 객체의 프로퍼티를 삭제할 수 있다. 111 | - delete 연산의 피연산자는 프로퍼티 값에 접근할 수 있는 표현식이어야 한다. 112 | 113 | ```js 114 | const obj = { a: 1 } 115 | 116 | delete obj.a; 117 | consol.log(obj.a); // undefined; 118 | ``` 119 | 120 |
121 | 122 | ## 10.9 ES6에서 추가된 객체 리터럴의 확장 기능 123 | 124 | ### 프로퍼티 축약 표현 125 | - 프로퍼티의 값을 변수를 사용하는 경우 프로퍼티의 키가 변수명과 같으면 이를 축약해서 나타낼 수 있다. 126 | 127 | ```js 128 | const someValue = "hey!!"; 129 | const obj = { 130 | someValue: someValue, 131 | }; 132 | 133 | // 위 표현을 아래처럼 나타낼 수 있다. 134 | 135 | const someValue = "hey!!"; 136 | const obj = { 137 | someValue, 138 | }; 139 | 140 | ``` 141 | 142 | ### 계산된 프로퍼티 이름 143 | - 대괄호([])를 사용하면 객체 리터럴 상에서 표현식의 평가값을 키로 사용할 수 있다. 144 | 145 | ```js 146 | const key = "key"; 147 | 148 | const obj = { 149 | [key]: 0, 150 | [key + 1]: 1, 151 | [key + 2]: 2, 152 | } 153 | 154 | // 위 객체는 다음 리터럴과 같이 해석된다. 155 | 156 | const obj = { 157 | key: 0, 158 | key1: 1, 159 | key2: 2, 160 | } 161 | ``` 162 | 163 | - 객체의 키로 변수를 포함하는 표현식을 사용하는 순간 이를 '리터럴'로 부를 수 있나 의문이 든다. 좀 더 공부해봐야겠다. 164 | 165 | ### 메서드 축약 표현 166 | - 메서드 또한 축약 표현이 존재한다. 167 | 168 | ```js 169 | // 기존에는 메서드를 다음과 같이 정의했다. 170 | const obj = { 171 | testFn: function() { 172 | ... 173 | }, 174 | }; 175 | 176 | // 축약 표현은 다음과 같다. 177 | const obj = { 178 | testFn() { 179 | ... 180 | } 181 | } 182 | ``` 183 | 184 | - 축약이라고는 했지만 사실 전자와 후자로 선언한 메서드는 조금 성격이 다르다. 축약 표현에 경우 constructor로 사용될 수 없는 등 차이점이 있다. 자세한 내용은 26장에서 다룬다. -------------------------------------------------------------------------------- /11장-원시-값과-객체의-비교/README.md: -------------------------------------------------------------------------------- 1 | # 11장 원시 값과 객체의 비교 2 | 3 | - 자바스크립트가 제공하는 7가지 데이터 타입은 크게 `원시 타입`과 `객체 타입`으로 구분할 수 있고 크게 세 가지 측면에서 다르다. 4 | - 원시 타입의 값, 즉 원시 값은 변경 불가능한 값이다. 이에 비해, 객체 타입의 값, 즉 객체는 변경 가능한 값이다. 5 | - 원시 값을 변수에 할당하면 변수(확보된 메모리 공간)에는 실제 값이 저장된다. 이에 비해 객체를 변수에 할당하면 변수에는 참조 값이 저장된다. 6 | - 원시 값을 갖는 변수를 다른 변수에 할당하면 원본의 원시 값이 복사되어 전달된다. 이에 비해 객체를 가리키는 변수를 다른 변수에 할당하면 원본의 참조 값이 복사되어 전달된다. 7 | 8 | 9 | 10 | ### 11.1 원시값 11 | #### 11.1.1 변경 불가능한 값 12 | --- 13 | - 한 번 생성된 원시 값은 읽기 전용 값으로서 변경할 수 없다. 14 | - 변경 불가능 한 것은 변수가 아니라 `값`에 대한 진술이다. 즉, "원시 값은 변경 불가능하다"는 말은 원시 값 자체를 변경할 수 없다는 것이지 변수 값을 변경할 수 없다는 것이 아니다. 15 | - 원시 값은 어떤 일이 있어도 불변하는 특성을 가지고 있기 떄문에 `데이터의 신뢰성`을 보장한다. 16 | - `불변성`을 갖는 원시 값을 할당한 변수는 `재할당` 이외에 변수 값을 변경할 수 있는 방법이 없다. 17 | 18 | 19 |
20 | 21 | #### 11.1.2 문자열과 불변성 22 | --- 23 | - 문자열도 원시값이기 떄문에 변경 불가능한 값이다. 24 | - 원시 값인 문자열은 다른 원시 값과 비교했을 때 독특한 특징이 있다. 25 | - 문자열은 0개 이상의 문자로 이뤄진 집합을 말하며, 1개의 문자는 2바이트의 메모리 공간에 저장된다. 26 | - 숫자값은 1도, 10000000000도 동일한 8바이트가 필요하지만, 문자열의 경우 (단순하게 계산했을 때) 1개의 문자로 이뤄진 문자열은 2바이트, 10개면 20바이트가 필요하다 27 | - 문자열은 유사 배열 객체이면서 iterable이므로 배열과 유사하게 각 문자에 접근할 수 있다. 28 | 29 | > 유사 배열 객체 : 마치 배열처럼 인덱스로 프로퍼티 값에 접근할 수 있고, length 프로퍼티를 갖는 객체 30 | 31 |
32 | 33 | #### 11.1.3 값에 의한 전달 34 | --- 35 | 36 | ```javascript 37 | 38 | var test = 80; 39 | var copy = test; 40 | 41 | console.log(test) // 80 42 | console.log(copy) // 80 43 | 44 | test = 100; 45 | 46 | console.log(test) // 100 47 | console.log(copy) // 80 48 | ``` 49 |
50 | 51 | - 위처럼, 변수에 원시 값을 갖는 변수를 할당하면 할당 받는 변수(copy)에는 할당되는 변수(test)의 원시 값이 복사되어 전달된다. 이를 `값에 의한 전달`이라 한다. 52 | - test 변수와 copy 변수는 숫자 값 80을 갖는다는 점에서는 동일하지만, `test 변수`와 `copy 변수`의 값 80은 다른 메모리 공간에 저장된 별개의 값이다. 떄문에, test 변수의 값을 변경해도 copy 변수의 값에는 어떠한 영향도 주지 않는다. 53 | - 참고로, `값에 의한 전달`이라는 용어는 자바스크립트를 위한 용어가 아니므로 오해가 있을 수도 있다. 엄격하게 표현하면 변수에는 값이 전달되는 것이 아니라 메모리 주소가 전달되기 때문이다. 이는 변수와 같은 식별자는 값이 아니라 메모리 주소를 기억하고 있기 때문이다. 54 | 55 |
56 | 57 | 58 | ### 11.2 객체 59 | #### 11.2.1 변경 가능한 값 60 | --- 61 | 62 | - 객체(참조) 타입의 값, 즉 객체는 `변경 가능한 값`이다. 63 | - 객체는 프로퍼티의 개수가 정해져 있지 않으며, 동적으로 추가되고 삭제할 수 있다. 따라서, 원시 값과 같이 확보해야 할 메모리 공간의 크기를 사전에 정해 둘 수 없다. 64 | - 객체를 할당한 변수가 기억하는 메모리 주소를 통해 메모리 공간에 접근하면 `참조 값`에 접근할 수 있다. 참조 값은 생성된 객체가 저장된 메모리 공간의 주소, 그 자체다. 65 | - 객체를 할당한 변수를 참조하면 메모리에 저장되어 있는 참조 값을 통해 실제 객체에 접근한다. 66 | - 객체를 할당한 변수는 재할당 없이 객체를 `직접 변경할 수 있다`. 67 | - 객체를 생성하고 관리하는 방식은 매우 복잡하며 비용이 많이 드는 일이다. 객체는 크기가 매우 클 수도 있고, 프로퍼티 값이 객체일 수도 있어서 복사해서 생성하는 비용이 많이든다. 다시 말해, 메모리의 효율적 소비가 어렵고 성능이 나빠진다. 따라서 메모리를 효율적으로 사용하기 위해, 그리고 객체를 복사해 생성하는 비용을 절약하여 성능을 향상시키기 위해 객체는 변경 가능한 값으로 설계되어 있다. 68 | - 객체는 이러한 구조적 단점에 따른 부작용이 있다. 원시 값과는 다르게 `여러 개의 식별자가 하나의 객체를 공유할 수 있다`는 것이다. 69 | 70 |
71 | 72 | #### 11.2.2 참조에 의한 전달 73 | --- 74 | 75 | ```javascript 76 | var person = { 77 | name: 'Kim' 78 | }; 79 | 80 | var copy = person 81 | ``` 82 | 83 | - 객체를 가리키는 변수(person)를 다른 변수(copy)에 할당하면 원본의 참조 값이 복사 되어 전달된다. 이를 `참조에 의한 전달`이라 한다. 84 | - 원본 person과 사본 copy는 저장된 메모리 주소는 다르지만 동일한 참조 값을 갖는다. 즉, 둘은 모두 동일한 객체를 가리킨다. 85 | - 따라서, 원본 또는 사본 중 어느 한쪽에서 객체를 변경하면 서로 영향을 주고 받는다. 86 | - 결국 `값에 의한 전달`과 `참조에 의한 전달`은 식별자가 기억하는 메모리 공간에 저장되어 있는 값을 복사해서 전달한다는 면에서 동일하다. -------------------------------------------------------------------------------- /13장-스코프/README.md: -------------------------------------------------------------------------------- 1 | # 13장 스코프 2 | 3 | ## 13.1 스코프란? 4 | 5 | - #### 스코프는 식별자가 유효한 범위를 말한다. 6 | 7 | - **함수의 경우** 8 | 9 | **함수의 매개변수**는 함수 몸체 내부에서만 참조할 수 있다. 참조할 수 있는 유효범위(매개변수의 스코프)가 함수 몸체 내부로 한정되기 때문이다. 10 | 11 | ```javascript 12 | function add(x, y) { 13 | // 매개변수는 함수 몸체 내부에서만 참조할 수 있다. 14 | // 즉, 매개변수의 스코프(유효범위)는 함수 몸체 내부다. 15 | console.log(x, y); // 2 5 16 | return x + y; 17 | } 18 | 19 | add(2, 5); 20 | 21 | // 매개변수는 함수 몸체 내부에서만 참조할 수 있다. 22 | console.log(x, y); // ReferenceError: x is not defined 23 | ``` 24 | 25 | - **변수의 경우** 26 | 27 | 변수는 코드의 가장 바깥 영역뿐 아니라 코드 블록이나 함수 몸체에서도 선언할 수 있다. 28 | 29 | 변수는 **자신이 선언된 위치**에 의해 자신이 **유효한 범위**, 즉 **다른 코드가 변수 자신을 참조할 수 있는 범위**가 결정된다. (식별자도 동일) 30 | 31 | 즉, 모든 식별자(변수 이름, 함수 이름, 클래스 이름 등)는 자신이 선언된 위치에 의해 다른 코드가 식별자 자신을 참조할 수 있는 유효범위가 결정된다. 이를 스코프라 한다. 32 | 33 | ```javascript 34 | var x = 'global'; 35 | 36 | function foo() { 37 | var x = 'local'; 38 | console.log(x); // local 39 | } 40 | 41 | foo(); 42 | 43 | console.log(x); // global 44 | ``` 45 | 46 | - #### 스코프는 자바스크립트 엔진이 식별자를 검색할 때 사용하는 규칙이다. 47 | 48 | - 식별자 결정 : 자바스크립트 엔진은 이름이 같은 두 변수 중 어떤 변수를 참조해야할 것인지를 결정한다. 49 | 50 | - 코드의 문맥과 환경 51 | 52 | 자바스크립트 엔진은 코드를 실행할 때 **코드의 문맥(context)** 을 고려한다. 53 | 54 | 렉시컬 환경(lexical environment) : 코드가 어디서 실행되며 주변에 어떤 코드가 있는지 55 | 56 | 코드의 문맥(context) : 렉시컬 환경으로 이루어짐 57 | 58 | 실행 컨텍스트(execution context) : 코드의 문맥을 구현한 것. 모든 코드는 실행 컨텍스트에서 평가되고 실행된다. 59 | 60 | - #### 스코프는 네임스페이스다. 61 | 62 | - 스코프를 통해 식별자인 변수 이름의 충돌을 방지하여 같은 이름의 변수를 사용할 수 있게 한다. 스코프 내에서 식별자는 유일해야하지만, 다른 스코프에는 같은 이름의 식별자를 사용할 수 있다. 63 | 64 | ```javascript 65 | function foo() { 66 | var x = 1; 67 | // var 키워드로 선언된 변수는 같은 스코프 내에서 중복 선언을 허용한다. 68 | // 아래 변수 선언문은 자바스크립트 엔진에 의해 var 키워드가 없는 것처럼 동작한다. 69 | var x = 2; 70 | console.log(x); // 2 71 | } 72 | foo(); 73 | 74 | function bar() { 75 | let x = 1; 76 | // let이나 const 키워드로 선언된 변수는 같은 스코프 내에서 중복 선언을 허용하지 않는다. 77 | let x = 2; // SyntaxError: Identifier 'x' has already been declared 78 | } 79 | bar(); 80 | ``` 81 | 82 | 83 | 84 | ## 13.2 스코프의 종류 85 | 86 | 코드는 전역과 지역으로 구분할 수 있다. 87 | 88 | | 구분 | 설명 | 스코프 | 변수 | 89 | | ---- | --------------------- | ----------- | --------- | 90 | | 전역 | 코드의 가장 바깥 영역 | 전역 스코프 | 전역 변수 | 91 | | 지역 | 함수 몸체 내부 | 지역 스코프 | 지역 변수 | 92 | 93 | 변수는 자신이 전언된 위치(전역/지역)에 의해 자신이 유효한 범위인 스코프(전역 스코프/지역 스코프)가 결정된다. 94 | 95 | ### 13.2.1 전역과 전역 스코프 96 | 97 | - 전역 변수는 어디서든지 참조할 수 있다. 98 | 99 | ### 13.2.2 지역과 지역 스코프 100 | 101 | - 지역이란 함수 몸체 내부를 말한다. 102 | 103 | ```javascript 104 | var x = 'global'; 105 | 106 | function foo() { 107 | var x = 'local'; 108 | console.log(x); // local 109 | } 110 | 111 | foo(); 112 | 113 | console.log(x); // global 114 | ``` 115 | 116 | foo 함수 내부에서 선언된 x는 **지역변수**다. 지역 변수 x는 자신의 지역 스코프인 함수 foo 내부에서만 참조할 수 있다. 하지만 지역 변수 x를 전역 또는 foo 함수 내부 이외의 지역에서 참조하면 **참조 에러**가 발생한다. 117 | 118 | 그런데 foo 함수 내부에서 선언된 x 변수 이외에 이름이 같은 **전역 변수** x가 존재한다. 이 때 foo 함수 내부에서 x 변수를 참조하면 전역 변수 x를 참조하는 것이 아니라 foo 함수 내부에서 선언된 x 변수를 참조한다. 이는 자바스크립트 엔진이 **스코프 체인**을 통해 참조할 변수를 검색(identifier resolution)했기 때문이다. 119 | 120 | 121 | 122 | ## 13.3 스코프 체인 123 | 124 | - **중첩함수** : 함수 몸체 내부에서 정의한 함수 125 | - **외부함수** : 중첩함수를 포함하는 함수 126 | 127 | - **스코프**는 함수의 중첩에 의해 **계층적 구조**를 갖는다. 128 | - 외부함수의 지역 스코프를 중첩 함수의 상위 스코프라 한다. 129 | - 모든 지역 스코프의 최상위 스코프는 **전역 스코프**다. 130 | - **스코프 체인**은 물리적인 실체로 존재한다. 131 | - 자바스크립트 엔진은 코드를 실행하기에 앞서 **렉시컬 환경**을 생성한다. 132 | - **변수 선언**이 실행되면 변수 식별자가 렉시컬 환견에 키로 등록된다. 133 | - **변수 할당**이 일어나면 렉시컬 환경의 변수 식별자에 해당하는 값을 변경한다. 134 | - **변수의 검색**도 렉시컬 환경 상에서 이루어진다. 135 | - **변수를 참조**할 때 자바스크립트 엔진은 **스코프 체인**을 통해 참조하는 코드의 스코프에서 시작하여 상위 스코프 방향으로 이동하며 선언된 변수를 검색한다. 136 | - **스코프 체인**은 실행 컨텍스트의 렉시컬 환경을 단방향으로 연결 한 것이다. 137 | - **전역 렉시컬 환경** : 코드가 로드되면 곧바로 생성된다. 138 | - **함수의 렉시컬 환경** : 함수가 호출되면 곧바로 생성된다. 139 | 140 | ### 13.3.1 스코프 체인에 의한 변수 검색 141 | 142 | - 상위 스코프에서 유효한 변수는 하위 스코프에서 자유롭게 참조할 수 있지만 하위 스코프에서 유효한 변수를 상위 스코프에서 참조할 수 없다. 143 | - 스코프 체인으로 연결된 스코프의 계층적 구조는 부자관계로 이뤄진 상속과 유사하다. (상속 : 부모의 자산을 자식이 자유롭게 사용할 수 있지만 자식의 자산을 부모가 사용할 순 없다.) 144 | 145 | ### 13.3.2 스코프 체인에 의한 함수 검색 146 | 147 | - 배경지식 : 함수 선언문으로 함수를 정의하면 런타임 이전에 함수 객체가 먼저 생성된다. 그리고 자바스크립트 엔진은 함수 이름과 동일한 이름의 식별자를 암묵적으로 선언하고 생성된 함수 객체를 할당한다. 148 | 149 | - 함수도 식별자에 할당되기 때문에 스코프를 갖는다. 함수는 식별자에 함수 객체가 할당된 것 외에 일반 변수과 다를 게 없다. 따라서 스코프는 "**식별자를 검색하는 규칙**"이다. 150 | 151 | 152 | 153 | ## 13.4 함수 레벨 스코프 154 | 155 | 지역은 함수 몸체 내부를 말하고 지역은 지역 스코프를 만든다. 이는 코드 블록이 아닌 **함수에 의해서만 지역 스코프가 생성된다**는 의미다. 156 | 157 | - 블록 레벨 스코프 : 모든 코드 블록(if, for, while, try/catch 등)이 지역 스코프를 만든다. 158 | - 함수 레벨 스코프 : var 키워드로 선언된 변수는 오로지 함수의 코드 블록(함수 몸체) 만을 지역 스코프로 인정한다. 159 | 160 | ```javascript 161 | var x = 1; 162 | 163 | if (true) { 164 | // var 키워드로 선언된 변수는 함수의 코드 블록(함수 몸체)만을 지역 스코프로 인정한다. 165 | // 함수 밖에서 var 키워드로 선언된 변수는 코드 블록 내에서 선언되었다 할지라도 모두 전역 변수다. 166 | // 따라서 x는 전역 변수다. 이미 선언된 전역 변수 x가 있으므로 x 변수는 중복 선언된다. 167 | // 이는 의도치 않게 변수 값이 변경되는 부작용을 발생시킨다. 168 | var x = 10; 169 | } 170 | 171 | console.log(x); // 10 172 | ``` 173 | 174 | 175 | 176 | ## 13.5 렉시컬 스코프 177 | 178 | ```javascript 179 | var x = 1; 180 | 181 | function foo() { 182 | var x = 10; 183 | bar(); 184 | } 185 | 186 | function bar() { 187 | console.log(x); 188 | } 189 | 190 | foo(); // ? 191 | bar(); // ? 192 | ``` 193 | 194 | - 자바스크립트는 렉시컬 스코프를 따른다. 195 | - **렉시컬 스코프**(정적 스코프) : 상위 스코프가 동적으로 변하지 않고 함수 정의가 평가되는 시점에 상위 스코프가 정적으로 결정된다. 196 | - 즉, 함수를 어디서 정의했는지에 따라 상위 스코프를 결정한다. 197 | - 함수 정의(함수 선언문 또는 함수 표현식)가 실행되어 생성된 함수 객체는 결정된 상위 스코프를 기억한다. -------------------------------------------------------------------------------- /14장-전역-변수의-문제점/README.md: -------------------------------------------------------------------------------- 1 | # 14장 전역 변수의 문제점 2 | 3 | ## 14.1 변수의 생명 주기 4 | 5 | ### 14.1.1 지역 변수의 생명 주기 6 | 7 | - 변수는 **선언**에 의해 생성되고 **할당**을 통해 값을 갖는다. 8 | - 전역 변수의 생명 주기는 애플리케이션의 생명 주기와 같지만 대부분 지역 변수의 생명 주기는 함수의 생명 주기와 같다. 9 | - 함수의 생명 주기가 끝난 후에도 해당 스코프의 참조 카운터가 유지 된다면 스코프가 해제되지 않고 지역 변수는 생존하게 된다. 10 | - 이와 관련된 내용은 `24장-클로저`에서 확인할 수 있다. 11 | 12 | ```javascript 13 | function foo() { 14 | var x = "local"; 15 | console.log(x); // local 16 | return x; 17 | } 18 | 19 | foo(); 20 | console.log(x); 21 | ``` 22 | 23 | ### 14.1.2 전역 변수의 생명주기 24 | 25 | - 전역 코드는 함수 호출과 같이 전역 코드를 실행하는 특별한 진입점이 없고 코드가 로드되자마자 곧바로 해석되고 실행된다. 26 | - 전역 코드는 반환문을 사용할 수 없으므로 마지막 문이 실행되어 더 이상 실행될 문이 없을 때 종료한다. 27 | 28 | ## 14.2 전역 변수의 문제점 29 | 30 | ### 14.2.1 암묵적 결합 31 | 32 | - 전역 변수는 코드 어디서든 참조하고 할당할 수 있다. 33 | - 이는 모든 코드가 전역 변수를 참조하고 변경할 수 있는 **암묵적 결합**을 허용하는 것이다. 34 | - 변수의 유효 범위가 커질수록 코드의 가독성은 나빠지고 위험성은 높아진다. 35 | 36 | ### 14.2.2 긴 생명 주기 37 | 38 | - 전역 변수는 생명주기가 길다. 39 | - 따라서 메모리 리소스도 오랜 기간 소비하게 된다. 40 | 41 | ### 14.2.3 스코프 체인 상에서 종점에 존재 42 | 43 | - 전역 변수는 스코프 체인 상에서 종점에 존재한다. 44 | - 즉, 전역 변수의 검색 속도가 가장 느리다. 45 | 46 | ### 14.2.4 네임스페이스 오염 47 | 48 | - 자바스크립트는 파일이 분리되어 있어도 하나의 전역 스코프를 공유한다. 49 | - 따라서 다른 파일 내에서 동일한 이름으로 명명된 전역 변수가 존재할 경우 예상치 못한 결과를 가져올 수 있다. 50 | 51 | ## 14.3 전역 변수의 사용을 억제하는 방법 52 | 53 | - 전역 변수를 반드시 사용해야 할 이유를 찾지 못한다면 지역 변수를 사용해야 한다. 54 | - 변수의 스코프는 좁을수록 좋다. 55 | 56 | ### 14.3.1 즉시 실행 함수 57 | 58 | - 모든 코드를 즉시 실행 함수로 감싸면 모든 변수는 즉시 실행 함수의 지역 변수가 된다. 59 | 60 | ### 14.3.2 네임스페이스 객체 61 | 62 | - 전역에 네임스페이스 역할을 담당할 객체를 생성하고 전역 변수처럼 사용하고 싶은 변수를 프로퍼티로 추가하는 방법. 63 | 64 | ```js 65 | var MYAPP = {}; 66 | 67 | MYAPP.name = "yujo"; 68 | 69 | MYAPP.person = { 70 | name: "yujo", 71 | address: "seoul", 72 | }; 73 | ``` 74 | 75 | ### 14.3.3 모듈 패턴 76 | 77 | - 모듈 패턴은 관련 있는 변수와 함수를 모아 즉시 실행 함수로 감싸 하나의 모듈을 만든다. 78 | - 모듈 패턴은 클로저를 기반으로 동작 한다. 79 | - 모듈 패턴은 전역 변수를 억제할 수 있으며 캡슐화까지 구현할 수 있다. 80 | 81 | > 캡슐화: 객체의 상태를 나타내는 프로퍼티와 프로퍼티를 참조하고 조작할 수 있는 동작인 메서드를 하나로 묶는 것을 말한다. 82 | 83 | ```js 84 | var Counter = (function () { 85 | var num = 0; // private 변수 86 | 87 | // 외부로 공개할 데이터나 메서드 88 | return { 89 | increase() { 90 | return ++num; 91 | }, 92 | decrease() { 93 | return --num; 94 | }, 95 | }; 96 | })(); 97 | 98 | console.log(Counter.num); // undefined 99 | console.log(Counter.increase()); // 1 100 | ``` 101 | 102 | ### 14.3.4 ES6 모듈 103 | 104 | - **ES6 모듈은 파일 자체의 독자적인 모듈 스코프를 제공한다.** 따라서 더는 전역 변수를 사용할 수 없다. 105 | - ES6 모듈을 사용하기 위해서는 ` 109 | ``` 110 | 111 | - ES6 모듈은 IE를 포함한 구형 브라우저에서는 동작하지 않는다. 112 | - 때문에 아직까지는 브라우저가 지원하는 ES6 모듈 기능보다는 Webpack등의 모듈 번들러를 사용하는 것이 일반적이다. 113 | -------------------------------------------------------------------------------- /15장-let,const키워드와-블록-레벨-스코프/README.md: -------------------------------------------------------------------------------- 1 | # 15장 let, const 키워드와 블록 레벨 스코프 2 | 3 | ## 15.1 var 키워드로 선언한 변수의 문제점 4 | 5 |
6 | 7 | 자바스크립트의 `var` 키워드를 통해 선언된 변수는 다른 언어들과는 조금 다른 독특한 성질들을 갖는다. 따라서 다른 언어를 작성하듯 `var` 키워드를 사용하면 여러 문제를 마주할 수 있다. 예컨대 다음과 같은 상황이다. 8 | 9 |
10 | 11 | 1. 변수의 중복 선언을 허용한다. 12 | - 이미 선언된 변수가 다른 선언에 의해 덮어씌워질 수 있다. 13 | - 초기화없는 선언의 경우 앞선 선언에 의해 무시될 수 있다. 14 | 2. 함수 레벨 스코프를 따른다. 15 | - 쉽게 말해 실행 컨텍스트 단위로만 스코프가 형성된다고 할 수 있겠다. 16 | - 이는 하나의 실행 컨텍스트 안에서는 모든 변수가 전역으로 취급된다는 것을 의미한다. 17 | - 개인적으로 특정 언어의 스코프 상 특징은 그 언어를 사용하는 개발자가 충분히 숙지하고 그에 맞게 사용해야할 몫이라고 생각하지만 스코프를 나눌 수단이 함수 하나만으로 제한되는 것은 분명히 불편한 점이다. 18 | 3. 호이스팅에 의한 참조 19 | - 자바스크립트의 변수는 실행컨텍스트가 생성될 때, 즉 런타임 이전에 렉시컬 환경에 등록된다. 20 | - 특히 var키워드로 생성된 변수는 실행컨텍스트 생성 시 변수 선언과 동시에 초기화 단계가 진행되는데 이때 변수의 값을 undefined로 초기화 한다. 21 | - 자바스크립트 런타임에서 특정 변수를 사용할 수 있는 지는 변수 초기화 여부에 달렸다. var 키워드로 선언한 변수의 경우 런타임 이전에 초기화를 하기 때문에 런타임에서 변수 선언문 위치와 관계없이 사용할 수 있다. 가령 다음과 같은 상황이 가능해진다. 22 | ``` 23 | console.log(foo); // undefined 24 | foo = 123; 25 | console.log(foo); // 123 26 | // 변수 선언 전이지만 참조, 할당 모두 가능 27 | 28 | var foo = 1; 29 | ``` 30 | 31 | > **tip!**
32 | > var 키워드를 사용하더라도 런타임이전에 '할당'을 하지는 않는다. 33 | - 일반적인 변수 사용에서 선언을 뒤늦게 할 이유가 전혀 없다. 이는 오히려 가독성만 해칠 뿐이다. 34 | 35 | 이러한 `var`키워드의 문제점을 보완하기위해 ES6에서는 let과 const를 도입했다. 36 | 37 |
38 | 39 | ## 15.2 let 키워드 40 | 41 |
42 | 43 | `let` 키워드로 선언한 변수는 다음과 같은 특징을 갖는다. 44 | 45 | 1. 변수 중복을 허용하지 않는다. 46 | - let키워드를 통해 같은 이름의 변수를 중복 선언하면 SyntaxError가 발생한다. 47 | 2. 블록 레벨 스코프를 따른다. 48 | - 실행 컨텍스트 자체는 기존처럼 실행가능한 코드 단위(전역, 함수, eval)로 생성하지만 블록({}) 구문을 만날 때 마다 새로운 렉시컬 환경을 생성함으로써 블로 스코핑을 구현한다. 49 | 3. 코드 상 선언 전에는 참조 할 수 없다. 50 | - let키워드를 통해 선언된 변수도 호이스팅된다. 51 | - 그러나 런타임 전에 선언만 될 뿐 초기화 하지는 않는다. 52 | - 즉, 쓰레기 값이 그대로 남아있는 상태 53 | - 자바스크립트에서는 초기화 되지 않은 변수를 사용할 수 없기 때문에(js는 쓰레기 값 못참지) reference error가 발생한다. 54 | - 호이스팅 이후 실제 선언문이 실행될때에서야 초기화가 된다.(undefined) 55 | - 초기화 되기 전까지 `선언은 되었지만 참조할 수 없는 상태에 있는` 구간을 TDZ(Temporal Dead Zone)이라고 부른다. 56 | 57 | > **tip!**
58 | > 간혹 let, const로 선언된 변수에 대해 호이스팅되지 않는다고 오해하는 경우가 있다. 이는 호이스팅이 정의상 변수 선언과 초기화를 모두 포함하는 개념임을 전제로한다. 그러나 이런 정의는 다음과 같은 상황을 이해하는데 방해가 된다. 59 | > ``` 60 | > let foo = 1; 61 | > 62 | > { 63 | > console.log(foo) // ReferenceError! 64 | > let foo = 2; 65 | > } 66 | > ``` 67 | > 위 코드에서 reference error가 발생한다는 것은 해당 블록 최상단에서 '어떠한 일'이 벌어졌음을 의미한다. 즉, foo 변수의 선언이 '끌어올려'졌다고 보는 것이 타당하다. let 키워드에 의해 끌어올림 현상(호이스팅)이 발생하지 않는다 말하려면 foo가 새로운 선언문을 만나기 전까지 기존 값을 가지고 있어야 자연스럽다. 따라서 호이스팅은 변수의 선언 단계에 한해 이야기 되어져야 한다. 실제로 [ecma-262](https://262.ecma-international.org/11.0/#sec-for-in-and-for-of-statements-runtime-semantics-bindinginitialization) 문서상으로도 hoist되는 현상과 preinitialize되는 현상을 구분한다. 68 | 4. 전역 객체의 프로퍼티로 취급되지 않는다. 69 | 70 |
71 | 72 | ## 15.3 const 키워드 73 | const 키워드는 let 키워드의 특징을 상당부분 공유하나 다음과 같은 차이점을 갖는다. 74 | 75 | 1. 변수의 선언과 초기화(할당)가 동시에 이루어져야한다. 76 | 2. 다른 값을 재할당할 수 없다. 77 | > tip!
78 | > 재할당이 불가능하다는 특징과 변경이 불가능하다는 특징을 혼동해서는 안된다. const로 선언된 변수에 원시값이 할당되어 있다면 그 값에 대한 변경도 불가능하지만 객체가 할당되어 있을 경우에는 조금 이야기가 다르다. 변수에 할당되어 있는 객체의 주소값은 바뀌지 않겠지만 자바스크립트 객체의 특성인 변경 가능하다는 성질까지 바뀌지는 않는다. 따라서 다음과 같은 상황은 충분히 가능하다. 79 | > ``` 80 | > const person = { 81 | > name: "Lee" 82 | > }; 83 | > 84 | > person.name = "Kim"; 85 | > console.log(person); // { name: "Kim" } 86 | > ``` 87 | > 88 | 89 | const 키워드는 상수로 사용할 목적의 값에도 유용하지만 전반적인 변수 선언에 사용하는 것이 좋다. 추후 재할당을 할 '의도'를 가진 변수가 아니라면 불필요한 가능성은 모두 제거하는 편이 안전하기 때문이다. -------------------------------------------------------------------------------- /17장-생성자-함수에-의한-객체-생성/README.md: -------------------------------------------------------------------------------- 1 | # 17장 생성자 함수에 의한 객체 생성 2 | 3 | ### 17.1 Object 생성자 함수 4 | 5 | --- 6 | 7 | - 생성자 함수란 new 연산자와 함께 호출하여 객체(인스턴스)를 생성하는 함수를 말한다. 8 | 9 | - new 연산자와 함께 Object 생성자 함수를 호출하면 빈 객체를 생성하여 반환한다. 10 | 11 | ```javascript 12 | // 빈 객체의 생성 13 | const person = new Object(); 14 | ``` 15 | 16 | - 자바스크립트는 Object 생성자 함수 이외에도 String, Number, Boolean, Function, Array등의 빌트인 생성자 함수를 제공한다. 17 | 18 | 19 | 20 |
21 | 22 | 23 | 24 | ### 17.2 생성자 함수 25 | 26 | #### 17.2.1 객체 리터럴에 의한 객체 생성 방식의 문제점 27 | 28 | --- 29 | 30 | - 객체 리터럴에 의한 객체 생성 방식은 직관적이고 간편하지만 단 하나의 객체만 생성한다. 31 | - 따라서 동일한 프로퍼티를 갖는 객체를 여러 개 생성해야 하는 경우 매번 같은 프로퍼티를 기술해야 하기 때문에 비효율적이다. 32 | 33 |
34 | 35 | #### 17.2.2 생성자 함수에 의한 객체 생성 방식의 장점 36 | 37 | --- 38 | 39 | - 생성자 함수에 의한 객체 생성 방식은 마치 객체(인스턴스)를 생성하기 위한 템플릿(클래스)처럼 생성자 함수를 사용하여 프로퍼티 구조가 동일한 객체 여러 개를 간편하게 생성할 수 있다. 40 | 41 | ```javascript 42 | // 생성자 함수 43 | function Circle(radius) { 44 | // 생성자 함수 내부의 this는 생성자 함수가 생성할 인스턴스를 가리킨다. 45 | this.raduis = radius; 46 | this.getDiameter = function () { 47 | return 2 * this.radius; 48 | }; 49 | } 50 | 51 | // 인스턴스의 생성 52 | const circle1 = new Circle(5); 53 | const circle2 = new Circle(10); 54 | ``` 55 | 56 | > `this`는 객체 자신의 프로퍼티나 메서드를 참조하기 위한 자기 참조 변수다. this가 가리키는 값은 함수 호출 방식에 따라 동적으로 결정된다. 57 | 58 | - 자바와 같은 클래스 기반 객체지향언어의 생성자와는 다르게 그 형식이 정해져 있는 것이 아니라 일반 함수와 동일한 방법으로 생성자 함수를 정의하고 `new 연산자와 함께 호출하면 해당 함수는 생성자 함수로 동작한다.` 만약, new 연산자와 함께 생성자 함수를 호출하지 않으면 일반 함수로 동작한다. 59 | 60 |
61 | 62 | #### 17.2.3 생성자 함수의 인스턴스 생성 과정 63 | 64 | --- 65 | 66 | - 먼저, 생성자 함수의 역할은 프로퍼티 구조가 동일한 인스턴스를 생성하기 위한 템플릿(클래스)으로서 동작하여 `인스턴스`를 생성하는 것과 `생성된 인스턴스를 초기화(인스턴스 프로퍼티 추가 및 초기값 할당)`하는 것이다. 67 | - new 연산자와 함께 생성자 함수를 호출하면 자바스크립트 엔진은 `아래와 같은 과정`을 거쳐 암묵적으로 인스턴스를 생성하고 인스턴스를 초기화한 뒤 암묵적으로 인스턴스를 반환한다. 68 | 69 | 1. 인스턴스 생성과 this 바인딩 (런타임 이전에 실행) 70 | - 암묵적으로 빈 객체(인스턴스)가 생성된다. 71 | - 인스턴스는 `this`에 바인딩된다. -> 생성자 함수 내부의 `this`가 생성자 함수가 생성할 인스턴스를 가리키는 이유 72 | 2. 인스턴스 초기화 73 | - 생성자 함수에 기술되어 있는 코드가 한 줄씩 실행되어 `this`에 바인딩되어 있는 인스턴스를 초기화한다. 74 | - 즉, 인스턴스에 프로퍼티나 메서드 추가, 생성자 함수가 인수로 전달받은 초기값을 인스턴스 프로퍼티에 할당하여 초기화하거나 고정값을 할당한다. 75 | 3. 인스턴스 반환 76 | - 생성자 함수 내부의 모든 처리가 끝나면 완성된 인스턴스가 바인딩된 `this`가 암묵적으로 반환된다. 77 | - 만약, `this`가 아닌 다른 객체를 명시적으로 반환하면 `this`가 아니라 return 문에 명시한 객체가 반환된다. 78 | - 하지만, 명시적으로 원시 값을 반환하면 원시 값은 무시되고 `this`가 반환된다. 79 | 80 |
81 | 82 | #### 17.2.4 내부 메서드 [[Call]]과 [[Construct]] 83 | 84 | --- 85 | 86 | - 함수는 객체이지만 일반 객체와는 다르다 -> 일반 객체는 호출할 수 없지만 함수는 호출 가능하다. 87 | - 따라서, 함수 객체는 일반 객체가 가지고 있는 내부 슬롯과 내부 메서드는 물론 함수로서 동작하기 위해 함수 객체만을 위한 [[Environment]], [[FormalParameters]]등의 `내부 슬롯`과 [[Call]]. [[Construct]] 같은 `내부 메서드`를 추가로 가지고 있다. 88 | - 함수가 일반 함수로서 호출되면 함수 객체의 내부 메서드 `[[Call]]`이 호출된다. 89 | - new 연산자와 함께 생성자 함수로서 호출되면 내부 메서드 `[[Construct]]`가 호출된다. 90 | - 모든 함수 객체는는 내부 메서드 `[[Call]]`를 가지고 있지만 모든 함수 객체가 `[[Construct]]`를 갖는 것은 아니다. (모든 함수 객체를 생성자 함수로서 호출할 수 있는 것은 아니라는 뜻) 91 | 92 |
93 | 94 | #### 17.2.5 constructor와 non-constructor의 구분 95 | 96 | --- 97 | 98 | - constructor(생성자 함수로서 호출 가능한 함수) - 함수 선언문, 함수 표현식, 클래스(클래스도 함수다) 99 | - non-constructor(생성자 함수로서 호출할 수 없는 함수) - 메서드(ES6 메서드 축약 표현), 화살표 함수 100 | 101 |
102 | 103 | #### 17.2.6 new 연산자 104 | 105 | --- 106 | 107 | - new 연산자와 함께 함수를 호출하면 해당 함수는 생성자 함수로 동작한다. 108 | 109 | ```javascript 110 | // 생성자 함수 111 | function Circle(radius) { 112 | this.radius = radius; 113 | this.getDiameter = function () { 114 | return 2 * this.radius; 115 | } 116 | } 117 | ``` 118 | 119 | - 위의 Circle 함수를 new 연산자와 함께 생성자 함수로서 호출하면 함수 내부의 `this`는 Circle 생성자 함수가 생성할 인스턴스를 가리킨다. 하지만 new 연산자없이 일반적인 함수로서 호출하면 `this`는 전역 객체 window를 가리킨다. 120 | 121 | 122 | 123 |
124 | 125 | 126 | 127 | #### 17.2.7 new.target 128 | 129 | ---- 130 | 131 | - 생성자 함수가 new 연산자 없이 호출되는 것을 방지하기 위해 ES6에서는 `new.target`을 지원한다. 132 | - `new.target`은 `this`와 유사해게 constructor인 모든 함수 내부에서 암묵적인 지역 변수와 같이 사용되며 메타 프로퍼티라고 부른다. 133 | - 함수 내부에서 `new.target`을 사용하면 new 연산자와 함께 생성자 함수로서 호출되었는지 확인할 수 있다. 134 | - new 연산자와 함께 생성자 함수로서 호출되면 함수 내부의 `new.target`은 함수 자신을 가리킨다. new 연산자 없이 일반 함수로서 호출된 함수 내부의 `new.target`은 undefined다. 135 | 136 | -------------------------------------------------------------------------------- /18장-함수와-일급-객체/README.md: -------------------------------------------------------------------------------- 1 | # 18장 함수와 일급 객체 2 | 3 | ### 18.1 일급 객체 4 | 5 | --- 6 | 7 | - 다음과 같은 조건을 만족하는 객체를 `일급 객체`라 한다. 8 | 9 | - 무명의 리터럴로 생성할 수 있다. 즉, 런타임에 생성이 가능하다. 10 | - 변수나 자료구조(객체, 배열등)에 저장할 수 있다. 11 | - 함수의 매개변수에 전달할 수 있다. 12 | - 함수의 반환값으로 사용할 수 있다. 13 | 14 | 15 | 16 | - 자바스크립트의 함수는 위의 조건을 모두 만족하므로 `일급 객체`이다. 17 | 18 | 19 | 20 | ### 18.2 함수 객체의 프로퍼티 21 | 22 | --- 23 | 24 | - 함수는 객체다. 따라서 함수도 프로퍼티를 가질 수 있다. 25 | - `arguments`, `caller`, `length`, `name`, `prototype` 프로퍼티는 모두 함수 객체의 데이터 프로퍼티이고 일반 객체에는 없는 함수 객체 고유의 프로퍼티다. 26 | 27 |
28 | 29 | #### 18.2.1 arguments 프로퍼티 30 | 31 | --- 32 | 33 | - 함수 객체의 `arguments` 프로퍼티 값은 `arguments` 객체다. `arguments` 객체는 함수 호출 시 전달된 인수(argument)들의 정보를 담고 있는 순회 가능한 유사 배열 객체이며, 함수 내부에서 지역 변수처럼 사용된다. 즉, 함수 외부에서는 참조할 수 없다. 34 | - 모든 인수는 암묵적으로 `arguments` 객체의 프로퍼티로 보관된다. 35 | - 인수를 프로퍼티 값으로 소유하며 프로퍼티 키는 인수의 순서를 나타낸다. 36 | 37 | - `arguments` 객체는 매개변수 개수를 확정할 수 없는 `가변 인자 함수`를 구현할 때 유용하다. 38 | 39 |
40 | 41 | #### 18.2.2 caller 프로퍼티 42 | 43 | --- 44 | 45 | - 함수 객체의 `caller` 프로퍼티는 함수 자신을 호출한 함수를 가리킨다. 46 | 47 |
48 | 49 | #### 18.2.3 length 프로퍼티 50 | 51 | --- 52 | 53 | - 함수 객체의 `length` 프로퍼티는 함수를 정의할 때 선언한 매개변수의 개수를 가리킨다. 54 | - 단, `arguments` 객체의 length 프로퍼티는 인자(argument)의 개수를 나타낸다. 55 | 56 |
57 | 58 | #### 18.2.4 name 프로퍼티 59 | 60 | --- 61 | 62 | - 함수 객체의 name 프로퍼티는 함수 이름을 나타낸다. 63 | 64 | ``` javascript 65 | // 기명 함수 표현식 66 | var namedFunc = function foo() {}; 67 | console.log(namedFunc.name); // foo 68 | 69 | //익명 함수 표현식 70 | var anonymousFunc = function() {}; 71 | // ES5: name 프로퍼티는 빈 문자열을 값으로 갖는다. 72 | // ES6: name 프로퍼티는 함수 객체를 가리키는 변수 이름을 값으로 갖는다. 73 | console.log(anonymousFunc.name); // anonymousFunc 74 | 75 | // 함수 선언문 76 | function bar() {} 77 | console.log(bar.name); //bar 78 | ``` 79 | 80 |
81 | 82 | #### 18.2.5 `__proto__` 접근자 프로퍼티 83 | 84 | --- 85 | 86 | - 모든 객체는 [[Prototype]]이라는 내부 슬롯을 갖는다. [[Prototype]] 내부 슬롯은 객체지향 프로그래밍의 상속을 구현하는 프로토타입 객체를 가리킨다. 87 | - `__proto__` 프로퍼티는 [[Prototpye]] 내부 슬롯이 가리키는 프로토타입 객체에 접근하기 위해 사용하는 접근자 프로퍼티다. (내부슬롯에는 직접 접근할 수 없기 때문) 88 | 89 |
90 | 91 | #### 18.2.6 prototype 프로퍼티 92 | 93 | --- 94 | 95 | - prototype 프로퍼티는 생성자 함수로 호출할 수 있는 함수 객체, 즉 constructor만이 소유하는 프로퍼티다. 96 | - 함수가 객체를 생성하는 생성자 함수로 호출될 때 생성자 함수가 생성할 인스턴스의 프로토타입 객체를 가리킨다. 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /19장-프로토타입(19.8~)/img/README/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ritajeong/javascript-study/2d7cbc387fb4bff05963e8bbe93b601a945f8cbe/19장-프로토타입(19.8~)/img/README/1.jpg -------------------------------------------------------------------------------- /19장-프로토타입(19.8~)/img/README/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ritajeong/javascript-study/2d7cbc387fb4bff05963e8bbe93b601a945f8cbe/19장-프로토타입(19.8~)/img/README/2.jpg -------------------------------------------------------------------------------- /19장-프로토타입(19.8~)/img/README/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ritajeong/javascript-study/2d7cbc387fb4bff05963e8bbe93b601a945f8cbe/19장-프로토타입(19.8~)/img/README/3.jpg -------------------------------------------------------------------------------- /19장-프로토타입(19.8~)/img/README/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ritajeong/javascript-study/2d7cbc387fb4bff05963e8bbe93b601a945f8cbe/19장-프로토타입(19.8~)/img/README/4.jpg -------------------------------------------------------------------------------- /20장-strict-mode/README.md: -------------------------------------------------------------------------------- 1 | # 20장 strict mode 2 | 3 | ### 20.1 strict mode란? 4 | 5 | - 오타나 문법 지식의 미비로 인한 실수는 언제나 발생한다 -> 따라서, 오류를 줄여 안정적인 코드를 생산하기 위해서는 좀 더 `근본적인 접근`이 필요하다. 6 | - 다시 말해, 잠재적인 오류를 발생시키기 어려운 개발 환경을 만들고 그 환경에서 개발하는 것이 좀 더 근본적인 해결책이라고 할 수 있다. 7 | - 이를 지원하기 위해 ES5부터 `strict mode`가 추가된 것!! 8 | - strict mode는 자바스크립트 언어의 문법을 좀 더 엄격히 적용하여 오류를 발생시킬 가능성이 높거나 자바스크립트 엔진의 최적화 작업에 문제를 일으킬 수 있는 코드에 대해 명시적인 에러를 발생시킨다. 9 | - ESLint 같은 린트 도구를 사용해도 strict mode와 유사한 효과를 얻을 수 있다. 10 | 11 |
12 | 13 | ### 20.2 strict mode의 적용 14 | 15 | --- 16 | 17 | - strict mode를 적용하려면 전역의 선두 또는 함수 몸체의 선두에 `use strict`를 추가한다. 전역의 선두에 추가하면 스크립트 전체에 strict mode가 적용된다. 18 | 19 | ```javascript 20 | 'use strict'; 21 | 22 | function foo() { 23 | x = 10; //ReferenceError: x is not defined 24 | } 25 | foo(); 26 | ``` 27 | 28 |
29 | 30 | ### 20.3 전역에 strict mode를 적용하는 것은 피하자 31 | 32 | --- 33 | 34 | - 전역에 적용한 strict mode는 스크립트 단위로 적용된다. 35 | - strict mode 스크립트와 non-script mode 스크립트를 혼용하는 것은 오류를 발생시킬 수 있다. 특히 외부 서드파티 라이브러리를 사용하는 경우 라이브러리가 non-strict mode인 경우도 있기 때문에 전역에 strict mode를 적용하는 것은 바람직하지 않다. 36 | - 이러한 경우 즉시 실행 함수로 스크립트 전체를 감싸서 스코프를 구분하고 즉시 실행 함수의 선두에 strict mode를 적용한다. 37 | 38 |
39 | 40 | ### 20.4 함수 단위로 strict mode를 적용하는 것도 피하자 41 | 42 | --- 43 | 44 | - 함수 단위로도 strict mode를 적용할 수 있다. 45 | - 하지만, 함수마다 strict mode를 적용하는 여부를 다르게 하는 것은 바람직하지 않으며 모든 함수에 일일이 적용하는 것은 번거로운 일이다. 46 | - 따라서 strict mode는 즉시 실행 함수로 감싼 스크립트 단위로 적용하는 것이 바람직하다. 47 | 48 |
49 | 50 | ### 20.5 strict mode가 발생시키는 에러 51 | 52 | #### 20.5.1 암묵적 전역 53 | 54 | --- 55 | 56 | - 선언하지 않은 변수를 참조하면 `ReferenceError`가 발생한다. 57 | 58 | ```javascript 59 | (function () { 60 | 'use strict'; 61 | 62 | x = 1; 63 | console.log(x); // ReferenceError: x is not defined 64 | }) 65 | ``` 66 | 67 |
68 | 69 | #### 20.5.2 변수, 함수, 매개변수의 삭제 70 | 71 | --- 72 | 73 | - delete 연산자로 변수, 함수, 매개변수를 삭제하면 `SyntaxError`가 발생한다. 74 | 75 | ```javascript 76 | (function () { 77 | 'use strict'; 78 | 79 | var x = 1; 80 | delete x; // SyntaxError: Delete of an unqualified identifier in strict mode. 81 | 82 | function foo(a) { 83 | delete a; // SyntaxError: Delete of an unqualified identifier in strict mode. 84 | } 85 | delete foo; // SyntaxError: Delete of an unqualified identifier in strict mode. 86 | }()) 87 | ``` 88 | 89 | 90 | 91 |
92 | 93 | #### 20.5.3 매개변수 이름의 중복 94 | 95 | --- 96 | 97 | - 중복된 매개변수 이름을 사용하면 `SyntaxError`가 발생한다. 98 | 99 | ```javascript 100 | (function () { 101 | 'use strict'; 102 | 103 | //SyntaxError: Duplicate parameter name not allowed in this context 104 | function foo(x, x) { 105 | return x + x; 106 | } 107 | console.log(foo(1, 2)); 108 | }()); 109 | ``` 110 | 111 |
112 | 113 | #### 20.5.4 with 문의 사용 114 | 115 | --- 116 | 117 | - with 문을 사용하면 `SyntaxError`가 발생한다. with 문은 전달된 객체를 스코프 체인에 추가한다. 118 | - with문은 동일한 객체의 프로퍼티를 반복해서 사용할 때 객체 이름을 생략할 수 있어서 코드가 간단해지는 효과가 있지만 성능과 가독성이 나빠지는 문제가 있다. 119 | 120 |
121 | 122 | ### 20.6 strict mode 적용에 의한 변화 123 | 124 | #### 20.6.1 일반 함수의 this 125 | 126 | --- 127 | 128 | - strict mode에서 함수를 일반 함수로서 호출하면 this에 undefined가 바인딩된다. 생성자 함수가 아닌 일반 함수 내부에서는 this를 사용할 필요가 없기 때문이다. 129 | 130 |
131 | 132 | #### 20.6.2 arguments 객체 133 | 134 | ---- 135 | 136 | - strict mode에서는 매개변수에 전달된 인수를 재할당하여 변경해도 arguments 객체에 반영되지 않는다. 137 | 138 | 139 | 140 | -------------------------------------------------------------------------------- /21장-빌트인-객체/README.md: -------------------------------------------------------------------------------- 1 | # 21장. 빌트인 객체 2 | 3 | ### 21.1 자바스크립트 객체의 분류 4 | 5 | --- 6 | 7 | - 표준 빌트인 객체 8 | 9 | - ECMAScript 사양에 정의된 객체를 말하며, 애플리케이션 전역의 공통기능을 제공한다. 10 | - 자바스크립트 실행 환경(브라우저 또는 Node.js 환경)과 관계없이 언제나 사용할 수 있다. 11 | - 표준 빌트인 객체는 전역 객체의 프로퍼티로서 제공된다. -> 따라서 별도의 선언 없이 전역 변수처럼 언제나 참조 가능 12 | 13 | - 호스트 객체 14 | 15 | - ECMAScript 사양에 정의되어 있지 않지만 자바스크립트 실행 환경(브라우저 or Node .js)에서 추가로 제공하는 객체 16 | 17 | - 브라우저 환경에서는 DOM, BOM, Canvas, XMLHttpRequest, fetch등과 같은 클라이언트 사이드 Web API를 호스트 객체로 제공한다. 18 | - Node.js 환경에서는 Node.js 고유의 API를 호스트 객체로 제공한다. 19 | 20 | - 사용자 정의 객체 21 | 22 | - 표준 빌트인 객체와 호스트 객체처럼 기본 제공되는 객체가 아닌 사용자가 직접 정의한 객체 23 | 24 | 25 | 26 |
27 | 28 | ### 21.2 표준 빌트인 객체 29 | 30 | --- 31 | 32 | - 자바스크립트는 Object, String, Number, Boolean, Symbol, Date등 40여 개의 표준 빌트인 객체를 제공한다. 33 | - Math, Reflect, JSON을 제외한 표준 빌트인 객체는 모두 인스턴스를 생성할 수 있는 생성자 함수 객체이다. 34 | - 생성자 함수 객체인 표준 빌트인 객체는 `프로토타입 메서드`와 `정적 메서드`를 제공 35 | - 해당 객체가 생성한 인스턴스의 프로토타입은 표준 빌트인 객체의 `prototype 프로퍼티`에 바인딩된 객체이다. 36 | - 생성자 함수 객체가 아닌 표준 빌트인 객체는 `정적 메서드`만 제공 37 | 38 | ```javascript 39 | // Number 생성자 함수에 의한 Number 객체 생성 40 | const numObj = new Number(1.5) // Number {1.5} 41 | 42 | // toFixed는 Number.prototype의 프로토타입 메서드다. 43 | // Number.prototype.toFixed는 소수점 자리를 반올림하여 문자열로 반환한다. 44 | console.log(numObj.toFixed()); // 2 45 | 46 | // isInteger는 Number의 정적 메서드다. 47 | // Number.isInteger는 인수가 정수(integer)인지 검사하여 그 결과를 Boolean으로 반환한다. 48 | console.log(Number.isInteger(0.5)); // false 49 | ``` 50 | 51 |
52 | 53 | ### 21.3 원시값과 래퍼 객체 54 | 55 | --- 56 | 57 | **그렇다면, 표준 빌트인 생성자 함수가 존재하는 이유는 무엇일까?** 58 | 59 | - 원시값은 객체가 아니므로 프로퍼티나 메서드를 가질 수 없는데도 원시값인 문자열이 마치 객체처럼 동작한다. 60 | 61 | ```javascript 62 | const str = 'hello'; 63 | 64 | // 원시 타입인 문자열이 프로퍼티와 메서드를 갖고 있는 객체처럼 동작한다. 65 | console.log(str.length); //5 66 | console.log(str.toUpperCase()); // HELLO 67 | ``` 68 | 69 | - 이는, 원시값인 문자열, 숫자, 불리언 값의 경우 이들 원시값에 대해 마치 객체처럼 마침표 표기법(또는 대괄호 표기법)으로 접근하면 자바스크립트 엔진이 일시적으로 원시값을 연관된 객체로 변환해주기 때문이다. -> 즉, 원시값을 객체처럼 사용하면 자바스크립트 엔진은 암묵적으로 연관된 객체를 생성하여 생성된 객체로 프로퍼티에 접근하거나 메서드를 호출하고 다시 원시값으로 되돌린다. 70 | 71 | - 이처럼 문자열, 숫자, 불리언 값에 대해 객체처럼 접근하면 생성되는 임시 객체를 `래퍼 객체`라 한다. 72 | 73 | 74 | 75 | - **ex) 문자열에 대해 마침표 표기법으로 접근하면 발생하는 상황** 76 | 77 | - 접근하는 순간 래퍼 객체인 String 생성자 함수의 `인스턴스`가 생성되고 문자열은 래퍼 객체의 `[[StringData]] 내부 슬롯`에 할당된다. 78 | 79 | ```javascript 80 | const str = 'hi'; 81 | 82 | //원시 타입인 문자열이 래퍼객체인 String 인스턴스로 변환된다. 83 | console.log(str.length) // 2 84 | console.log(str.toUpperCase()); // HI 85 | 86 | // 래퍼 객체로 프로퍼티에 접근하거나 메서드를 호출한 후, 다시 원시값으로 되돌린다. 87 | console.log(typeof str); // string 88 | ``` 89 | 90 | - 이때 문자열 래퍼 객체인 String 생성자 함수의 `인스턴스`는 String.prototype의 메서드를 상속받아 사용할 수 있다. 91 | 92 | - 그 후 래퍼 객체의 처리가 종료되면 래퍼 객체의 `[[StringData]]` 내부 슬롯에 할당된 원시값으로 원래의 상태, 즉 식별자가 원시값을 갖도록 되돌리고 래퍼 객체는 가비지 컬렉션의 대상이 된다. 93 | 94 | ``` javascript 95 | // 1. 식별자 str은 문자열을 값으로 가지고 있다. 96 | const str = 'hello'; 97 | 98 | // 2. 식별자 str은 암묵적으로 생성된 래퍼 객체를 가리킨다. 99 | // 식별자 str의 값 'hello'는 래퍼 객체의 [[StringData]] 내부 슬롯에 할당된다. 100 | // 래퍼 객체에 name 프로퍼티가 동적 추가된다. 101 | str.name = 'song'; 102 | 103 | // 3. 식별자 str은 다시 원래의 문자열, 즉 래퍼 객체의 [[StringData]] 내부 슬롯에 할당된 원시값을 갖는다. 104 | // 이때 2번에서 생성된 래퍼 객체는 아무도 참조하지 않는 상태이므로 가비지 컬렉션의 대상이 된다. 105 | 106 | // 4. 식별자 str은 새롭게 암묵적으로 생성된(2번에서 생성된 래퍼 객체와는 다른) 래퍼 객체를 가리킨다. 107 | // 새롭게 생성된 래퍼 객체에는 name 프로퍼티가 존재하지 않는다. 108 | console.log(str.name); // undefined 109 | 110 | // 5. 식별자 str은 다시 원래의 문자열, 즉 래퍼 객체의 [[StringData]] 내부 슬롯에 할당된 원시값을 갖는다. 111 | // 이때, 4번에서 생성된 래퍼 객체는 아무도 참조하지 않는 상태이므로 가비지 컬렉션의 대상이 된다. 112 | console.log(typeof str, str) // string hello 113 | ``` 114 | 115 | - 문자열과 같이 숫자와 불리언도 동일하게 작동한다 116 | 117 |
118 | 119 | ### 21.4 전역 객체 120 | 121 | --- 122 | 123 | - 전역 객체는 코드가 실행되기 `이전 단계`에 자바스크립트 엔진에 의해 어떤 객체보다도 먼저 생성되는 특수한 객체이며, 어떤 객체에도 속하지 않은 최상위 객체다. 124 | 125 | - 전역 객체는 표준 빌트인 객체와 환경에 따른 호스트 객체, 그리고 var 키워드로 선언한 전역 변수와 전역 함수를 `프로퍼티`로 갖는다. 126 | 127 | - 즉, 전역 객체는 계층적 구조상 어떤 객체에도 속하지 않은 모든 빌트인 객체(표준 빌트인 객체와 호스트 객체)의 최상위 객체다. 128 | - **전역 객체가 최상위 객체라는 것은 프로토타입 상속 관계상에서 최상위 객체라는 의미가 아니다** 129 | - 전역 객체 자신은 어떤 객체의 프로퍼티도 아니며 객체의 계층적 구조상 표준 빌트인 객체와 호스트 객체를 프로퍼티로 소유한다는 것을 말한다. 130 | 131 | - 전역 객체는 개발자가 의도적으로 생성할 수 없다. 즉, 전역 객체를 생성할 수 있는 생성자 함수가 제공되지 않는다. 132 | 133 | - 전역 객체의 프로퍼티를 참조할 때 window(또는 global)를 생략할 수 있다. 134 | 135 | - var 키워드로 선언한 전역 변수와 선언하지 않은 변수에 값을 할당한 `암묵적 전역`, 그리고 전역 함수는 전역 객체의 프로퍼티가 된다. 136 | 137 | ```javascript 138 | // 선언하지 않은 변수에 값을 암묵적 전역. bar는 전역 변수가 아니라 전역 객체의 프로퍼티다. 139 | bar = 2; // window.bar = 2 140 | console.log(window.bar) // 2 141 | ``` 142 | 143 | - let이나 const 키워드로 선언한 전역 변수는 전역 객체의 프로퍼티가 아니다. 보이지 않는 개념적인 블록(전역 렉시컬 환경의 선언적 환경 레코드)내에 존재하기 때문이다. 144 | 145 | - 브라우저 환경의 모든 자바스크립트 코드는 하나의 전역 객체 window를 공유한다. 여러 개의 script 태그를 통해 자바스크립트 코드를 분리해도 하나의 전역 객체 `window`를 공유하는 것은 변함이 없다. 146 | 147 |
148 | 149 | #### 21.4.1 빌트인 전역 프로퍼티 150 | 151 | --- 152 | 153 | - 빌트인 전역 프로퍼티는 전역 객체의 프로퍼티를 의미한다. 154 | 155 | - 빌트인 전역 프로퍼티 종류 156 | - **Inifinity** 157 | - 무한대를 나타내는 숫자값 Infinity를 갖는다. 158 | - **NaN** 159 | - 숫자가 아님을 나타내는 숫자값 NaN을 갖는다. 160 | - **undefined** 161 | - 원시 타입 undefined를 값으로 갖는다. 162 | 163 |
164 | 165 | #### 21.4.2 빌트인 전역 함수 166 | 167 | --- 168 | 169 | - 애플리케이션 전역에서 호출할 수 있는 빌트인 함수로서 전역 객체의 메서드이다. 170 | 171 | - 빌트인 전역 함수 종류 172 | 173 | - **eval** 174 | 175 | - 자바스크립트 코드를 나타내는 문자열을 인수로 전달받는다. 176 | - 전달받은 문자열 코드가 표현식이라면 eval 함수는 문자열 코드를 런타임에 평가하여 값을 생성한다. 177 | - 전달받은 인수가 표현식이 아닌 문이라면 eval 함수는 문자열 코드를 런타임에 실행한다. 178 | 179 | ```javascript 180 | // 표현식인 문 181 | eval('1 + 2;'); // 3 182 | 183 | // 표현식이 아닌 문 184 | eval('var x = 5;'); // undefined 185 | 186 | // eval 함수에 의해 런타임에 변수 선언문이 실행되어 x 변수가 선언되었다. 187 | console.log(x); // 5 188 | 189 | // 객체 리터럴은 반드시 괄호로 둘러싼다. 190 | const o = oval('({ a: 1 })'); 191 | console.log(o) // {a: 1} 192 | 193 | // 함수 리터럴은 반드시 괄호로 둘러싼다. 194 | const f = eval('(function() { return 1;})'); 195 | console.log(f()); // 1 196 | 197 | ``` 198 | 199 | - eval 함수는 자신이 호출된 위치에 해다하는 기존의 스코프를 런타임에 동적으로 수정한다. 200 | - 함수는 호출되면 런타임 이전에 먼저 함수 몸체 내부의 모든 선언문을 먼저 실행하고 그 결과를 스코프에 등록한다. 201 | - 하지만 해당 함수 내부에 eval 함수는 기존의 스코프를 런타임에 동적으로 수정한다. 202 | - **eval 함수의 사용을 금지해야하는 이유** 203 | - eval 함수를 통해 사용자로부터 입력받은 콘텐츠를 실행하는 것은 보안에 매우 취약하기 때문에 204 | - eval 함수를 통해 실행되는 코드는 자바스크립트 엔진에 의해 최적화가 수행되지 않으므로 일반적인 코드 실행에 비해 처리 속도가 느리기 때문에 205 | 206 | - **isFinite** 207 | 208 | - 전달받은 인수가 정상적인 유한수인지 검사하여 유한수이면 true를 반환하고, 무한수이면 false를 반환하다. 209 | 210 | - **isNaN** 211 | 212 | - 전달받은 인수가 NaN인지 검사하여 그 결과를 불리언 타입으로 반환한다. 213 | 214 | - **parseFloat** 215 | 216 | - 전달받은 문자열 인수를 부동 소수점 숫자, 즉 실수로 해석하여 반환한다. 217 | 218 | - **parseInt** 219 | 220 | - 전달받은 문자열 인수를 정수로 해석하여 반환한다. 221 | 222 | - 두 번째 인수로 진법을 나타내는 기수를 전달할 수 있다. 기수를 지정하면 첫 번째 인수로 전달된 문자열을 해당 기수의 숫자로 해석하여 반환한다. 이때 반환값은 언제나 10진수다. 223 | 224 | ```javascript 225 | // '10'을 10진수로 해석하고 그 결과를 10진수 정수로 반환한다. 226 | parseInt('10'); // 10 227 | // '10'을 2진수로 해석하고 그 결과를 10진수 정수로 반환한다. 228 | parseInt('10') // 2 229 | ``` 230 | 231 | - **encodeURI** 232 | 233 | - 완전한 URI를 문자열로 전달받아 이스케이프 처리를 위해 인코딩한다. 234 | 235 | ```javascript 236 | // 완전한 URI 237 | const uri = 'http://naver.com?name=가나다&job=programmer&teacher'; 238 | 239 | // encodeURI 함수는 완전한 URI를 전달받아 이스케이프 처리를 위해 인코딩한다. 240 | const enc = encodeURI(uri); 241 | console.log(enc); 242 | // http://naver.com?name=%EG%23%D3%E3%FE%U5%O5%A8&job=programmer&teacher 243 | ``` 244 | 245 | - **decodeURI** 246 | 247 | - 인코딩된 URI를 인수로 전달받아 이스케이프 처리 이전으로 디코딩 한다. 248 | 249 | ```javascript 250 | // http://naver.com?name=%EG%23%D3%E3%FE%U5%O5%A8&job=programmer&teacher 251 | const enc = encodeURI(uri) 252 | 253 | const dec = decodeURI(enc); 254 | console.log(dec) 255 | // http://naver.com?name=가나다&job=programmer&teacher 256 | ``` 257 | 258 | - **encodeURIComponent** 259 | 260 | - URI 구성 요소를 인수로 전달받아 인코딩한다. 261 | - 인수로 전달된 문자열을 URI의 구성요소인 쿼리 스트링의 일부로 간주한다. 따라서 쿼리 스트링 구분자로 사용되는 =, ?, &까지 인코딩한다. 262 | 263 |
264 | 265 | #### 21.4.3 암묵적 전역 266 | 267 | --- 268 | 269 | ```javascript 270 | var x = 10; // 전역 변수 271 | 272 | function foo() { 273 | // 선언하지 않은 식별자에 값을 할당 274 | y = 20; // window.y = 20; 275 | } 276 | foo(); 277 | 278 | // 선언하지 않은 식별자 y를 전역에서 참조할 수 있다. 279 | console.log(x + y); // 30 280 | ``` 281 | 282 | - 함수 foo 내부의 y처럼 선언하지 않은 식별자에 값을 할당하면 전역 객체의 프로퍼티가 된다. 283 | - foo 함수가 호출되면 자바스크립트 엔진은 y 변수에 값을 할당하기 위해 먼저 `스코프 체인`을 통해 선언된 변수인지 확인한다. 이때 foo 함수의 스코프와 전역 스코프 어디에서도 y 변수의 선언을 찾을 수 없으므로 `참조 에러`가 발생한다. -> 하지만 자바스크립트 엔진은 y = 20을 `window.y = 20`으로 해석하여 전역 객체에 프로퍼티를 동적 생성한다. 이러한 현상을 **암묵적 전역**이라 한다. 284 | - y는 변수 선언 없이 단지 전역 객체의 프로퍼티로 추가되었을 뿐이다. 285 | 286 | -------------------------------------------------------------------------------- /22장-this/README.md: -------------------------------------------------------------------------------- 1 | # 22장 this 2 | 3 | [참고영상](https://youtu.be/7RiMu2DQrb4) 4 | 5 | ## 22.1 this키워드 6 | 7 |
8 | 9 | - 자바스크립트의 일반적인 함수들은 this라는 변수를 가지고 있다. 10 | - this는 해당 함수를 메서드로 가지고 있는 객체 또는 해당 함수가 생성할 객체를 참조하는 변수이다. 11 | - this는 함수 내에서 쓰일 때 의미가 있지만 전역에서도 참조할 수는 있다. 12 | - this를 전역에서 참조할 경우 다음과 같이 동작한다. 13 | - 브라우저에서 동작할 경우 this는 전역 객체인 window를 참조한다. 14 | - node REPL에서 this는 전역 객체인 global을 참조한다. 15 | - commonJS의 모듈 시스템을 따르는 node 환경에서는 module.exports를 의미한다. 16 | - 브라우저 환경일 때, strict mode라면 undefined가 할당된다. 17 | 18 |
19 | 20 | ## 22.2 함수 호출 방식과 this 바인딩 21 | 22 |
23 | 24 | 자바스크립트에서 this 바인딩은 함수 호출 방식에 따라 동적으로 결정된다. 25 | 26 |
27 | 28 | 1. 일반함수 호출 29 | 30 | - 일반함수 호출이란 함수를 참조하고 있는 식별자에 괄호 연산자를 붙여 호출하는 방식을 의미한다. 31 | ```js 32 | // 다음의 경우 모두 일반함수 호출에 해당한다. 33 | function fn1() {} 34 | fn1(); 35 | 36 | const fn2 = function() {}; 37 | fn2(); 38 | 39 | const obj = { method: function() {} }; 40 | const fn3 = obj.method; 41 | fn3(); 42 | 43 | // 특히 fn3의 경우 처럼 객체의 메서드를 다른 식별자에 할당만 했음에도 44 | // 단지 호출 방식에 따라 일반함수 호출로 분류된다. 45 | ``` 46 | - 일반 함수 호출 시 기본적으로 this에 전역 객체가 바인딩 된다. 47 | - 브라우저 환경에서는 window, node 환경에서는 global이 바인딩된다. 48 | - 단, strict mode일 경우에는 undefined가 할당된다. 49 | - node REPL에서는 그대로 전역객체가 바인딩된다. 50 | 51 | 2. 메서드 호출 52 | - 점 연산자를 통해 객체의 메서드로써 호출되는 함수의 내부에서 this는 점 연산자 앞에 있는 객체에 바인딩된다. 53 | - 이를 암묵적 바인딩이라고 부른다. 54 | 55 | 3. 생성자 함수 호출 56 | - new 연산자와 함께 호출되는 경우 함수는 생성자로써 호출된다. 57 | - 생성자 함수 내부에서 this는 생성할 객체를 의미한다. 58 | - 생정자 함수의 동작을 간단히 나타내면 다음과 같다. 59 | ```js 60 | function Func() { 61 | this.a = 1; 62 | } 63 | 64 | // 위 함수가 생성자 함수로 호출된 경우 다음과 같은 일들이 암묵적으로 일어난다. 65 | 66 | function Func() { 67 | // 생성될 객체에 this를 바인딩 68 | this = {}; 69 | 70 | // prototype 연결 71 | 72 | // 함수 실행 73 | this.a = 1; 74 | 75 | // this를 반환 76 | return this; 77 | } 78 | ``` 79 | 80 | 4. Function 객체의 메서드에 의한 호출 81 | - Function 객체는 this를 명시적으로 바인딩할 수 있는 메서드들을 제공한다. 82 | - apply, call 83 | - apply와 call 메서드는 this를 특정 객체에 바인딩 한 채로 함수를 호출한다. 84 | - 첫번째 인자에 this가 가리킬 객체를 넣고 두번째부터는 함수에게 전달될 인수를 넣는다. 85 | - apply와 call은 함수의 인수를 전달하는 방식에서만 차이를 갖는다. 86 | ```js 87 | function testBinding(...args) { 88 | console.log(this, args); 89 | } 90 | 91 | const obj = { a: 1 }; 92 | 93 | console.log(getThis.apply(obj, [1, 2, 3])); // { a: 1 }, [1,2,3] 94 | console.log(getThis.call(obj, 1, 2, 3); // { a: 1 }, [1,2,3] 95 | 96 | // apply는 배열 혹은 유사 배열로 인수를 전달한다. 97 | // call은 쉼표로 구분해 인수를 각각 전달한다. 98 | ``` 99 | - bind 100 | - bind 메서드는 this바인딩이 '고정' 되어있는 함수를 호출한다. 101 | - bind에 의해 생성된 함수는 일반 함수 호출 시에도 고정된 this바인딩을 보장 받는다. 102 | - bind 메서드에 전달한 두번째 이후 인수들은 고정된 인수도 여겨진다. 103 | ```js 104 | function testBinding(...args) { 105 | console.log(this, args); 106 | } 107 | 108 | const obj = { a: 1 }; 109 | const boundObj = testBinding.bind(obj, 1, 2); 110 | 111 | boundObj(3); // { a: 1 } [ 1, 2, 3 ] 112 | ``` 113 | 114 |
115 | 116 | > bind 메서드의 리턴값
117 | > bind 메서드가 리턴하는 객체를 두고 편의상 함수라 표현하긴 했지만 사실 단순히 '함수'라고 이야기할 수는 없다. 자바스크립트 명세상 bind메서드가 반환하는 객체는 bound function exotic object라고 하는 특수 객체이다. 이 객체는 일반 함수의 프로퍼티와 내부 슬롯에 더해 실제 호출할 함수를 의미하는 [[BoundTargetFunction]], 고정된 this와 인수를 의미하는 [[BoundThis]], [[BoundArguments]]를 가지고 있다. 함수의 프로퍼티와 내부 슬롯을 모두 가지고 있는데 왜 함수라고 불릴 수는 없을까? 이 특수 객체는 [[Call]] 메서드를 구현하고 있기 때문에 callable이긴하나 호출 방식에 있어 일반 함수 다르다. [[Call]] 메서드 호출 시 실제로 실행되는 코드는 [[BoundTargetFunction]]의 내용이며 이때 [[BoundThis]]와 [[BoundArguments]]가 사용된다. 자세한 내용은 [여기](https://tc39.es/ecma262/#sec-bound-function-exotic-objects)를 참고하자 118 | 119 |
120 | 121 | ## 추가 내용1: this 바인딩 우선순위 122 | 123 |
124 | 125 | 살펴본 바와 같이 자바스크립트에서 this는 각 상황에 맞게 다양한 방식으로 바인딩된다. 그리고 이러한 바인딩 방식에는 우선순위가 존재한다. this의 바인딩 우선 순위는 다음과 같다. 126 | 127 | `new 연산자에 의한 바인딩 > 명시적 바인딩 > 암시적 바인딩 > 일반함수 호출에 의한 바인딩` 128 | 129 | 그리고 이때 bind메서드에 의한 바인딩이 apply, call에 의한 바인딩 보다 우선순위가 높다. 조금 더 정확하게 말하면 apply, call, bind 실행시 이를 호출한 함수가 화살표 함수이거나 bound function exotic object이면 첫번째인자인 thisArg가 무시된다. 130 | 131 |
132 | 133 | ## 추가 내용2: 화살표 함수와 this 134 | 135 |
136 | 137 | 화살표함수는 this 키워드에 해당하는 값을 자기 스코프 내에 정의하지 않는다. 따라서 화살표 함수 내에서 this를 참조하게 되면 스코프 체인상 상위 스코프를 탐색한다. 138 | 139 |
140 | 141 | ## 추가 내용3: 콜백과 this 142 | 143 |
144 | 145 | 함수가 다른 함수의 인수, 그리나까 콜백으로 전달되면 그 함수는 일반 함수로써 호출되리라 기대된다. 그러나 이는 그 함수를 사용하는 함수의 정책에 의존한다. 자바스크립트의 몇몇 내장 메서드들은 콜백함수의 명시적 this 바인딩에 대한 정책을 가지고 있기도 하다. 예를 들어 addEventListener 메서드의 경우 이벤트 핸들러 인자에 해당하는 콜백의 this로 `currentTarget`을 바인딩하며 배열의 내장 메서드들의 경우 두번째 인자를 통해 콜백의 this에 바인딩할 객체를 받기도 한다. 146 | 147 |
-------------------------------------------------------------------------------- /23장-실행-컨텍스트/README.md: -------------------------------------------------------------------------------- 1 | # 23장 실행 컨텍스트 2 | 3 | ## 23.1 소스코드의 타입 4 | 5 |
6 | 7 | ECMAScript 사양은 실행 컨텍스트를 생성하는 소스 코드를 총 4가지 타입으로 구분한다. 다음의 소스코드 타입에 따라 실행 컨텍스트를 생성하는 과정과 관리 내용이 다르다. 8 | 9 | 1. 전역코드: 전역에 존재하는 소스코드 10 | 2. 함수코드: 함수 내부에 존재하는 소스코드 11 | 3. eval 코드: eval 함수의 인수로 전달되어 실행되는 소스코드 12 | 4. 모듈코드: 모듈 내부에 존재하는 소스코드 13 | 14 |
15 | 16 | ## 23.3 소스코드의 평가와 실행 17 | 18 |
19 | 20 | 자바스크립트 엔진은 소스코드를 `평가`와 `실행`이라는 두 개의 과정으로 나누어 처리한다. 21 | 22 | 1. 소스코드 평가 과정 23 | - 실행 컨텍스트가 생성된다. 24 | - 변수, 함수 등 각 식별자에 대한 선언문들이 실행된다. 25 | - 선언문이 실행되면 해당하는 렉시컬 환경 내 환경 레코드에 등록된다. 26 | - var 키워드로 선언한 식별자에 해당하는 값은 undefined로 초기화 되며 함수 선언문으로 선언한 식별자의 값은 실제 함수 몸체를 담고 있는 함수 객체로 초기화 된다. 27 | - let, const, class 키워드로 선언한 식별자에 대해서는 초기화를 하지 않는다. 28 | 29 | 2. 소스코드 실행 과정 30 | - 평가 과정이 끝나면 이미 실행된 코드(선언문 등)를 제외한 소스코드가 순차적으로 실행된다. 31 | - 실행 과정에서 특정 식별자의 값을 참조할 경우 스코프에서 검색한다. 32 | 33 | 34 |
35 | 36 | ## 23.3 실행 컨텍스트의 역할 37 | 38 |
39 | 40 | 실행 컨텍스트는 코드를 실행하는 환경을 제공하고 관리한다. 소스코드는 여러 타입으로 나뉘긴하지만 본질적으로 명령과 상태의 집합이다. 실행 컨텍스트는 명령과 상태를 관리할 수 있는 방법을 제공한다. 실행 컨텍스트는 렉시컬 환경과 외부 렉시컬 환경 참조를 통해 스코프와 스코프 체인을 형성한다. 또한 실행 컨텍스트는 콜 스택에 의해 실행 순서를 보장받는다. 이에 대해 자세한 내용은 뒷내용을 통해 살펴보도록 한다. 41 | 42 |
43 | 44 | ## 23.4 실행 컨텍스트 스택 45 | 46 |
47 | 48 | 코드 평가를 통해 생성한 실행 컨텍스트는 스택 자료구조로 관리된다. 이를 콜스택(또는 실행 컨텍스트 스택)이라고 부른다. 콜스택은 각 코드의 실행 순서를 관리한다. 가령 코드를 실행하는 중에 함수 호출부를 만나면 기존 실행을 멈추고 함수를 실행한다. 이때, 함수의 실행이 끝나면 다시 원래 코드로 돌아가서 순차적으로 실행해야한다. 콜 스택에는 실행 컨텍스트가 생성되는 순서대로 담기고 항상 최상단에 있는 컨텍스트가 실행된다. 컨텍스트의 생명주기가 끝나면 바로 스택에서 제거되는데 이때, 그 컨텍스트를 생성했던 실행 컨텍스트가 최상단에 위치하게 되므로 바로 직전의 코드로 돌아갈 수 있다. 49 | 50 |
51 | 52 | ## 23.5 렉시컬 환경 53 | 54 |
55 | 56 | 렉시컬 환경은 실행 컨텍스트의 구성 요소이다. 렉시컬 환경은 환경 레코드와 외부 렉시컬 환경 참조로 구성되어 있다. 환경 레코드에는 식별자와 그 식별자에 바인딩 된 값이 키, 값 형태로 등록, 관리된다. this에 대한 정보도 바로 이 환경레코드에 저장된다. 외부 렉시컬 환경 참조에는 상위 스코프에 대한 참조가 담긴다. 여기서 말하는 상위 스코프란 해당 실행 컨텍스트를 생성한 코드를 포함하는 코드의 렉시컬 환경을 의미한다. 이때 외부 렉시컬 환경 참조를 통해 전역 실행 컨텍스트까지 이어지는 단방향 링크드 리스트가 형성되는데 이를 스코프 체인이라 부른다. 57 | 58 |
59 | 60 | ## 23.6 실행 컨텍스트의 생성과 식별자 검색 과정 61 | 62 |
63 | 64 | 자바스크립트가 실행 컨텍스트를 생성해 코드를 실행하고 식별자를 검색하는 과정은 다음과 같다. 65 | 66 | 1. 전역 객체 생성 67 | - 전역객체는 전역 코드가 평가되기 이전에 생성된다. 68 | - 전역 객체는 동작 환경에 따라 다르게 생성되며 각각 다른 호스트 객체를 포함한다. 69 | 70 | 2. 전역 코드 평가 71 | 1. 전역 실행 컨텍스트 생성 72 | - 전역 실행 컨텍스트를 생성해 콜스택에 넣는다. 73 | 2. 전역 렉시컬 환경 생성 74 | - 전역 렉시컬 환경을 생성하고 전역 실행 컨텍스트에 연결한다.(전역 실행 컨텍스트 안에 있는 렉시컬 환경 컴포넌트가 이를 참조한다.) 75 | - 전역 환경 레코드를 생성한다. 전역 환경 레코드는 객체 환경 레코드와 선언적 환경 레코드 그리고 this 바인딩 정보가 저장되는 내부슬롯(GlobalThisValue)로 구성된다. 76 | - 객체 환경 레코드는 var와 함수 선언문으로 선언한 식별자와 빌트인 전역 프로퍼티, 빌트인 전역 함수, 표준 빌트인 객체를 관리한다. 77 | - 객체 환경 레코드 안에는 BindingObject라고 하는 요소가 존재한다. 78 | - BindingObject는 전역 객체에 대한 참조를 담고 있다. 79 | - var, 함수 선언문을 통해 선언된 식별자는 BindingObject를 통해 전역 객체의 프로퍼티 및 메서드가 된다. 80 | - 전역 변수를 식별자 그 자체로써 뿐만아니라 전역 객체의 프로퍼티로 참조할 수 있는 이유가 바로 여기에 있다. 예를 들어 var 키워드로 전역 변수 `a`를 선언했다고 가정하자. 변수 `a`는 전역객체 `window`의 프로퍼티 이므로 `window.a`와 같은 방식으로 참조할 수 있으며 동시에 `a`와 같은 방식으로 참조할 수 있다. 후자의 경우 객체 환경 레코드에 있는 BindingObject를 탐색한다. 81 | - 선언적 환경 레코드는 let, const 키워드로 선언한 전역 변수를 관리한다. 82 | - 선언적 환경 레코드는 개념적인 블록의 역할을 수행한다. 83 | - GlobalThisValue가 바인딩 된다. 일반적으로 전역 객체가 바인딩 되며 전역 코드상에서 this를 참조하면 전역 환경 레코드의 GlobalThisValue가 참조하는 객체를 반환한다. 84 | 3. 외부 렉시컬 환경 참조 결정 85 | - 외부 렉시컬 환경은 렉시컬 환경의 구성 요소이며 상위 코드(해당 실행 컨텍스트를 생성한 코드를 포함하는 코드)의 렉시컬 환경을 참조를 담고 있다. 86 | - 전역 렉시컬 환경의 외부 렉시컬 환경 참조에는 null이 할당 된다. 87 | 3. 전역 코드 실행 88 | - 전역 코드를 순차적으로 실행한다. 89 | - 식별자에 대한 참조가 있을 경우 렉시컬 환경 내에서 검색한다. 90 | - 실행 중 함수 호출부를 만나면 실행을 멈추고 함수의 코드를 평가 및 실행한다. 91 | 4. 함수 코드 평가 92 | 1. 함수 실행 컨텍스트 생성 93 | 2. 함수 렉시컬 환경 생성 94 | - 함수 환경 레코드를 생성한다. 95 | - 함수 환경 레코드는 매개변수, arguments 객체, 함수 내 식별자를 등록, 관리한다. 96 | - ThisValue가 바인딩 된다. ThisValue는 함수 내에서 this 참조 시 반환하는 객체를 담고 있는 내부 슬롯이다. 97 | - ThisValue의 값은 함수 호출 방식에 따라 결정된다.(22장 this 관련 내용 참고) 98 | 3. 외부 렉시컬 환경 참조 결정 99 | - 자바스크립트 엔진이 함수 객체를 생성할 때 생성 당시 실행 중인 컨텍스특의 렉시컬 환경을 내부 슬롯 \[\[Environment\]\]에 저장한다. 100 | - 함수 렉시컬 황경의 외부 렉시컬 환경 참조에 \[\[Environment\]\]가 할당 된다. 101 | - 함수 객체에는 \[\[Scope\]\]라는 내부 슬롯도 있는데 이것과 무슨 차이인지 공부해볼 필요가 있음. 102 | 5. 함수 코드 실행 103 | - 식별자에 대한 참조가 있을 경우 렉시컬 환경 내에서 검색한다. 104 | - 렉시컬 환경에 식별자 정보가 없다면 외부 렉시컬 환경 참조를 따라 상위 렉시컬 환경을 탐색한다. 105 | - 이렇게 스코프 체인을 따라 전역 렉시컬 환경까지 탐색한다. 106 | 6. 코드 실행 종료 107 | - 함수 또는 전역 코드의 실행이 종료되면 해당하는 실행 컨텍스트가 콜스택에서 제거 된다. 108 | 109 |
110 | 111 | ## 23.7 실행 컨텍스트와 블록 레벨 스코프 112 | 113 |
114 | 115 | es6에서 추가된 let, const 키워드의 경우 블록 레벨 스코프를 지원한다. 그렇다면 실행 컨텍스트 내에서 어떻게 블록 레벨 스코프를 지원할 수 있을까? 코드 실행 중 블록문({})을 만나면 선언적 환경 레코드를 갖는 렉시컬 환경(블록 렉시컬 환경)을 새롭게 생성하고 실행 컨텍스트에 있는 렉시컬 환경 컴포넌트가 이를 참조하도록 한다. 이때 새롭게 생성된 블록 렉시컬 환경은 기존 렉시컬 환경을 참조하는 외부 렉시컬 환경 참조를 갖는다. 코드 블록의 실행이 종료되면 블록 렉시컬 환경이 가지고 있는 외부 렉시컬 환경 참조를 이용해 실행 컨텍스트의 렉시컬 환경 컴포넌트가 기존 렉시컬 환경을 다시 참조하도록 원상 복구한다. 116 | 117 |
118 | 119 | ## 정리 후기 120 | 121 | - 책 내용에는 모듈 실행 컨텍스트에 대한 설명이 없는데 모듈 시스템에서는 어떤식으로 스코프 체인을 구성하는지 궁금해졌다. 122 | - 함수 실행 컨텍스트 생성 시 함수 객체의 \[\[Environment\]\] 내부 슬롯을 통해 외부 렉시컬 환경 참조를 결정한다고 했는데 함수 객체 안에 있는 \[\[Scope\]\]과는 어떤 차이점을 가지고 있고 \[\[Scope\]\]은 어떤 역할을 하는지 공부해 봐야겠다. 123 | - 공부에는 끝이 없다. -------------------------------------------------------------------------------- /26장-ES6-함수의-추가-기능/README.md: -------------------------------------------------------------------------------- 1 | # 26장 ES6 함수의 추가 기능 2 | 3 | ## 26.1 함수의 구분 4 | 5 |
6 | 7 | ### ES6 이전 8 | - ES6 이전에는 자바스크립트의 함수를 목적에 따라 구분하지 않았다. 9 | - 하나의 함수가 '일반 함수', '생성자 함수', '메서드'의 역할을 모두 수행했다. 10 | - 즉, ES6 이전의 함수는 callable인 동시에 constructor 였다. 11 | - 이러한 방식은 함수 활용에 있어 자유도를 높이는 한 편 실수의 가능성을 내포한다. 12 | - 함수 객체가 필요하지 않은 프로퍼티나 프로토타입을 갖게 된다. 13 | 14 | ### ES6 15 | - ES6에서는 함수를 사용 목적에 따라 명확하게 구분한다. 16 | 17 |
18 | 19 | |ES6 함수 구분|constructor|prototype|super|arguments| 20 | |-|:-:|:-:|:-:|:-:| 21 | |일반 함수|O|O|X|O| 22 | |메서드|X|X|O|O| 23 | |화살표 함수|X|X|X|X| 24 | 25 |
26 | 27 | ## 26.2 메서드 28 | 29 |
30 | 31 | - ES6에서 메서드는 '메서드 축약 표현'으로 정의된 함수를 의미한다. 32 | - 이후 이 장에서 말하는 메서드는 ES6의 메서드를 의미한다. 33 | - 메서드 축약 표현 예시 34 | ```js 35 | const obj = { 36 | x: 1, 37 | foo() { // 메서드 축약 표현 38 | console.log("test"); 39 | }, 40 | } 41 | ``` 42 | - 메서드는 내부 슬롯 [[HomeObject]]를 갖는다. 43 | - 메서드는 super 키워드를 사용할 수 있다. 44 | - [[HomeObject]]와 super에 대한 내용은 `25.8.5` 참고 45 | 46 |
47 | 48 | ## 26.3 화살표 함수 49 | 50 |
51 | 52 | ### 화살표 함수 정의 53 | 54 | - 화살표 함수의 기본 정의는 다음과 같다. 55 | ```js 56 | (매개변수들) => { 57 | 함수 몸체 58 | } 59 | ``` 60 | - 화살표 함수는 항상 함수 표현식으로 취급된다. 61 | ```js 62 | // 변수에 할당 63 | const multiply = (x, y) => { 64 | return x * y; 65 | } 66 | 67 | // 인수로 전달 68 | someFunc((x, y) => { 69 | return x * y; 70 | }) 71 | ``` 72 | - 매개변수가 하나라면 소괄호를 생략할 수 있다. 73 | ```js 74 | const arrow = x => { console.log(x) }; 75 | 76 | // 소괄호는 매개변수가 정확하게 1개일 때만 생략할 수 있다. 77 | // 따라서 매개변수가 없거나 두개 이상이라면 반드시 소괄호를 적어줘야한다. 78 | ``` 79 | - 함수의 몸체가 하나의 표현식이라면 중괄호를 생략할 수 있다. 이때 해당 표현식을 평가한 값이 암묵적으로 반환된다. 80 | ```js 81 | const power = x => x**2; 82 | 83 | power(2); // 4 84 | ``` 85 | - 만약 중괄호를 생략한 채로 객체 리터럴을 반환하고 싶다면 반드시 소괄호로 감싸줘야한다. 객체 리터럴을 구성하는 중괄호가 함수 몸체를 감싸는 블록문으로 인식되기 때문이다. 86 | ```js 87 | const create = (id, content) => ({ id, content }); 88 | 89 | create(1, "test"); // { id: 1, content: "test" }를 반환 90 | ``` 91 | 92 |
93 | 94 | ### 화살표 함수와 일반 함수의 차이 95 | 96 |
97 | 98 | - 화살표 함수는 non-constructor이다. 99 | - 화살표 함수에서는 중복된 이름의 매개변수를 선언할 수 없다. 100 | - 이는 strict mode일 때의 함수 선언문과 같다. 101 | - 화살표 함수는 this, arguments, super, new.target을 갖지않는다. 102 | - 따라서 화살표 함수 내에서 위 키워드를 참조하면 현재 스코프를 제외한 스코프 체인에서 해당 키워드를 탐색한다. 103 | - 이러한 성질은 콜백 함수를 전달할 때 유용한다. 104 | - 기존에는 함수가 다른 함수의 인수로 전달될 경우 기존 컨텍스트를 유지하기 까다롭다는 문제점이 있었다. 105 | - 화살표 함수를 이용하면 다음과 같이 콜백 함수에서도 컨텍스트를 유지할 수 있다. 106 | ```js 107 | class A { 108 | constructor() { 109 | this.a = null; 110 | } 111 | 112 | a() { 113 | this.a = "a"; 114 | } 115 | } 116 | 117 | class B extends A { 118 | b() { 119 | setTimeout(() => { 120 | console.log(this.a); 121 | super.a(); 122 | console.log(this.a); 123 | }, 100) 124 | } 125 | } 126 | 127 | const instanceB = new B(); 128 | 129 | instanceB.b(); 130 | // null 131 | // "a" 132 | ``` 133 | - 위와 같은 성질 중 특히 this만을 꼽아 `Lexical this` 라고 부르기도 하는데 따로 이름을 주는게 의미가 있나 싶다. 다만 ES6 이전에 this가 얼마나 성가신 존재였는지를 나타내는 용어 정도의 의의가 있는 것 같다. 134 | 135 |
136 | 137 | ## 26.4 Rest 파라미터 138 | 139 |
140 | 141 | - Rest 파라미터는 매개변수의 이름 앞에 ...을 붙여 정의한다. 142 | - Rest 파라미터는 함수에 전달된 인수들의 목록을 배열로 전달받는다. 143 | 144 | ```js 145 | const foo = (...rest) => { 146 | console.log(rest); 147 | }; 148 | 149 | foo(1,2,3,4); // [1, 2, 3, 4] 150 | ``` 151 | 152 | - Rest 파라미터는 다른 인자와 함께 사용될 수 있다. 153 | - 이때, 반드시 Rest 파라미터가 마지막이 되어야하며 하나의 함수에서는 하나의 Rest 파라미터만 정의할 수 있다. 154 | - 함수의 인자들에 순차적으로 할당되고 남은 인수들은 모두 Rest 파라미터에게 전달된다. 155 | 156 | ```js 157 | const foo = (first, second, ...rest) => { 158 | console.log(first); 159 | console.log(second); 160 | console.log(rest); 161 | 162 | foo(1, 2, 3, 4); 163 | // 1 164 | // 2 165 | // [3, 4] 166 | ``` 167 | - 유사 배열은 arguments 객체와 다르게 배열이라는 장점이 있다. 168 | - arguments 객체를 갖지 않는 화살표 함수에서 가변 인자를 구현하기 위해서는 Rest 파라미터를 사용해야한다. 169 | 170 |
171 | 172 | ## 26.5 매개변수 기본값 173 | 174 | - ES6에서는 함수의 인자에 기본값을 지정할 수 있다. 175 | - Rest 파라미터에는 기본값을 지정할 수 없다. 176 | 177 | ```js 178 | const foo = (x = 0, y = 0) => x + y; 179 | 180 | foo(); // 0 181 | // 인자를 전달하지 않았으므로 x, y는 각각 지정한 기본값이 된다. 182 | ``` -------------------------------------------------------------------------------- /33장-Symbol/README.md: -------------------------------------------------------------------------------- 1 | # Symbol 2 | 3 | ## 33.1 심벌이란? 4 | 5 |
6 | 7 | 심벌(Symbol)은 ES6에서 추가된 원시타입으로 모든 심벌 타입의 값은 다른 값과 중복되지 않는 유일한 값으로 취급된다. 8 | 9 |
10 | 11 | ## 33.2 심벌 값의 생성 12 | 13 |
14 | 15 | 1. Symbol 함수를 통한 생성 16 | - 심벌 값은 기본적으로 Symbol 함수로 생성할 수 있다. 17 | ```js 18 | Symbol([description]) 19 | // description: string 20 | ``` 21 | - 심벌 값을 정의하는 리터럴은 존재하지 않는다. 22 | - Symbol 함수는 new 키워드를 통해 호출할 수 없다. 23 | - Symbol 함수에는 문자열 인수를 선택적으로 전달 할 수 있다. 이때 전달한 문자열은 심벌 값에 대한 설명이며 디버깅 용도로 사용된다. 주의할 점은 해당 설명이 심벌 값 자체에는 영향을 주지 않는다는 것이다. 즉, 같은 문자열은 인수로 전달해 생성한 심벌 값들이라도 각각 유일하다. 24 | - Symbol 생성 시 인수로 전달한 설명은 description 프로퍼티로 참조할 수 있다. 이때, 다른 원시타입처럼 래퍼객체를 생성하여 Symbol.prototype의 프로퍼티를 참조한다. 25 | ```js 26 | const mySymbol = Symbol("이건 내 심벌이야"); 27 | 28 | console.log(mySymbol.description); // 이건 내 심벌이야 29 | ``` 30 | - 심벌은 문자열이나 숫자로 암묵적 형변환되지 않는다. 다만 Symbol.prototype에 toString 메서드를 가지고 있다. 31 | - 불리언 타입으로는 암묵적으로 형변환 될 수 있다. 32 | 33 |
34 | 35 | 2. Symbol.for / Symbol.keyFor 메서드 36 | - 전역 심벌 레지스트리는 문자열 키와 심벌 값의 쌍을 저장하는 객체이다. 37 | - 이를 통해 유일한 심벌 값을 전역에서 공유할 수 있다. 38 | - Symbol.for과 Symbol.keyFor을 통해 전역 심벌 레지스트리에 접근한다. 39 | - Symbol.for(key: string) 40 | - Symbol.for에는 인수로 문자열을 전달한다. 41 | - 인수로 전달한 문자열을 키로하는 심벌 값이 전역 심벌 레지스트리에 있으면 해당 값을 반환한다. 42 | - 키와 일치하는 값이 없으면 해당 문자열을 키로하는 새로운 심벌 값을 생성하고 이를 반환한다. 43 | - Symbol.keyFor(value: Symbol) 44 | - Symbol.keyFor에는 인수로 심벌을 전달한다. 45 | - 인수로 받은 심벌이 전역 심벌 레지스트리에 저장되어 있으면 해당하는 심벌의 키를 반환한다. 46 | 47 |
48 | 49 | ## 33.4~6 심벌과 프로퍼티 50 | 51 |
52 | 53 | - 객체의 프로퍼티 키로 심벌을 사용할 수 있다. 54 | - 심벌은 유일한 값이므로 프로퍼티의 키로 사용하면 다른 프로퍼티 키와 충돌하지 않는다. 55 | - 키가 심벌인 프로퍼티는 for ...in 문이나 Object.keys, Object.getOwnPropertyNames로 찾을 수 없다. 따라서 심벌을 이용해 프로퍼티를 은닉할 수 있다. 56 | - 단, ES6에 도입된 Object.getOwnPropertySymbols 메서드를 사용하면 키가 심벌인 프로퍼티를 조화할 수 있다. 57 | - 표준 빌트인 객체에 메서드를 추가할 때 심벌을 키로 사용하면 보다 안전하게 활용할 수 있다. 58 | 59 |
60 | 61 | ## 33.7 Well-known Symbol 62 | 63 | - 자바스크립트에는 기본적으로 제공하는 빌트인 심벌 값이 있다. 이를 Well-known Symbol이라고 부른다. 64 | - Well-known Symbol은 자바스크립트 엔진의 내부 알고리즘에 사용된다. 65 | - 예를 들어 Symbol.iterator는 이터레이터를 반환하는 메서드의 키로 사용되는 심벌이다. -------------------------------------------------------------------------------- /34장-이터러블/README.md: -------------------------------------------------------------------------------- 1 | # 34장 - 이터러블 2 | 3 | ## 34.1 이터레이션 프로토콜 4 | 5 |
6 | 7 | - 이터레이션 프로토콜은 순회 가능한 자료구조를 위한 규칙이다. 8 | - 이터레이션 프로토콜을 준수하는 컬렉션은 for...of 문, 스프레드 문법, 배열 디스트럭처링 할당의 대상이 될 수 있다. 9 | - 이터레이션 프로토콜에는 `이터러블 프로토콜`과 `이터레이터 프로토콜`이 있다. 10 | 11 |
12 | 13 | ### 이터러블 프로토콜 14 | - Symbol.iterator를 키로하는 메서드를 프로토타입 체인 내에 가지고 있어야한다. 15 | - Symbol.iterator 메서드를 호출하면 이터레이터 프로토콜을 준수한 이터레이터를 반환해야한다. 16 | 17 |
18 | 19 | ### 이터레이터 프로토콜 20 | - 'next'를 키로하는 메서드를 가지고 있어야한다. 21 | - next 메서드를 호출하면 'value'와 'done' 프로퍼티를 갖는 객체를 반환해야한다. 이때 반환하는 객체를 이터레이터 리절트 객체라고 한다. 22 | - value는 현재 순회 중인 이터러블의 값을 나타내며, done은 이터러블 순회 완료 여부를 나타낸다. 23 | 24 |
25 | 26 | ### 이터러블 27 | - 이터러블 프로토콜을 준수한 객체를 `이터러블`이라고한다. 28 | - 이터러블은 for ...of 문으로 순회할 수 있으며, 스프레드 문법, 배열 디스트럭처링 할당의 대상이된다. 29 | - Array와 String은 자바스크립트의 빌트인 이터러블이다. 30 | 31 |
32 | 33 | ### 이터레이터 34 | 35 |
36 | 37 | - 이터레이터 프로토콜을 준수하는 객체이다. 38 | 39 |
40 | 41 | ## 34.3 for...of 문 42 | 43 |
44 | 45 | - for ...of 문은 이터러블을 순회하기 위한 문법이다. 46 | ```js 47 | for (변수 선언문 of 이터러블) { ... } 48 | ``` 49 | - for ...of 문은 내부적으로 이터러블의 Symbol.iterator를 통해 이터레이터를 얻는다. 50 | - 이터레이터의 next 메서드를 통해 순회하는데 next가 반환하는 리절트 객체의 value 프로퍼티를 for...of 문 변수에 할당한다. 51 | - 이때, 순회는 리절트 객체의 done 프로퍼티의 값이 true일 때까지 반복한다. 52 | 53 |
54 | 55 | ## 34.4 이터러블과 유사 배열 객체 56 | 57 |
58 | 59 | - 유사 배열 객체는 각 프로퍼티에 인덱스로 접근할 수 있고 length 프로퍼티를 갖는 객체를 의미한다. 60 | - 겉으로 보기에 배열같아 보이지만 배열의 메서드를 사용할 수 없음은 물론 Symbol.iterator 메서드도 가지고 있지 않기 때문에 이터러블도 아니다. -------------------------------------------------------------------------------- /35장-스프레드-문법/README.md: -------------------------------------------------------------------------------- 1 | # 35장 스프레드 문법 2 | 3 | - ES6에서 도입된 스프레드 문법 `...` 은 하나로 뭉쳐 있는 여러 값들의 집합을 펼쳐서(전개, 분산하여) 개별적인 값들의 목록으로 만든다. 4 | - 스프레드 문법을 사용할 수 있는 대상은 Array, String, Map, Set, DOM 컬력센(NodeList, HTMLCollection), arguments와 같이 `for ...of` 문으로 순회할 수 있는 이터러블에 한정된다. 5 | 6 | ```js 7 | // ...[1, 2, 3]은 [1, 2, 3]을 개별 요소로 분리한다(→ 1, 2, 3) 8 | console.log(...[1, 2, 3]); // 1 2 3 9 | 10 | // 문자열은 이터러블이다. 11 | console.log(...'Hello'); // H e l l o 12 | 13 | // Map과 Set은 이터러블이다. 14 | console.log(...new Map([['a', '1'], ['b', '2']])); // [ 'a', '1' ] [ 'b', '2' ] 15 | console.log(...new Set([1, 2, 3])); // 1 2 3 16 | 17 | // 이터러블이 아닌 일반 객체는 스프레드 문법의 대상이 될 수 없다. 18 | console.log(...{ a: 1, b: 2 }); 19 | // TypeError: Found non-callable @@iterator 20 | ``` 21 | 22 | - 스프레드 문법의 결과는 값이 아니다. 이는 스프레드 문법(`...`)이 피연산자를 연산하여 값을 생성하는 연산자가 이님을 의미한다. 23 | - 따라서 스프레드 문법의 결과는 변수에 할당할 수 없다. 24 | 25 | ```js 26 | // 스프레드 문법의 결과는 값이 아니다. 27 | const list = ...[1, 2, 3]; // SyntaxError: Unexpected token ... 28 | ``` 29 | 30 | - 스프레드 문법의 결과물은 값으로 사용할 수 없고, 다음과 같이 쉼표로 구분한 값의 목록을 사용하는 문맥에서만 사용할 수 있다. 31 | - 함수 호출문의 인수 목록 32 | - 배열 리터럴의 요소 목록 33 | - 객체 리터럴의 프로퍼티 목록 34 | 35 | ## 35.1 함수 호출문의 인수 목록에서 사용하는 경우 36 | 37 | ```js 38 | const arr = [1, 2, 3]; 39 | 40 | // 배열 arr의 요소 중에서 최대값을 구하기 위해 Math.max를 사용한다. 41 | const max = Math.max(arr); // -> NaN 42 | ``` 43 | 44 | - Math.max 메서드는 매개변수 개수를 확정할 수 없는 가변 인자 함수다. 다음과 같이 개수가 정해져 있지 않은 여러 개의 숫자를 인수로 전달받아 최대값을 반환한다. 45 | - 만약 Math.max 메서드에 숫자가 아닌 배열을 인수로 전달하면 최댓값을 구할 수 없으므로 NaN을 반환한다. 46 | - 이 같은 문제를 해결하기 위해 스프레드 문법을 사용할 수 있다. 47 | 48 | ```js 49 | const arr = [1, 2, 3]; 50 | 51 | // 스프레드 문법을 사용하여 배열 arr을 1, 2, 3으로 펼쳐서 Math.max에 전달한다. 52 | // Math.max(...[1, 2, 3])은 Math.max(1, 2, 3)과 같다. 53 | const max = Math.max(...arr); // -> 3 54 | ``` 55 | 56 | - 스프레드 문법은 Rest 파라미터와 형태가 동일하여 혼동할 수 있으므로 주의가 필요하다. 57 | - Rest 파라미터는 함수에 전달된 인수들의 목록을 배열로 전달받기 위해 이름 앞에 ...을 붙이는 것이다. 58 | - 스프레드 문법은 여러 개의 값이 하나로 뭉쳐있는 배열과 같은 이터러블을 펼쳐서 개별 값의 목록을 만드는 것이다. 59 | - 따라서 Rest 파라미터와 스프레드 문법은 서로 반대의 개념이다. 60 | 61 | ```js 62 | // Rest 파라미터는 인수들의 목록을 배열로 전달받는다. 63 | function foo(...rest) { 64 | console.log(rest); // 1, 2, 3 -> [ 1, 2, 3 ] 65 | } 66 | 67 | // 스프레드 문법은 배열과 같은 이터러블을 펼쳐서 개별적인 값들의 목록을 만든다. 68 | // [1, 2, 3] -> 1, 2, 3 69 | foo(...[1, 2, 3]); 70 | ``` 71 | 72 | ## 35.2 배열 리터럴 내부에서 사용하는 경우 73 | 74 | ### 35.2.1 concat 75 | 76 | - ES5에서 2개의 배열을 1개의 배열로 결합하는 경우 concat 메서드를 사용해야 한다. 77 | 78 | ```js 79 | // ES5 80 | var arr = [1, 2].concat([3, 4]); 81 | console.log(arr); // [1, 2, 3, 4] 82 | ``` 83 | 84 | - 스프레드 문법을 사용하면 별도의 메서드를 사용하지 않고 배열 리터럴만으로 2개의 배열을 1개의 배열로 결합할 수 있다. 85 | 86 | ```js 87 | // ES6 88 | const arr = [...[1, 2], ...[3, 4]]; 89 | console.log(arr); // [1, 2, 3, 4] 90 | ``` 91 | 92 | ### 35.2.2 splice 93 | 94 | - ES5에서 배열 중간에 요소를 추가하려면 splice메서드를 사용한다. 95 | 96 | ```js 97 | // ES5 98 | var arr1 = [1, 4]; 99 | var arr2 = [2, 3]; 100 | 101 | // 세 번째 인수 arr2를 해체하여 전달해야 한다. 102 | // 그렇지 않으면 arr1에 arr2 배열 자체가 추가된다. 103 | arr1.splice(1, 0, arr2); 104 | 105 | // 기대한 결과는 [1, [2, 3], 4]가 아니라 [1, 2, 3, 4]다. 106 | console.log(arr1); // [1, [2, 3], 4] 107 | ``` 108 | 109 | - 위 예제의 경우 세번째 인수 [2, 3]을 해체하여 전달해야 한다. 그렇지 않으면 arr배열 자체가 추가된다. 110 | - 따라서 이러한 경우 apply 메서드를 사용해야 한다. 111 | 112 | ```js 113 | // ES5 114 | var arr1 = [1, 4]; 115 | var arr2 = [2, 3]; 116 | 117 | /* 118 | apply 메서드의 2번째 인수(배열)는 apply 메서드가 호출한 splice 메서드의 인수 목록이다. 119 | apply 메서드의 2번째 인수 [1, 0].concat(arr2)는 [1, 0, 2, 3]으로 평가된다. 120 | 따라서 splice 메서드에 apply 메서드의 2번째 인수 [1, 0, 2, 3]이 해체되어 전달된다. 121 | 즉, arr1[1]부터 0개의 요소를 제거하고 그 자리(arr1[1])에 새로운 요소(2, 3)를 삽입한다. 122 | */ 123 | Array.prototype.splice.apply(arr1, [1, 0].concat(arr2)); 124 | console.log(arr1); // [1, 2, 3, 4] 125 | ``` 126 | 127 | - 스프레드 문법을 사용하면 다음과 같이 간결하게 표현할 수 있다. 128 | 129 | ```js 130 | // ES6 131 | const arr1 = [1, 4]; 132 | const arr2 = [2, 3]; 133 | 134 | arr1.splice(1, 0, ...arr2); 135 | console.log(arr1); // [1, 2, 3, 4] 136 | ``` 137 | 138 | ### 35.2.3 배열 복사 139 | 140 | - ES5에서 배열을 복사하기 위해서 slice 메서드를 사용한다. 141 | 142 | ```js 143 | // ES5 144 | var origin = [1, 2]; 145 | var copy = origin.slice(); 146 | 147 | console.log(copy); // [1, 2] 148 | console.log(copy === origin); // false 149 | ``` 150 | 151 | - 스프레드 문법을 사용하면 간결하게 표현할 수 있다. 152 | 153 | ```js 154 | // ES6 155 | const origin = [1, 2]; 156 | const copy = [...origin]; 157 | 158 | console.log(copy); // [1, 2] 159 | console.log(copy === origin); // false 160 | ``` 161 | 162 | ### 35.2.4 이터러블을 배열로 변환 163 | 164 | - ES5에서 이터러블을 배열로 변환하려면 apply 또는 call 메서드를 사용하여 slice를 호출해야 한다. 165 | 166 | ```js 167 | // ES5 168 | function sum() { 169 | // 이터러블이면서 유사 배열 객체인 arguments를 배열로 변환 170 | var args = Array.prototype.slice.call(arguments); 171 | 172 | return args.reduce(function (pre, cur) { 173 | return pre + cur; 174 | }, 0); 175 | } 176 | 177 | console.log(sum(1, 2, 3)); // 6 178 | ``` 179 | 180 | - 이 방법은 이터러블 뿐만 아니라 유사 배열 객체도 배열로 변환할 수 있다. 181 | 182 | ```js 183 | // 이터러블이 아닌 유사 배열 객체 184 | const arrayLike = { 185 | 0: 1, 186 | 1: 2, 187 | 2: 3, 188 | length: 3 189 | }; 190 | 191 | const arr = Array.prototype.slice.call(arrayLike); // -> [1, 2, 3] 192 | console.log(Array.isArray(arr)); // true 193 | ``` 194 | 195 | - 스프레드 문법을 사용하면 좀 더 간편하게 이터러블을 배열로 변환할 수 있다. 196 | 197 | ```js 198 | // ES6 199 | function sum() { 200 | // 이터러블이면서 유사 배열 객체인 arguments를 배열로 변환 201 | return [...arguments].reduce((pre, cur) => pre + cur, 0); 202 | } 203 | 204 | console.log(sum(1, 2, 3)); // 6 205 | ``` 206 | 207 | - 위 예제보다 나은 방법은 Rest 파라미터를 사용하는 것이다. 208 | 209 | ```js 210 | // Rest 파라미터 args는 함수에 전달된 인수들의 목록을 배열로 전달받는다. 211 | const sum = (...args) => args.reduce((pre, cur) => pre + cur, 0); 212 | 213 | console.log(sum(1, 2, 3)); // 6 214 | ``` 215 | 216 | - 단, 이터러블이 아닌 유사 배열 객체는 스프레드 문법의 대상이 될 수 없다. 217 | 218 | ```js 219 | // 이터러블이 아닌 유사 배열 객체 220 | const arrayLike = { 221 | 0: 1, 222 | 1: 2, 223 | 2: 3, 224 | length: 3 225 | }; 226 | 227 | const arr = [...arrayLike]; 228 | // TypeError: object is not iterable (cannot read property Symbol(Symbol.iterator)) 229 | ``` 230 | 231 | - 이터러블이 아닌 유사 배열 객체를 배열로 변경하려면 ES6에 도입된 Array.from 메서드를 사용한다. 232 | 233 | ```js 234 | // Array.from은 유사 배열 객체 또는 이터러블을 배열로 변환한다 235 | Array.from(arrayLike); // -> [1, 2, 3] 236 | ``` 237 | 238 | ## 35.3 객체 리터럴 내부에서 사용하는 경우 239 | 240 | - TC39 프로세스의 stage 4 단계에 제안되어 있는 스프레드 프로퍼티를 사용하면 객체 리터럴의 프로퍼티 목록에도 스프레드 문법을 사용할 수 있다. 241 | 242 | ```js 243 | // 스프레드 프로퍼티 244 | // 객체 복사(얕은 복사) 245 | const obj = { x: 1, y: 2 }; 246 | const copy = { ...obj }; 247 | console.log(copy); // { x: 1, y: 2 } 248 | console.log(obj === copy); // false 249 | 250 | // 객체 병합 251 | const merged = { x: 1, y: 2, ...{ a: 3, b: 4 } }; 252 | console.log(merged); // { x: 1, y: 2, a: 3, b: 4 } 253 | ``` 254 | 255 | - 스프레드 프로퍼티가 제안되기 전에는 Object.assign 메서드를 사용하여 객체를 병합하거나 프로퍼티를 변경, 추가했다. 256 | 257 | ```js 258 | // 객체 병합. 프로퍼티가 중복되는 경우, 뒤에 위치한 프로퍼티가 우선권을 갖는다. 259 | const merged = Object.assign({}, { x: 1, y: 2 }, { y: 10, z: 3 }); 260 | console.log(merged); // { x: 1, y: 10, z: 3 } 261 | 262 | // 특정 프로퍼티 변경 263 | const changed = Object.assign({}, { x: 1, y: 2 }, { y: 100 }); 264 | console.log(changed); // { x: 1, y: 100 } 265 | 266 | // 프로퍼티 추가 267 | const added = Object.assign({}, { x: 1, y: 2 }, { z: 0 }); 268 | console.log(added); // { x: 1, y: 2, z: 0 } 269 | ``` 270 | 271 | - 스프레드 프로퍼티는 Object.assign 메서드를 대체할 수 있는 간편한 문법이다. 272 | 273 | ```js 274 | // 객체 병합. 프로퍼티가 중복되는 경우, 뒤에 위치한 프로퍼티가 우선권을 갖는다. 275 | const merged = { ...{ x: 1, y: 2 }, ...{ y: 10, z: 3 } }; 276 | console.log(merged); // { x: 1, y: 10, z: 3 } 277 | 278 | // 특정 프로퍼티 변경 279 | const changed = { ...{ x: 1, y: 2 }, y: 100 }; 280 | // changed = { ...{ x: 1, y: 2 }, ...{ y: 100 } } 281 | console.log(changed); // { x: 1, y: 100 } 282 | 283 | // 프로퍼티 추가 284 | const added = { ...{ x: 1, y: 2 }, z: 0 }; 285 | // added = { ...{ x: 1, y: 2 }, ...{ z: 0 } } 286 | console.log(added); // { x: 1, y: 2, z: 0 } 287 | ``` 288 | 289 | -------------------------------------------------------------------------------- /36장-디스트럭처링-할당/README.md: -------------------------------------------------------------------------------- 1 | # 36장 디스트럭처링 할당 2 | 3 | - 디스트럭처링 할당(구조 분해 할당)은 구조화된 배열과 같은 이터러블 또는 객체를 destructuring하여 1개 이상의 변수에 개별적으로 할당하는 것을 말한다. 배열과 같은 이터러블 또는 객체 리터럴에서 필요한 값만 추출하여 변수에 할당할 때 유리하다. 4 | 5 | ## 36.1 배열 디스트럭처링 할당 6 | 7 | - ES5에서 구조화된 배열을 디스트럭처링하여 1개 이상의 변수에 할당하는 방법은 다음과 같다. 8 | 9 | ```js 10 | // ES5 11 | var arr = [1, 2, 3]; 12 | 13 | var one = arr[0]; 14 | var two = arr[1]; 15 | var three = arr[2]; 16 | 17 | console.log(one, two, three); // 1 2 3 18 | ``` 19 | 20 | - ES6의 배열 디스트럭처링 할당은 배열의 각 요소를 배열로부터 추출하여 1개 이상의 변수에 할당한다. 21 | - **이때 배열 디스트럭처링 할당의 대상(할당문의 우변)은 이터러블이어야 하며, 할당 기준은 배열의 인덱스다.** 22 | 23 | ```js 24 | const arr = [1, 2, 3]; 25 | 26 | // ES6 배열 디스트럭처링 할당 27 | // 변수 one, two, three를 선언하고 배열 arr을 디스트럭처링하여 할당한다. 28 | // 이때 할당 기준은 배열의 인덱스다. 29 | const [one, two, three] = arr; 30 | 31 | console.log(one, two, three); // 1 2 3 32 | ``` 33 | 34 | - 배열 디스트럭처링 할당을 위해서는 할당 연산자 왼쪽에 값을 할당받을 변수를 선언해야 한다. 이때 변수를 배열 리터럴 형태로 선언한다. 35 | 36 | ```js 37 | const [x, y] = [1, 2]; 38 | ``` 39 | 40 | - 이때 우변에 이터러블을 할당하지 않으면 에러가 발생한다. 41 | 42 | ```js 43 | const [x, y]; // SyntaxError: Missing initializer in destructuring declaration 44 | 45 | const [a, b] = {}; // TypeError: {} is not iterable 46 | ``` 47 | 48 | - 배열 디스트럭처링 할당의 기준은 배열의 인덱스다. 이때 변수의 개수와 이터러블의 요소 개수가 반드시 일치할 필요는 없다. 49 | 50 | ```js 51 | const [a, b] = [1, 2]; 52 | console.log(a, b); // 1 2 53 | 54 | const [c, d] = [1]; 55 | console.log(c, d); // 1 undefined 56 | 57 | const [e, f] = [1, 2, 3]; 58 | console.log(e, f); // 1 2 59 | 60 | const [g, , h] = [1, 2, 3]; 61 | console.log(g, h); // 1 3 62 | ``` 63 | 64 | - 배열 디스트럭처링 할당을 위한 변수에 기본값을 설정할 수 있다. 65 | 66 | ```js 67 | // 기본값 68 | const [a, b, c = 3] = [1, 2]; 69 | console.log(a, b, c); // 1 2 3 70 | 71 | // 기본값보다 할당된 값이 우선한다. 72 | const [e, f = 10, g = 3] = [1, 2]; 73 | console.log(e, f, g); // 1 2 3 74 | ``` 75 | 76 | - 배열 디스트럭처링 할당은 배열과 같은 이터러블에서 필요한 요소만 추출하여 변수에 할당하고 싶을 때 유용하다. 77 | - 다음 예제는 URL을 파싱하여 protocol, host, path 프로퍼티를 갖는 객체를 생성해 반환한다. 78 | 79 | ```js 80 | // url을 파싱하여 protocol, host, path 프로퍼티를 갖는 객체를 생성해 반환한다. 81 | function parseURL(url = '') { 82 | // '://' 앞의 문자열(protocol)과 '/' 이전의 '/'으로 시작하지 않는 문자열(host)과 '/' 이후의 문자열(path)을 검색한다. 83 | const parsedURL = url.match(/^(\w+):\/\/([^/]+)\/(.*)$/); 84 | console.log(parsedURL); 85 | /* 86 | [ 87 | 'https://developer.mozilla.org/ko/docs/Web/JavaScript', 88 | 'https', 89 | 'developer.mozilla.org', 90 | 'ko/docs/Web/JavaScript', 91 | index: 0, 92 | input: 'https://developer.mozilla.org/ko/docs/Web/JavaScript', 93 | groups: undefined 94 | ] 95 | */ 96 | 97 | if (!parsedURL) return {}; 98 | 99 | // 배열 디스트럭처링 할당을 사용하여 이터러블에서 필요한 요소만 추출한다. 100 | const [, protocol, host, path] = parsedURL; 101 | return { protocol, host, path }; 102 | } 103 | 104 | const parsedURL = parseURL('https://developer.mozilla.org/ko/docs/Web/JavaScript'); 105 | console.log(parsedURL); 106 | /* 107 | { 108 | protocol: 'https', 109 | host: 'developer.mozilla.org', 110 | path: 'ko/docs/Web/JavaScript' 111 | } 112 | */ 113 | ``` 114 | 115 | - 배열 디스트럭처링 할당을 위한 변수에 Rest 파라미터와 유사하게 Rest 요소 ...을 사용할 수 있다. 116 | 117 | ```js 118 | // Rest 요소 119 | const [x, ...y] = [1, 2, 3]; 120 | console.log(x, y); // 1 [ 2, 3 ] 121 | ``` 122 | 123 | ## 36.2 객체 디스트럭처링 할당 124 | 125 | - ES5에서 객체의 각 프로퍼티를 객체로부터 디스트럭처링하여 변수에 할당하기 위해서는 프로퍼티 키를 사용해야 한다. 126 | 127 | ```js 128 | // ES5 129 | var user = { firstName: 'Ungmo', lastName: 'Lee' }; 130 | 131 | var firstName = user.firstName; 132 | var lastName = user.lastName; 133 | 134 | console.log(firstName, lastName); // Ungmo Lee 135 | ``` 136 | 137 | - ES6 객체 디스트럭처링 할당은 객체의 각 프로퍼티를 객체로부터 추출하여 변수에 할당한다. 138 | - 이때 객체 디스트럭처링 할당의 대상은 객체이어야 하며, **할당 기준은 프로퍼티 키다.** 즉, 순서는 의미가 없으며 선언된 변수 이름과 프로퍼티 키가 일치하면 할당된다. 139 | 140 | ```js 141 | const user = { firstName: 'Ungmo', lastName: 'Lee' }; 142 | 143 | // ES6 객체 디스트럭처링 할당 144 | // 변수 lastName, firstName을 선언하고 user 객체를 디스트럭처링하여 할당한다. 145 | // 이때 프로퍼티 키를 기준으로 디스트럭처링 할당이 이루어진다. 순서는 의미가 없다. 146 | const { lastName, firstName } = user; 147 | 148 | console.log(firstName, lastName); // Ungmo Lee 149 | ``` 150 | 151 | - 배열 디스트럭처링 할당과 마찬가지로 객체 디스트럭처링 할당을 위해서는 할당 연산자 왼쪽에 프로퍼티 값을 할당받을 변수를 선언해야 한다. 152 | 153 | ```js 154 | const { lastName, firstName } = { firstName: 'Ungmo', lastName: 'Lee' }; 155 | ``` 156 | 157 | - 객체 프로퍼티 키와 다른 변수 이름으로 프로퍼티 값을 할당받으려면 다음과 같이 변수를 선언한다. 158 | 159 | ```js 160 | const user = { firstName: 'Ungmo', lastName: 'Lee' }; 161 | 162 | // 프로퍼티 키를 기준으로 디스트럭처링 할당이 이루어진다. 163 | // 프로퍼티 키가 lastName인 프로퍼티 값을 ln에 할당하고, 164 | // 프로퍼티 키가 firstName인 프로퍼티 값을 fn에 할당한다. 165 | const { lastName: ln, firstName: fn } = user; 166 | 167 | console.log(fn, ln); // Ungmo Lee 168 | ``` 169 | 170 | - 객체 디스트럭처링 할당은 객체에서 프로퍼티 키로 필요한 프로퍼티 값만 추출하여 변수에 할당하고 싶을 때 유용하다. 171 | 172 | ```js 173 | const str = 'Hello'; 174 | // String 래퍼 객체로부터 length 프로퍼티만 추출한다. 175 | const { length } = str; 176 | console.log(length); // 5 177 | 178 | const todo = { id: 1, content: 'HTML', completed: true }; 179 | // todo 객체로부터 id 프로퍼티만 추출한다. 180 | const { id } = todo; 181 | console.log(id); // 1 182 | ``` 183 | 184 | - 객체 디스트럭처링 할당은 객체를 인수로 받는 함수의 매개변수에도 사용할 수 있다. 185 | 186 | ```js 187 | function printTodo(todo) { 188 | console.log(`할일 ${todo.content}은 ${todo.completed ? '완료' : '비완료'} 상태입니다.`); 189 | } 190 | 191 | printTodo({ id: 1, content: 'HTML', completed: true }); 192 | // 할일 HTML은 완료 상태입니다. 193 | ``` 194 | 195 | - 위 예제를 디스트럭처링 할당을 사용하여 가독성 좋게 표현할 수 있다. 196 | 197 | ```js 198 | function printTodo({ content, completed }) { 199 | console.log(`할일 ${content}은 ${completed ? '완료' : '비완료'} 상태입니다.`); 200 | } 201 | 202 | printTodo({ id: 1, content: 'HTML', completed: true }); 203 | // 할일 HTML은 완료 상태입니다. 204 | ``` 205 | 206 | - 배열 요소가 객체인 경우 배열 디스트럭처링 하당과 객체 디스트럭처링 할당을 혼용할 수 있다. 207 | 208 | ```js 209 | const todos = [ 210 | { id: 1, content: 'HTML', completed: true }, 211 | { id: 2, content: 'CSS', completed: false }, 212 | { id: 3, content: 'JS', completed: false } 213 | ]; 214 | 215 | // todos 배열의 두 번째 요소인 객체로부터 id 프로퍼티만 추출한다. 216 | const [, { id }] = todos; 217 | console.log(id); // 2 218 | ``` 219 | 220 | - 중첩 객체인 경우는 다음과 같이 사용한다. 221 | 222 | ```js 223 | const user = { 224 | name: 'Lee', 225 | address: { 226 | zipCode: '03068', 227 | city: 'Seoul' 228 | } 229 | }; 230 | 231 | // address 프로퍼티 키로 객체를 추출하고 이 객체의 city 프로퍼티 키로 값을 추출한다. 232 | const { address: { city } } = user; 233 | console.log(city); // 'Seoul' 234 | ``` 235 | 236 | - 객체 디스트럭처링 할당을 위한 변수에 Rest 파라미터나 Rest요소와 유사하게 **Rest 프로퍼티 ...** 을 사용할 수 있다. 237 | 238 | ```js 239 | // Rest 프로퍼티 240 | const { x, ...rest } = { x: 1, y: 2, z: 3 }; 241 | console.log(x, rest); // 1 { y: 2, z: 3 } 242 | ``` 243 | 244 | - Rest 프로퍼티는 스프레드 프로퍼티와 함께 TC39의 stage 4 단계에 제안되어 있다. 245 | 246 | -------------------------------------------------------------------------------- /37장-Map/README.md: -------------------------------------------------------------------------------- 1 | # 37장 Set과 Map 2 | 3 | 메서드 요약 4 | 5 | | 이름 | 기능 | 비고 | 6 | | --------------------------- | -------------- | ------------------------------------------------------------ | 7 | | Map.prototype.size 프로퍼티 | 요소 개수 확인 | getter함수만 존재 | 8 | | Map.prototype.add | 요소 추가 | method chaining 가능 | 9 | | Map.prototype.has | 요소 존재 확인 | 반환값: boolean | 10 | | Map .prototoype.delete | 요소 삭제 | 반환값: boolean(method chaing 불가) | 11 | | Map.prototype.clear | 요소 일괄 삭제 | 반환값: undefined | 12 | | Map.prototype.forEach | 요소 순회 | | 13 | | Map.prototype.keys | | 요소키를 값으로 갖는 이터러블이면서 이터레이터인 객체를 반환 | 14 | | Map.prototype.values | | 요소값을 값으로 갖는 이터러블이면서 이터레이터인 객체를 반환 | 15 | | Map.prototype.entries | | 요소키와 요소값을값으로 갖는 이터러블이면서 이터레이터인 객체를 반환 | 16 | 17 | 18 | 19 | ## 37.2 Map 20 | 21 | - Map 은 키와 값의 쌍으로 이루어진 컬렉션이다. 22 | 23 | - 객체와 유사하지만 차이점이 있다. 24 | 25 | | 구분 | 객체 | Map 객체 | 26 | | ---------------------- | ----------------------- | --------------------- | 27 | | 키로 사용할 수 있는 값 | 문자열 또는 심벌 값 | 객체를 포함한 모든 값 | 28 | | 이터러블 | X | O | 29 | | 요소 개수 확이 | Object.keys(obj).length | map.size | 30 | 31 | ### 37.2.1 Map 객체의 생성 32 | 33 | - Map 생성자 함수로 생성 34 | 35 | ```javascript 36 | const map = new Map(); 37 | console.log(map); // Map(0) {} 38 | ``` 39 | 40 | Map 생성자 함수는 이터러블을 인수로 전달받아 Map객체를 생성함. 이때 인수로 전달되는 이터러블은 키와 값의 쌍으로 이루어진 요소로 구성돼야함 41 | 42 | ```javascript 43 | const map1 = new Map([['key1', 'value1'], ['key2', 'value2']]); 44 | console.log(map1); // Map(2) {"key1" => "value1", "key2" => "value2"} 45 | 46 | const map2 = new Map([1, 2]); // TypeError: Iterator value 1 is not an entry object 47 | ``` 48 | 49 | Map 생성자 함수의 인수로 전달한 이터러블에 중복된 키를 갖는 요소가 존재하면 갑싱 덮어씌어짐. 즉, Map 객체에는 중복된 키를 갖는 요소가 존재할 수 없다. 50 | 51 | ```javascript 52 | const map = new Map([['key1', 'value1'], ['key1', 'value2']]); 53 | console.log(map); // Map(1) {"key1" => "value2"} 54 | ``` 55 | 56 | 57 | 58 | ### 37.2.2 요소 개수 확인 59 | 60 | - Map.prototype.size 프로퍼티를 사용 61 | 62 | ```javascript 63 | const { size } = new Map([['key1', 'value1'], ['key2', 'value2']]); 64 | console.log(size); // 2 65 | 66 | ``` 67 | 68 | size 프로퍼티는 setter 함수 없이 getter함수만 존재하는 접근자 프로퍼티. 69 | 70 | 즉, size 프로퍼티에 숫자를 할당하여 Map 객체의 요소 개수를 변경할 수 없다. 71 | 72 | ```javascript 73 | const map = new Map([['key1', 'value1'], ['key2', 'value2']]); 74 | 75 | console.log(Object.getOwnPropertyDescriptor(Map.prototype, 'size')); 76 | // {set: undefined, enumerable: false, configurable: true, get: ƒ} 77 | 78 | map.size = 10; // 무시된다. 79 | console.log(map.size); // 2 80 | ``` 81 | 82 | ### 37.2.3 요소 추가 83 | 84 | - Map.prototype.set 메서드 사용 85 | 86 | ```javascript 87 | const map = new Map(); 88 | console.log(map); // Map(0) {} 89 | 90 | map.set('key1', 'value1'); 91 | console.log(map); // Map(1) {"key1" => "value1"} 92 | ``` 93 | 94 | - method chaining 가능 95 | 96 | ```javascript 97 | const map = new Map(); 98 | 99 | map 100 | .set('key1', 'value1') 101 | .set('key2', 'value2'); 102 | 103 | console.log(map); // Map(2) {"key1" => "value1", "key2" => "value2"} 104 | ``` 105 | 106 | - 중복된 키를 갖는 요소가 존재할 수 없으므로, 중복된 키를 갖는 요소를 추가하면 값이 덮어씌워짐. 에러발생X 107 | 108 | ```javascript 109 | const map = new Map(); 110 | 111 | map 112 | .set('key1', 'value1') 113 | .set('key1', 'value2'); 114 | 115 | console.log(map); // Map(1) {"key1" => "value2"} 116 | ``` 117 | 118 | - 일치 비교 연산자를 사용하면 NaN과 NaN은 다르다고 평가됨. 그러나 Map 객체는 이를 같다고 평가하여 중복 추가를 허용하지 않음. +0과 -0도 같은 맥락으로 허용하지 않음. 119 | 120 | ```javascript 121 | const map = new Map(); 122 | 123 | console.log(NaN === NaN); // false 124 | console.log(0 === -0); // true 125 | 126 | // NaN과 NaN을 같다고 평가하여 중복 추가를 허용하지 않는다. 127 | map.set(NaN, 'value1').set(NaN, 'value2'); 128 | console.log(map); // Map(1) { NaN => 'value2' } 129 | 130 | // +0과 -0을 같다고 평가하여 중복 추가를 허용하지 않는다. 131 | map.set(0, 'value1').set(-0, 'value2'); 132 | console.log(map); // Map(2) { NaN => 'value2', 0 => 'value2' } 133 | ``` 134 | 135 | - Map 객체는 키 타입에 제한이 없다. 따라서 객체를 포함한 모든 값을 키로 사용할 수 있다. 136 | 137 | ```javascript 138 | const map = new Map(); 139 | 140 | const lee = { name: 'Lee' }; 141 | const kim = { name: 'Kim' }; 142 | 143 | // 객체도 키로 사용할 수 있다. 144 | map 145 | .set(lee, 'developer') 146 | .set(kim, 'designer'); 147 | 148 | console.log(map); 149 | // Map(2) { {name: "Lee"} => "developer", {name: "Kim"} => "designer" } 150 | ``` 151 | 152 | 153 | 154 | ### 37.2.4 요소 취득 155 | 156 | - Map.prototype.get 메서드를 사용 157 | 158 | - 인수 : 키를 전달 159 | 160 | - 반환 : 인수로전달한 키를 갖는 `값`을 반환. 없다면 undefined를 반환 161 | 162 | ```javascript 163 | const map = new Map(); 164 | 165 | const lee = { name: 'Lee' }; 166 | const kim = { name: 'Kim' }; 167 | 168 | map 169 | .set(lee, 'developer') 170 | .set(kim, 'designer'); 171 | 172 | console.log(map.get(lee)); // developer 173 | console.log(map.get('key')); // undefined 174 | ``` 175 | 176 | ### 37.2.5 요소 존재 여부 확인 177 | 178 | - Map.prototype.has 메서드 179 | 180 | - 반환 : 불리언 값. 특정 요소의 존재 여부를 나타냄 181 | 182 | ```javascript 183 | const lee = { name: 'Lee' }; 184 | const kim = { name: 'Kim' }; 185 | 186 | const map = new Map([[lee, 'developer'], [kim, 'designer']]); 187 | 188 | console.log(map.has(lee)); // true 189 | console.log(map.has('key')); // false 190 | ``` 191 | 192 | ### 37.2.6 요소 삭제 193 | 194 | - Map.prototype.delete 메서드 사용 195 | 196 | - 반환값 : 불리언 값. 삭제 성공 여부. 197 | 198 | - 인수 : 삭제하려는 키를 전달 199 | 200 | ```javascript 201 | const lee = { name: 'Lee' }; 202 | const kim = { name: 'Kim' }; 203 | 204 | const map = new Map([[lee, 'developer'], [kim, 'designer']]); 205 | 206 | map.delete(kim); 207 | console.log(map); // Map(1) { {name: "Lee"} => "developer" } 208 | ``` 209 | 210 | 존재하지 않는 키를 인수로 전달하면 에러없이 무시됨. 211 | 212 | ```javascript 213 | const map = new Map([['key1', 'value1']]); 214 | 215 | // 존재하지 않는 키 'key2'로 요소를 삭제하려 하면 에러없이 무시된다. 216 | map.delete('key2'); 217 | console.log(map); // Map(1) {"key1" => "value1"} 218 | ``` 219 | 220 | delete는 불리언 값을 반환하므로 method chaining이 불가능 221 | 222 | ```javascript 223 | const lee = { name: 'Lee' }; 224 | const kim = { name: 'Kim' }; 225 | 226 | const map = new Map([[lee, 'developer'], [kim, 'designer']]); 227 | 228 | map.delete(lee).delete(kim); // TypeError: map.delete(...).delete is not a function 229 | ``` 230 | 231 | ### 37.2.7 요소 일괄 삭제 232 | 233 | - Map.prototype.clear 메서드 사용 234 | 235 | - undefined 반환 236 | 237 | ```javascript 238 | const lee = { name: 'Lee' }; 239 | const kim = { name: 'Kim' }; 240 | 241 | const map = new Map([[lee, 'developer'], [kim, 'designer']]); 242 | 243 | map.clear(); 244 | console.log(map); // Map(0) {} 245 | ``` 246 | 247 | ### 37.2.8 요소 순회 248 | 249 | - Map.prototype.forEach 메서드 사용 250 | 251 | - 인수 : 콜백함수와 forEach메서드의 콜백함수내부에서 this로 사용될 객체(옵션)를 전달 252 | 253 | - 콜백함수의 인수 : 1-현재 순회중인 요소값, 2-현재 순회중인 요소키, 3-현재 순회중인 Map객체 지체. 254 | 255 | ```javascript 256 | const lee = { name: 'Lee' }; 257 | const kim = { name: 'Kim' }; 258 | 259 | const map = new Map([[lee, 'developer'], [kim, 'designer']]); 260 | 261 | map.forEach((v, k, map) => console.log(v, k, map)); 262 | /* 263 | developer {name: "Lee"} Map(2) { 264 | {name: "Lee"} => "developer", 265 | {name: "Kim"} => "designer" 266 | } 267 | designer {name: "Kim"} Map(2) { 268 | {name: "Lee"} => "developer", 269 | {name: "Kim"} => "designer" 270 | } 271 | */ 272 | ``` 273 | 274 | - Map 객체는 이터러블. 따라서 하기의 동작이 가능함 275 | 276 | - for...of문으로 순회 가능. 277 | - 스프레드 문법 278 | - 배열 디스트럭쳐링 279 | 280 | ```javascript 281 | const lee = { name: 'Lee' }; 282 | const kim = { name: 'Kim' }; 283 | 284 | const map = new Map([[lee, 'developer'], [kim, 'designer']]); 285 | 286 | // Map 객체는 Map.prototype의 Symbol.iterator 메서드를 상속받는 이터러블이다. 287 | console.log(Symbol.iterator in map); // true 288 | 289 | // 이터러블인 Map 객체는 for...of 문으로 순회할 수 있다. 290 | for (const entry of map) { 291 | console.log(entry); // [{name: "Lee"}, "developer"] [{name: "Kim"}, "designer"] 292 | } 293 | 294 | // 이터러블인 Map 객체는 스프레드 문법의 대상이 될 수 있다. 295 | console.log([...map]); 296 | // [[{name: "Lee"}, "developer"], [{name: "Kim"}, "designer"]] 297 | 298 | // 이터러블인 Map 객체는 배열 디스트럭처링 할당의 대상이 될 수 있다. 299 | const [a, b] = map; 300 | console.log(a, b); // [{name: "Lee"}, "developer"] [{name: "Kim"}, "designer"] 301 | ``` 302 | 303 | 304 | 305 | ### Map 객체는 이터러블이면서 동시에 이터레이터인 객체를 반환하는 메서드를 제공한다. 306 | 307 | - Map.prototype.keys : 요소키를 값으로 갖는 이터러블이면서 이터레이터인 객체를 반환 308 | - Map.prototype.values : 요소값을 값으로 갖는 이터러블이면서 이터레이터인 객체를 반환 309 | - Map.prototype.entries : 요소키와 요소값을값으로 갖는 이터러블이면서 이터레이터인 객체를 반환 310 | 311 | ```javascript 312 | const lee = { name: 'Lee' }; 313 | const kim = { name: 'Kim' }; 314 | 315 | const map = new Map([[lee, 'developer'], [kim, 'designer']]); 316 | 317 | // Map.prototype.keys는 Map 객체에서 요소키를 값으로 갖는 이터레이터를 반환한다. 318 | for (const key of map.keys()) { 319 | console.log(key); // {name: "Lee"} {name: "Kim"} 320 | } 321 | 322 | // Map.prototype.values는 Map 객체에서 요소값을 값으로 갖는 이터레이터를 반환한다. 323 | for (const value of map.values()) { 324 | console.log(value); // developer designer 325 | } 326 | 327 | // Map.prototype.entries는 Map 객체에서 요소키와 요소값을 값으로 갖는 이터레이터를 반환한다. 328 | for (const entry of map.entries()) { 329 | console.log(entry); // [{name: "Lee"}, "developer"] [{name: "Kim"}, "designer"] 330 | } 331 | ``` 332 | 333 | -------------------------------------------------------------------------------- /37장-Set/README.md: -------------------------------------------------------------------------------- 1 | # 37장 Set과 Map 2 | 3 | 메서드 요약 4 | 5 | | 이름 | 기능 | 비고 | 6 | | --------------------------- | -------------- | ----------------------------------- | 7 | | Set.prototype.size 프로퍼티 | 요소 개수 확인 | getter함수만 존재 | 8 | | Set.prototype.add | 요소 추가 | method chaining 가능 | 9 | | Set.prototype.has | 요소 존재 확인 | 반환값: boolean | 10 | | Set .prototoype.delete | 요소 삭제 | 반환값: boolean(method chaing 불가) | 11 | | Set.prototype.clear | 요소 일괄 삭제 | 반환값: undefined | 12 | | Set.prototype.forEach | 요소 순회 | | 13 | 14 | 15 | 16 | ## 37.1 Set 17 | 18 | Set 객체 : 중복되지 않는 유일한 값들의 집합 19 | 20 | 수학적 집학을 구현하기 위한 자료구조이며, 그 특성과 일치한다. 따라서 교집합, 합집합, 차집합, 여집합 등을 구현할 수 있다. 21 | 22 | 23 | 24 | 배열과 유사하지만 다음에 기술된 항목에 대해서는 **Set은 해당되지 않는다.** 25 | 26 | - 동일한 값을 중복하여 포함할 수 있다. 27 | - 요소 순서에 의미가 있다. 28 | - 인덱스로 요소에 접근할 수 있다. 29 | 30 | 31 | 32 | ### 37.1.1 Set 객체의 생성 33 | 34 | - Set 객체는 Set 생성자 함수로 생성한다. 35 | 36 | ```javascript 37 | const set = new Set(); 38 | console.log(set); // Set(0){} 39 | ``` 40 | 41 | 42 | 43 | ```javascript 44 | //1. Set 생성자 함수는 이터러블을 인수로 전달받아 Set 객체를 생성한다. 45 | const set1 = new Set([1, 2, 3, 3]); 46 | console.log(set1); // Set(3) {1, 2, 3} 47 | 48 | 49 | const set2 = new Set('hello'); 50 | console.log(set2); // Set(4) {"h", "e", "l", "o"} 51 | //2. 이터러블의 중복된 값은 Set 객체에 요소로 저장되지 않는다. 52 | 53 | 54 | // 3. 중복을 허용하지 않는 Set 객체의 특성을 활용하여 배열에서 중복된 요소를 제거할 수 있다. 55 | // 배열의 중복 요소 제거 56 | const uniq = array => array.filter((v, i, self) => self.indexOf(v) === i); 57 | console.log(uniq([2, 1, 2, 3, 4, 3, 4])); // [2, 1, 3, 4] 58 | 59 | // Set을 사용한 배열의 중복 요소 제거 60 | const uniq = array => [...new Set(array)]; 61 | console.log(uniq([2, 1, 2, 3, 4, 3, 4])); // [2, 1, 3, 4] 62 | ``` 63 | 64 | 65 | 66 | ### 37.1.2 요소 개수 확인 67 | 68 | - Set.prototype.size 프로퍼티를 사용 69 | 70 | ```javascript 71 | const { size } = new Set([1, 2, 3, 3]); 72 | console.log(size); // 3 73 | ``` 74 | 75 | size 프로퍼티는 setter 함수 없이 getter함수만 존재하는 접근자 프로퍼티. 76 | 77 | 즉, size 프로퍼티에 숫자를 할당하여 Set 객체의 요소 개수를 변경할 수 없다. 78 | 79 | ```javascript 80 | const set = new Set([1, 2, 3]); 81 | 82 | console.log(Object.getOwnPropertyDescriptor(Set.prototype, 'size')); 83 | // {set: undefined, enumerable: false, configurable: true, get: ƒ} 84 | 85 | set.size = 10; // 무시된다. 86 | console.log(set.size); // 3 87 | ``` 88 | 89 | ### 37.1.3 요소 추가 90 | 91 | - Set.prototype.add 메서드를 사용 92 | 93 | ```javascript 94 | const set = new Set(); 95 | console.log(set); // Set(0) {} 96 | 97 | // 1. Set객체에 요소 추가 : add 메서드 사용 98 | set.add(1); 99 | console.log(set); // Set(1) {1} 100 | 101 | // 2. method chaining 102 | set.add(2).add(3); 103 | console.log(set); // Set(3) {1, 2, 3} 104 | 105 | // 3. 중복된 요소를 추가할 경우 무시됨 106 | set.add(3).add(3); 107 | console.log(set); //Set(3) {1, 2, 3} 108 | ``` 109 | 110 | ```javascript 111 | 112 | // 4. 일치 비교 연산자를 사용하면 NaN과 NaN은 다르다고 평가됨. 그러나 Set 객체는 이를 같다고 평가하여 중복 추가를 허용하지 않음. +0과 -0도 같은 맥락으로 허용하지 않음. 113 | const set = new Set(); 114 | 115 | console.log(NaN === NaN); // false 116 | console.log(0 === -0); // true 117 | 118 | // NaN과 NaN을 같다고 평가하여 중복 추가를 허용하지 않는다. 119 | set.add(NaN).add(NaN); 120 | console.log(set); // Set(1) {NaN} 121 | 122 | // +0과 -0을 같다고 평가하여 중복 추가를 허용하지 않는다. 123 | set.add(0).add(-0); 124 | console.log(set); // Set(2) {NaN, 0} 125 | ``` 126 | 127 | ```javascript 128 | 129 | // 5. Set 객체는 객체나 배열과 같이 자바스크립트의 모든 값을 요소로 저장가능 130 | const set = new Set(); 131 | 132 | set 133 | .add(1) 134 | .add('a') 135 | .add(true) 136 | .add(undefined) 137 | .add(null) 138 | .add({}) 139 | .add([]); 140 | 141 | console.log(set); // Set(7) {1, "a", true, undefined, null, {}, []} 142 | ``` 143 | 144 | ### 37.1.4 요소 존재 여부 확인 145 | 146 | - Set.prototype.has 메서드 사용 147 | - 반환값 : 불리언 값. 요소의 존재 여부를 나타냄. 148 | 149 | ```javascript 150 | const set = new Set([1, 2, 3]); 151 | 152 | console.log(set.has(2)); // true 153 | console.log(set.has(4)); // false 154 | ``` 155 | 156 | ### 37.1.5 요소 삭제 157 | 158 | - Set .prototoype.delete 메서드 사용 159 | - 반환값 : 불리언 값. 삭제 성공 여부. 160 | - 인수 : 삭제하려는 요소값을 전달 161 | 162 | ```javascript 163 | const set = new Set([1, 2, 3]); 164 | 165 | // 요소 2를 삭제한다. 166 | set.delete(2); 167 | console.log(set); // Set(2) {1, 3} 168 | 169 | // 요소 1을 삭제한다. 170 | set.delete(1); 171 | console.log(set); // Set(1) {3} 172 | 173 | // 존재하지 않는 값에 대한 삭제는 무시됨 174 | set.delete(0); 175 | console.log(set); // Set(1) {3} 176 | 177 | 178 | // delete는 불리언 값을 반환하므로 method chaining이 불가능 179 | set.delete(1).delete(2); // TypeError: set.delete(...).delete is not a function 180 | ``` 181 | 182 | ### 37.1.6 요소 일괄 삭제 183 | 184 | - Set.prototype.clear 메서드 사용 185 | - 반환값 : undefined 186 | 187 | ```javascript 188 | const set = new Set([1, 2, 3]); 189 | 190 | set.clear(); 191 | console.log(set); // Set(0) {} 192 | ``` 193 | 194 | ### 37.1.7 요소 순회 195 | 196 | - Set.prototype.forEach 메서드 사용 197 | - 인수 : 콜백함수와 forEach메서드의 콜백함수내부에서 this로 사용될 객체(옵션)를 전달 198 | - 콜백함수의 인수 : 1-현재 순회중인 요소값, 2-현재 순회중인 요소값, 3-현재 순회중인 Set 객체 지체. (첫번째, 두번째 인수가 동일한 이유는 단지 Array.prototype.forEach메서드와 인터페이스를 통일하기 위함.) 199 | 200 | ```javascript 201 | const set = new Set([1, 2, 3]); 202 | 203 | set.forEach((v, v2, set) => console.log(v, v2, set)); 204 | /* 205 | 1 1 Set(3) {1, 2, 3} 206 | 2 2 Set(3) {1, 2, 3} 207 | 3 3 Set(3) {1, 2, 3} 208 | */ 209 | ``` 210 | 211 | - Set 객체는 이터러블. 따라서 하기의 동작이 가능함 212 | - for...of문으로 순회 가능. 213 | - 스프레드 문법 214 | - 배열 디스트럭쳐링 215 | 216 | ```javascript 217 | const set = new Set([1, 2, 3]); 218 | 219 | // Set 객체는 Set.prototype의 Symbol.iterator 메서드를 상속받는 이터러블이다. 220 | console.log(Symbol.iterator in set); // true 221 | 222 | // 이터러블인 Set 객체는 for...of 문으로 순회할 수 있다. 223 | for (const value of set) { 224 | console.log(value); // 1 2 3 225 | } 226 | 227 | // 이터러블인 Set 객체는 스프레드 문법의 대상이 될 수 있다. 228 | console.log([...set]); // [1, 2, 3] 229 | 230 | // 이터러블인 Set 객체는 배열 디스트럭처링 할당의 대상이 될 수 있다. 231 | const [a, ...rest] = [...set]; 232 | console.log(a, rest); // 1, [2, 3] 233 | ``` 234 | 235 | - Set 객체는 요소의 순서에 의미를 갖지 않지만, Set 객체를 순회하는 순서는 요소가 추가된 순서를 따른다. 236 | 237 | ### 37.1.8 집합연산 238 | 239 | - 교집합 240 | 241 | ```javascript 242 | // 방법1 243 | Set.prototype.intersection = function (set) { 244 | const result = new Set(); 245 | 246 | for (const value of set) { 247 | // 2개의 set의 요소가 공통되는 요소이면 교집합의 대상이다. 248 | if (this.has(value)) result.add(value); 249 | } 250 | 251 | return result; 252 | }; 253 | 254 | const setA = new Set([1, 2, 3, 4]); 255 | const setB = new Set([2, 4]); 256 | 257 | // setA와 setB의 교집합 258 | console.log(setA.intersection(setB)); // Set(2) {2, 4} 259 | // setB와 setA의 교집합 260 | console.log(setB.intersection(setA)); // Set(2) {2, 4} 261 | 262 | //방법2 263 | Set.prototype.intersection = function (set) { 264 | return new Set([...this].filter(v => set.has(v))); 265 | }; 266 | 267 | const setA = new Set([1, 2, 3, 4]); 268 | const setB = new Set([2, 4]); 269 | 270 | // setA와 setB의 교집합 271 | console.log(setA.intersection(setB)); // Set(2) {2, 4} 272 | // setB와 setA의 교집합 273 | console.log(setB.intersection(setA)); // Set(2) {2, 4} 274 | ``` 275 | 276 | - 합집합 277 | 278 | ```javascript 279 | //방법1 280 | Set.prototype.union = function (set) { 281 | // this(Set 객체)를 복사 282 | const result = new Set(this); 283 | 284 | for (const value of set) { 285 | // 합집합은 2개의 Set 객체의 모든 요소로 구성된 집합이다. 중복된 요소는 포함되지 않는다. 286 | result.add(value); 287 | } 288 | 289 | return result; 290 | }; 291 | 292 | const setA = new Set([1, 2, 3, 4]); 293 | const setB = new Set([2, 4]); 294 | 295 | // setA와 setB의 합집합 296 | console.log(setA.union(setB)); // Set(4) {1, 2, 3, 4} 297 | // setB와 setA의 합집합 298 | console.log(setB.union(setA)); // Set(4) {2, 4, 1, 3} 299 | 300 | //방법2 301 | Set.prototype.union = function (set) { 302 | return new Set([...this, ...set]); 303 | }; 304 | 305 | const setA = new Set([1, 2, 3, 4]); 306 | const setB = new Set([2, 4]); 307 | 308 | // setA와 setB의 합집합 309 | console.log(setA.union(setB)); // Set(4) {1, 2, 3, 4} 310 | // setB와 setA의 합집합 311 | console.log(setB.union(setA)); // Set(4) {2, 4, 1, 3} 312 | ``` 313 | 314 | - 차집합 315 | 316 | ```javascript 317 | //방법1 318 | Set.prototype.difference = funct\ion (set) { 319 | // this(Set 객체)를 복사 320 | const result = new Set(this); 321 | 322 | for (const value of set) { 323 | // 차집합은 어느 한쪽 집합에는 존재하지만 다른 한쪽 집합에는 존재하지 않는 요소로 구성된 집합이다. 324 | result.delete(value); 325 | } 326 | 327 | return result; 328 | }; 329 | 330 | const setA = new Set([1, 2, 3, 4]); 331 | const setB = new Set([2, 4]); 332 | 333 | // setA에 대한 setB의 차집합 334 | console.log(setA.difference(setB)); // Set(2) {1, 3} 335 | // setB에 대한 setA의 차집합 336 | console.log(setB.difference(setA)); // Set(0) {} 337 | 338 | //방법2 339 | Set.prototype.difference = function (set) { 340 | return new Set([...this].filter(v => !set.has(v))); 341 | }; 342 | 343 | const setA = new Set([1, 2, 3, 4]); 344 | const setB = new Set([2, 4]); 345 | 346 | // setA에 대한 setB의 차집합 347 | console.log(setA.difference(setB)); // Set(2) {1, 3} 348 | // setB에 대한 setA의 차집합 349 | console.log(setB.difference(setA)); // Set(0) {} 350 | ``` 351 | 352 | - 부분집합과 상위집합 353 | 354 | ```javascript 355 | //방법1 356 | // this가 subset의 상위 집합인지 확인한다. 357 | Set.prototype.isSuperset = function (subset) { 358 | for (const value of subset) { 359 | // superset의 모든 요소가 subset의 모든 요소를 포함하는지 확인 360 | if (!this.has(value)) return false; 361 | } 362 | 363 | return true; 364 | }; 365 | 366 | const setA = new Set([1, 2, 3, 4]); 367 | const setB = new Set([2, 4]); 368 | 369 | // setA가 setB의 상위 집합인지 확인한다. 370 | console.log(setA.isSuperset(setB)); // true 371 | // setB가 setA의 상위 집합인지 확인한다. 372 | console.log(setB.isSuperset(setA)); // false 373 | 374 | //방법2 375 | // this가 subset의 상위 집합인지 확인한다. 376 | Set.prototype.isSuperset = function (subset) { 377 | const supersetArr = [...this]; 378 | return [...subset].every(v => supersetArr.includes(v)); 379 | }; 380 | 381 | const setA = new Set([1, 2, 3, 4]); 382 | const setB = new Set([2, 4]); 383 | 384 | // setA가 setB의 상위 집합인지 확인한다. 385 | console.log(setA.isSuperset(setB)); // true 386 | // setB가 setA의 상위 집합인지 확인한다. 387 | console.log(setB.isSuperset(setA)); // false 388 | ``` 389 | 390 | 391 | 392 | -------------------------------------------------------------------------------- /38장-브라우저의-렌더링-과정/README.md: -------------------------------------------------------------------------------- 1 | # 38장 브라우저 렌더링 과정 2 | 3 | - 과정 : HTML, CSS, 자바스크립트로 작성된 텍스트 문서 -> 파싱(해석) -> 브라우저에 렌더링 4 | 5 | - 파싱(parsing) 6 | 7 | = 구문 분석, syntax analysis 8 | 9 | 목적 : 프로그래밍 언어의 문법에 맞게 작성된 텍스트 문서를 읽어들여 실행하기 위함 10 | 11 | 텍스트 문서의 문자열을 `토큰`(어휘 분석, lexical analysis)으로 분해한다. 12 | 13 | 토큰에 문법적 의미와 구조를 반영하여 트리 구조의 자료구조인 파스 트리(parse tree/ syntax tree)를 생성한다. 14 | 15 | 문자열을 토큰으로 분해(어휘 분석) 16 | 17 | - 렌더링(rendering) 18 | 19 | HTML, CSS, javascript 로 작성된 문서를 파싱하여 브라우저에 시각적으로 출력하는 것을 말함. 20 | 21 | **과정** 22 | 23 | 1. 브라우저는 렌더링에 필요한 `리소스`(HTML, CSS, javascript, 이미지, 폰트 파일 등 )를 요청하고 서버로부터 응답을 받는다. 24 | 2. `브라우저의 렌더링 엔진`은 서버로부터 응답된 HTML과 CSS를 파싱하여 `DOM`과 `CSSOM`을 생성하고 이들을 결합하여 `렌더 트리`를 생성한다. 25 | 3. `브라우저의 자바스크립트 엔진`은 서버로부터 응답된 자바스크립트를 `파싱`하여 `AST`(Abstract Syntax Tree)를 생성하고 `바이트코드`로 변환하여 실행한다. 이 때 자바스크립트는 `DOM API`를 통해 `DOM`이나 `CSSOM`을 변경할 수 있다. 변경된 `DOM`과 `CSSOM`은 다시 `렌더 트리`로 결합된다. 26 | 4. 렌더 트리를 기반으로 HTML 요소의 레이아웃(위치와 크기)을 계산하고 브라우저 화면에 HTML 요소를 페인팅한다. 27 | 28 | ## 38.1 요청과 응답 29 | 30 | - 브라우저의 핵심 기능 : 렌더링 31 | 32 | - 서버에 요청을 전송하기 위해 브라우저는 주소창을 제공함. 33 | 34 | - 주소창에 URL 입력 -> 엔터키 입력 -> URL의 호스트 이름이 DNS를 통해 IP주소로 변환됨 -> 이 IP주소를 갖는 서버에게 요청을 전송함 35 | 36 | - 일반적으로 서버는 루트 요청에 대해 암묵적으로 index.html을 응답하도록 기본 설정되어있다. 37 | 38 | 서버는 루트 요청에 대해 서버의 루트 폴더에 존재한느 정적 파일 index.html을 클라이언트(브라우저)로 응답함. 39 | 40 | - index.html이 아닌 다른 정적 파일을 서버에 요청하려면, URI 호스트 뒤의 path에 기술하여 서버에 요청한다. 41 | 42 | - ajax, REST API를 사용해서 동적으로 서버에 정적/동적 데이터를 요청할 수도 있다. 43 | 44 | - request, response는 개발자 도구의 Network 패널에서 확인할 수 있다. (이미 response를 받은 경우 새로고침) 45 | 46 | - HTML(index.html)을 파싱하는 도중에 외부 리소스를 로드하는 태그를 만나면 HTML파싱을 일시 중단하고 해당 리소스 파일을 서버로 요청한다. (CSS파일을 로드하는 link 태그, 이미지 파일을 로드하는 img태그, 자바스크립트를 로드하는 script태그 등) 47 | 48 | ## 38.2 HTTP 1.1과 HTTP 2.0 49 | 50 | - HTTP는 웹에서 브라우저와 서버가 통신하기 위한 프로토콜(규약)이다. 51 | - 1989년, Sir Tim Berners-Lee에 의해 HTTP, HTML, URL이 고안됨. 52 | - 1.1과 2.0의 차이 53 | - HTTP/1.1 : 다중 요청/응답이 불가능함 54 | - HTTP/2.0 : 커넥션당 여러개의 요청과 응답이 가능함. 여러 리소스의 동시 전송이 가능하므로 1.1에 비해 페이지 로드 속도가 약 50%정도 빠르다고 알려짐 55 | 56 | ## 38.3 HTML 파싱과 DOM 생성 57 | 58 | - 브라우저의 요청에 의해 서버가 응답한 HTML 문서는 문자열로 이루어진 순수한 텍스트이다. 이를 브라우저에 시각적인 픽셀로 렌더링하려면 HTML문서를 브라우저가 이해할 수 있는 자료구조(객체)인 `DOM`으로 변환하여 메모리에 저장해야한다. 59 | - 브라우저 렌더링 엔진은 HTML문서를 파싱하여 DOM을 생성한다. 60 | 61 | #### 과정 62 | 63 | 1. 서버에 존재하는 HTML파일이 브라우저의 request에 의해 response됨. 이 때 서버는 브라우저가 요청한 HTML 파일을 읽어들여 메모리에 저장한 다음, 메모리에 저장된 바이트(2진수)를 인터넷을 경유하여 응답한다. 64 | 65 | 2. 브라우저는 서버가 응답한 HTML문서를 바이트(2진수)형태로 응답받음. 응답된 바이트 형태의 HTML문서는 meta 태그의 charset 어트리뷰트에 의해 지정된 인코딩 방식을 기준으로 문자열로 변환된다. 66 | 67 | 인코딩 방식은 response header에 담겨 응답된다. 68 | 69 | 3. 문자열로 변환된 HTML 문서를 읽어들여 문법적 의미를 갖는 코드의 최소단위인 `토큰`들로 분해된다. 70 | 71 | 4. 각 토큰들을 객체로 변환하여 `노드`들을 생성한다. 72 | 73 | 토큰의 내용에 따라 문서/요소/어트리뷰트/텍스트 노드가 생성된다. 74 | 75 | 5. HTML문서는 HTML 요소들의 집합으로 이루어지며 HTML 요소는 중첩관계를 갖는다. 즉 HTML 요소의 콘텐츠 영역에는 텍스트 뿐 아니라 다른 HTML 요소도 포함될 수 있다. 요소간 중첩관계에 의해 부자관계가 형성되고, 이를 트리 자료구조로 구성한다. 76 | 77 | 즉, DOM은 HTML 문서를 파싱한 결과물이다. 78 | 79 | ![img](http://devcecy.com/wp-content/uploads/2021/08/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA-2021-08-08-%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE-12.53.03-1024x825.png) 80 | 81 | ## 38.4 CSS 파싱과 CSSOM 생성 82 | 83 | - 렌더링 엔진은 HTML을 파싱하여 DOM을 생성해나가다가, CSS를 로드하는 link 태그나 style태그를 만나면 DOM 생성을 일시 중단한다. 그리고 link 태그의 href 어트리뷰트에 지정된 CSS파일을 서버에 요청하여 로드한 CSS파일이나 style 태그 내의 CSS를 HTML과 동일한 파싱과정(바이트-문자-토큰-노드-CSSOM)을 거치며 해석하여 CSSOM을 생성한다. 84 | - 이후 CSS파싱이 완료되면 HTML파싱이 중단된 지점부터 다시 HTML을 파싱하기 시작하여 DOM 생성을 재개한다. 85 | - CSSOM은 CSS의 상속을 반영한다. 86 | - ![img](http://devcecy.com/wp-content/uploads/2021/08/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA-2021-08-08-%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE-1.14.10-1024x460.png) 87 | 88 | ## 38.5 렌더 트리 생성 89 | 90 | - 렌더링 엔진은 서버로부터 응답된 HTML과 CSS를 파싱하여 DOM과 CSSOM을 생성한다. 그리고 이것들은 렌더트리로 결합된다. 91 | 92 | - 렌더 트리는 렌더링을 위한 트리 구조의 자료구조로, 브라우저 화면에 렌더링되는 노드만으로 구성된다. 93 | 94 | - 렌더 트리가, 완성되면 각 HTML 요소의 레이아웃을 계산하는데 사용되며, 브라우저 화면에 픽셀을 렌더링하는 `페인팅` 처리에 입력된다. 95 | 96 | - 브라우저의 렌더링 과정은 반복해서 실행될 수 있다. 97 | 98 | ``` 99 | - 자바스크립트에 의한 노드 추가 또는 삭제 100 | - 브라우저 창의 리사이징에 의한 뷰포트 크기 변경 101 | - HTML 요소의 레이아웃(위치, 크기)에 변경을 발생시키는 width/height, margin, padding, border, display, position, top/right/bottom/left등의 스타일 변경 102 | ``` 103 | 104 | - 레이아웃 계산과 페인팅을 다시 실행하는 `리렌더링`은 비용이 많이 드는, 성능에 악영향을 준다. 105 | 106 | ![img](http://devcecy.com/wp-content/uploads/2021/08/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA-2021-08-08-%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE-1.25.35-1024x578.png) 107 | 108 | - ![img](http://devcecy.com/wp-content/uploads/2021/08/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA-2021-08-08-%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE-1.28.12-1024x238.png) 109 | 110 | ## 38.6 자바스크립트 파싱과 실행 111 | 112 | - HTML 문서를 파싱한 결과물로서 생성된 DOM은 HTML 문서의 구조와 정보 뿐아니라 HTML요소와 스타일 등을 변경할 수 있는 프로그래밍 인터페이스로서 DOM API를 제공한다. ->39장 DOM 113 | 114 | - 렌더링 엔진은 HTML을 한줄씩 순차적으로 파싱하여 DOM을 생성함 115 | 116 | - 만약 자바스크립트 파일을 로드하는 script태그나 자바스크립트 코드를 콘텐츠로 담은 script 태그를 만나면 DOM 생성을 일시 중단한다. 117 | - 그리고 script태그의 src 어트리뷰트에 정의된 자바스크립트 파일을 서버에 요청, 로드한 자바스크립트 파일이나 script 태그 내의 자바스크립트 코드를 파싱하기 위해 **자바스크립트 엔진에 제어권을 넘긴다.** 이후 자바스크립트 코드의 파싱과 실행이 종료되면 **렌더링 엔진이 제어권을 가지며** 다시 DOM 생성을 재개한다. 118 | 119 | - 자바스크립트의 파싱과 실행은 자바스크립트 엔진이 처리한다. (브라우저의 렌더링 엔진이 아님) 120 | 121 | - 자바스크립트 엔진이 자바스크립트 코드를 `파싱`하여 CPU가 이해할 수 있는 `저수준 언어`로 변환하고 실행한다. 122 | - 자바스크립트 엔진의 종류 : 구글 크롬, Node.js의 V8, 파이어폭스의 SpiderMonkey, 사파리의 JavaScriptCore 등. (모두 ECMAScript 사양을 준수함) 123 | 124 | - 렌더링 엔진으로부터 제어권을 넘겨받은 자바스크립트 엔진은 파싱을 시작한다. 125 | 126 | - 자바스크립트 엔진은 자바스크립트를 해석하여 AST(Abstract Syntax Tree, 추상적 구문트리)를 생성한다. 이를 기반으로 인터프리터가 실행할 수 있는 중간코드인 `바이트코드`를 생성하여 실행한다. 127 | 128 | #### 토크나이징 129 | 130 | - 단순한 문자열인 자바스크립트 소스코드를 어휘 분석(lexical analysis)하여 문법적 의미를 갖는 최소 단위인 토큰들로 분해한다. 131 | - 렉싱(lexing)이라고도 부르기도 하짐나 토크나이징과 미묘한 차이가 있음 132 | 133 | #### 파싱 134 | 135 | - 토큰들의 집합을 구문 분석(syntactic analysis)하여 AST를 생성함. 136 | - AST는 토큰에 문법적 의미와 구조를 반영한 트리구조의 자료구조. 137 | - AST는 인터프리터나 컴파일러만이 사용하는 것은 아니며, TypeScript, Babel, Prettier같은 `트랜스파일러`를 구현할 수도 있다. 138 | 139 | ![img](http://devcecy.com/wp-content/uploads/2021/08/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA-2021-08-08-%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE-2.05.43-1024x500.png) 140 | 141 | #### 바이트코드의 생성과 실행 142 | 143 | - 파싱의 결과물로 생성된 AST는 인터프리터가 실행할 수 있는 중간코드인 `바이트코드`로 변환되고, 인터프리터에 의해 실행된다. 144 | 145 | ## 38.7 리플로우와 리페인트 146 | 147 | - 자바스크립트 코드에 DOM이나 CSSOM을 변경하는 DOM API가 사용된 경우 DOM이나 CSSOM이 변경된다. 148 | 149 | - 이 때 변경된 DOM과 CSSOM은 다시 렌더트리로 결합되고, 변경된 렌더트리를 기반으로 `레이아웃`과 `페인트`과정을 거쳐 브라우저의 화면에 **다시 렌더링한다.** 이를 `리플로우`, `리페인트`라고 한다. 150 | 151 | - 리플로우 : 레이아웃 계산을 다시 함. 노드 추가/삭제, 요소의 크기/위치 변경, 윈도우 리사이징 등. 152 | 153 | - 리페인트 : 재결합된 렌더 트리를 기반으로 다시 페인트를 함 154 | 155 | 리플로우와 리페인트가 반드시 순차적으로 동시에 실행되는 것은 아님. 레이아웃에 영향이 없는 변경은 리페인트만 실행됨 156 | 157 | ## 38.8 자바스크립트 파싱에 의한 HTML 파싱 중단 158 | 159 | - 렌더링 엔진과 자바스크립트 엔진은 직렬적으로 파싱을 수행한다. 160 | 161 | - 브라우저는 동기적(syunchronous)으로, HTML,CSS,javascript를 파싱하고 실행한다. 162 | 163 | script 태그의 위치에 따라 HTML파싱이 블로킹되어 DOM 생성이 지연될 수 있으므로, script 태그의 위치는 중요하다. 164 | 165 | - 주의사항 166 | 167 | - DOM이 완성되지 않은 상태에서 자바스크립트가 DOM을 조작하면 에러가 발생할 수 있다. 168 | - 자바스크립트 로딩/파싱/실행으로 인해 HTML 요소들의 렌더링에 지장받는 일이 발생한다면 페이지 로딩 시간이 연장된다. 169 | 170 | - 권고 171 | 172 | - body 요소의 가장 아래에 자바스크립트를 위치시키자. 173 | - 자바스크립트가 실행될 시점에는 이미 렌더링 엔진이 HTML 요소를 모두 파싱하여 DOM 생성을 완료한 이후이다. 즉, DOM이 완성되기 전에 자바스크립트가 DOM을 조작할 일이 없다. 174 | - DOM 생성이 완료된 후 자바스크립트가 실행되기 때문에 페이지 로딩 시간이 단축된다. 175 | 176 | ## 38.9 script 태그의 async/defer 어트리뷰트 177 | 178 | - 배경 179 | - DOM 생성이 중단되는 문제를 근본적으로 해결하기 위해 HTML5부터 script 태그에 async와 defer 어트리뷰트가 추가되었다. 180 | - 공통점 181 | - src 어트리뷰트를 통해 외부 자바스크립트 파일을 로드하는 경우에만 사용할 수 있다. 즉, src 어트리뷰트가 없는 인라인 자바스크립트에는 사용불가 182 | - HTML 파싱과 외부 자바크스립트 파일의 로드가 비동기적으로 동시에 진행된다. 183 | 184 | #### async 어트리뷰트 185 | 186 | - 자바스크립트의 파싱과 실행은 자바스크립트 파일의 로드가 완료된 직후 진행됨. 이 때 HTML 파싱이 중단됨. 187 | - ![img](http://devcecy.com/wp-content/uploads/2021/08/KakaoTalk_Photo_2021-08-08-15-19-54-1024x153.png) 188 | 189 | - 여러개의 script에 async 어트리뷰트를 지정하면, script 태그의 순서와는 상관없이 **로드가 완료된 자바스크립트부터** 먼저 실행되므로 순서가 보장되지 않는다. 따라서 순서 보장이 필요한 script 태그에는 async 어트리뷰트를 쓰면 안됨 190 | 191 | #### defer 어트리뷰트 192 | 193 | - 자바스크립트의 파싱과 실행은 자바스크립트 파일의 로드가 완료된 직후, 즉 DOM 생성이 완료된 직후(DOMContentLoaded이벤트가 발생)에 진행된다. 따라서 DOM 생성이 완료된 이후 실행되어야할 자바스크립트에 유용하다. 194 | - ![img](http://devcecy.com/wp-content/uploads/2021/08/KakaoTalk_Photo_2021-08-08-15-20-00-1024x206.png) 195 | 196 | 197 | 198 | 199 | 200 | - 이미지 출처 : http://devcecy.com/%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80%EB%8A%94-%EC%96%B4%EB%96%BB%EA%B2%8C-%EB%A0%8C%EB%8D%94%EB%A7%81-%EB%90%A0%EA%B9%8C/ 201 | -------------------------------------------------------------------------------- /39장-DOM(39.5~)/README.md: -------------------------------------------------------------------------------- 1 | # 39장 DOM 2 | 3 | ### 39.5 요소 노드의 텍스트 조작 4 | 5 | #### 39.5.1 nodeValue 6 | 7 | --- 8 | 9 | - Node.prototype.nodeValue 프로퍼티는 setter와 getter 모두 존재하는 접근자 프로퍼티다. 10 | 11 | - 노드 객체의 nodeValue 프로퍼티를 참조하면 노드 객체의 값을 반환한다. 여기서 노드 객체의 값이란 텍스트 노드의 텍스트다. 12 | 13 | ```html 14 | 15 | 16 |
Hello
17 | 18 | 30 | 31 | ``` 32 | 33 |
34 | 35 | #### 39.5.2 textContent 36 | 37 | --- 38 | 39 | - Node.prototype.textContent 프로퍼티는 setter와 getter 모두 존재하는 접근자 프로퍼티로서 요소 노드의 텍스트와 모든 자손 노드의 텍스트를 모두 취득하거나 변경한다. 40 | - 요소 노드의 textContent 프로퍼티를 참조하면 요소 노드의 콘텐츠 영역 내의 텍스트를 모두 반환한다. 41 | 42 | ```html 43 | 44 | 45 |
Hello world!
46 | 47 | 55 | 56 | ``` 57 | 58 |
59 | 60 | ### 39.6 DOM 조작 61 | 62 | --- 63 | 64 | - DOM 조작은 새로운 노드를 생성하여 DOM에 추가하거나 기존 노드를 삭제 또는 교체하는 것을 말한다. 65 | - DOM 조작에 의해 DOM에 새로운 노드가 추가되거나 삭제되면 리플로우와 리페인트가 발생하는 원인이 디므로 성능에 영향을 준다. 66 | 67 | 68 | #### 39.6.1 innerHTML 69 | 70 | --- 71 | 72 | - Element.prototype.innerHTML 프로퍼티는 setter와 getter 모두 존재하는 접근자 프로퍼티로서 요소 노드의 HTML 마크업을 취득하거나 변경한다. 73 | 74 | - 아래처럼 innerHTML 프로퍼티를 사용하면 HTML 마크업 문자열로 간단히 DOM 조작이 가능하다. 75 | 76 | ```html 77 | 78 | 79 | 82 | 83 | 95 | 96 | ``` 97 | 98 | - innerHTML 프로퍼티를 사용한 DOM 조작은 구현이 간단하고 직관적이라는 장점이 있지만 크로스 사이트 스크립트 공격에 취약하다는 단점이 있다. 99 | 100 |
101 | 102 | #### 39.6.2 insertAdjacentHTML 메서드 103 | 104 | --- 105 | 106 | - Element.prototype.insertAdjacentHTML(position, DOMString) 메서드는 기존 요소를 제거하지 않으면서 위치를 지정해 새로운 요소를 삽입한다. 107 | 108 | - ```html 109 | 110 | 111 | 112 |
113 | 114 | text 115 | 116 |
117 | 118 | 119 | 127 | 128 | ``` 129 | 130 |
131 | 132 | #### 39.6.3 노드 생성과 추가 133 | 134 | --- 135 | 136 | - DOM은 노드를 직접 생성/삽입/삭제/치환하는 메서드도 제공한다. 137 | 138 | - 아래의 과정을 통해서 노드의 생성과 추가가 이루어진다. (추후 설명 추가 예정) 139 | 140 | - 요소 노드 생성 141 | - 텍스트 노드 생성 142 | - 텍스트 노드를 요소 노드의 자식 노드로 추가 143 | - 요소 노드를 DOM에 추가 144 | 145 | ```html 146 | 147 | 148 | 151 | 152 | 167 | 168 | ``` 169 | 170 |
171 | 172 | #### 39.6.4 복수의 노드 생성과 추가 173 | 174 | --- 175 | 176 | - DOM을 여러번 변경하는 것은 각각 리플로우와 리페인팅을 야기시킨다. 이를 피하기 위해 컨테이너 요소를 사용할 수 있다. 177 | 178 | ```html 179 | 180 | 181 | 182 | 183 | 206 | 207 | ``` 208 | 209 | - 먼저 DocumentFragment 노드를 생성하고 DOM에 추가할 요소 노드를 생성하여 DocumentFragment 노드에 자식 노드로 추가한 다음, DocumentFragment 노드를 기존 DOM에 추가한다. 210 | - 이때, 실제로 DOM 변경이 발생하는 것은 한 번뿐이며 리플로우와 리페인트도 한 번만 실행한다. 211 | - 따라서, 여러 개의 요소 노드를 DOM에 추가하는 경우 DocumentFragment 노드를 사용하는 것이 더 효율적이다. 212 | 213 |
214 | 215 | #### 39.6.5 노드 삽입 216 | 217 | --- 218 | 219 | **마지막 노드로 추가** 220 | 221 | - Node.prototype.appendChild 메서드는 인수로 전달받은 노드를 자신을 호출한 노드의 마지막 자식 노드로 DOM에 추가한다. 222 | - 단, 노드를 추가할 위치를 지정할 수 없고 언제나 마지막 자식 노드로 추가한다. 223 | 224 | ```html 225 | 226 | 227 | 231 | 232 | 242 | 243 | ``` 244 | 245 | 246 | 247 |
248 | 249 | **지정한 위치에 노드 삽입** 250 | 251 | - Node.prototype.insertBefore(newNode, childNode) 메서드는 첫 번째 인수로 전달받은 노드를 두 번째 인수로 전달받은 노드 앞에 삽입한다. 252 | 253 | ```html 254 | 255 | 256 | 260 | 261 | 274 | 275 | ``` 276 | 277 |
278 | 279 | #### 39.6.6 노드 이동 280 | 281 | --- 282 | 283 | - DOM에 이미 존재하는 노드를 appendChild 또는 insertBefore 메서드를 사용하여 DOM에 다시 추가하면 현재 위치에서 노드를 제거하고 새로운 위치에 노드를 추가한다. 즉, 노드가 이동한다. 284 | 285 | ```html 286 | 287 | 288 | 293 | 294 | 307 | 308 | ``` 309 | 310 |
311 | 312 | #### 39.6.7 노드 복사 313 | 314 | --- 315 | 316 | - Node.prototype.cloneNode([deep: true | false]) 메서드는 노드의 사본을 생성하여 반환한다. 317 | 318 | ```html 319 | 320 | 321 | 324 | 325 | 341 | 342 | ``` 343 | 344 |
345 | 346 | #### 39.6.8 노드 교체 347 | 348 | --- 349 | 350 | - Node.prototype.replaceChild(newChild, oldChild) 메서드는 자신을 호출한 노드의 자식 노드를 다른 노드로 교체한다. 351 | 352 | ```html 353 | 354 | 355 | 358 | 359 | 369 | 370 | ``` 371 | 372 |
373 | 374 | #### 39.6.9 노드 삭제 375 | 376 | --- 377 | 378 | - Node.prototype.removeChild(child) 메서드는 child 매개변수에 인수로 전달한 노드를 DOM에서 삭제한다. 379 | 380 | ```html 381 | 382 | 383 | 387 | 388 | 394 | 395 | ``` 396 | 397 |
398 | 399 | ### 39.7 어트리뷰트 400 | 401 | #### 39.7.1 어트리뷰트 노드와 attributes 프로퍼티 402 | 403 | --- 404 | 405 | - HTML 문서의 구성 요소인 HTML 요소는 여러 개의 어트리뷰트를 가질 수 있다. 406 | - HTML 요소의 동작을 제어하기 위한 추가적인 정보를 제공한다. 407 | - 대부분의 어트리뷰트는 모든 HTML 요소에서 사용될 수 있지만, type, value, checked 어트리뷰트는 input 요소에만 사용할 수 있는 경우도 있다. 408 | - HTML 문서가 파싱될 때 HTML 요소의 어트리뷰트는 어트리뷰트 노드로 변환되어 요소 노드와 연결된다. 409 | 410 |
411 | 412 | #### 39.7.2 HTML 어트리뷰트 조작 413 | 414 | --- 415 | 416 | - HTML 어트리뷰트 값을 참조하려면 Element.prototype.getAttribute(attributeName) 메서드를 사용하고, HTML 어트리뷰트 값을 변경하려면 Element.prototype.setAttribute(attributeName, attributeValue) 메서드를 사용한다. 417 | 418 | ```html 419 | 420 | 421 | 422 | 433 | 434 | 435 | ``` 436 | 437 | 438 | 439 |
440 | 441 | -------------------------------------------------------------------------------- /39장-DOM(~39.4)/README.md: -------------------------------------------------------------------------------- 1 | # 39장 DOM 2 | 3 | - DOM은 HTML문서의 계층적 구조와 정보 그리고 여러 메서드들로 이루어진 트리 자료주조이다. 4 | 5 | ## 39.1 노드 6 | 7 |
8 | 9 | ### HTML요소 10 | - HTML 요소(HTML Element)는 HTML 문서를 구성하는 개별적인 요소를 의미한다. 11 | - 즉, HTML 문서에서 볼 수 있는 하나하나의 태그 요소들을 HTML 요소라고 부른다. 12 | - HTML요소는 시작 및 종료 태그, 어트리뷰트, 컨텐츠로 이루어진다. 13 | - 이때, 컨텐츠는 텍스트 또는 다른 HTML 요소가 될 수 있다. 14 | 15 | ```html 16 |
Hey
17 | 18 | 19 | 20 | 21 | 22 | 23 | ``` 24 | 25 |
26 | 27 | ### 노드 객체 28 | - 렌더링 엔진은 HTML 요소를 파싱해 노드 객체를 생성한다. 29 | - 이때, HTML 요소, 어트리뷰트, 텍스트 컨텐츠 등은 각각 타입에 맞는 노드 객체로 변환된다. 30 | - 예를들어 요소 노드 객체, 어트리뷰트 노드 객체, 텍스트 노드 객체 31 | - 앞서 이야기했듯 HTML 요소의 컨텐츠는 또 다른 HTML 요소가 될 수 있다. 즉, HTML 문서는 각 HTML요소들의 계층적 구조로 이루어진다. 32 | - 따라서 이를 표현하는 노드 객체 또한 계층적 구조를 가지며 루트 노드를 기준으로 트리가 형성된다. 33 | - 이렇게 생성된 노드 객체의 트리를 DOM이라고 부른다. 34 | 35 |
36 | 37 | ### 노드 객체의 타입 38 | 39 | - 노드 객체에는 종류가 있고 각각 종류에 따라 다른 상속 구조를 갖는다. 40 | - 노드 객체는 총 12개의 종류가 있다. 41 | - 대표적인 노드 타입은 다음과 같다. 42 | 1. 문서 노드(document node) 43 | - 문서노드는 DOM 트리의 최상위에 존재하는 루트 노드이다. 44 | - 실제로 document객체가 이 문서 노드에 해당한다. 45 | - document객체는 전역 객체인 window의 document 프로퍼티에 바인딩 되어있다. 46 | - HTML 문서당 document객체는 유일하다. 47 | 2. 요소 노드(element node) 48 | - HTML 요소를 가리키는 객체이다. 49 | 3. 어트리뷰트 노드(attribute node) 50 | - HTML 요소의 어트리뷰트를 나타내는 객체 51 | - 어트리뷰트 노드는 해당 어트리뷰트가 지정된 HTML 요소의 요소 노드와 연결되어 있다. 52 | 4. 텍스트 노드 53 | - HTML 요소의 텍스트를 가리키는 객체 54 | - 텍스트 노드는 항상 트리의 리프 노드이다. 55 | - 위 4가지 말고도 주석을 위한 Comment노드, DOCTYPE을 위한 DocumentType노드, DocumentFragment 노드, 공백 텍스트 노드 등이 존재한다. 56 | 57 |
58 | 59 | ### 노드 객체의 상속 구조 60 | - DOM을 구성하는 노드 객체는 자신의 구조와 정보를 제어할 수 있는 DOM API를 사용할 수 있다. 61 | - 이러한 API는 자바스크립트의 프로토타입에 의해 메서드의 형태로 상속된다. 62 | - 노드 객체의 상속 구조는 다음과 같다. 63 |
64 | ![노드객체구조](https://user-images.githubusercontent.com/57767891/139620868-a2dcc469-1b34-40e6-9493-bff3d27200dd.png) 65 | - 모든 노드는 Object, EventTarget, Node 객체를 상속받는다. 66 | - 이벤트 관련 기능은 EventTarget으로부터, 트리 탐색이나 노드 정보 제공 등의 기능은 Node로부터 상속 받는다. 67 | - 문서노드(document)는 Document, HTMLDocument를, 어트리뷰트 노드는 Attr을 텍스트 노드는 CharacterData를 각각 상속받는다. 68 | - 요소 노드는 Element와 HTMLElement를 상속 받는데 각 태그 종류에 따라 HTMLBodyElement, HTMLDivElement 등을 상속받는다. 69 | - style프로퍼티와 같이 공통의 정보는 HTMLElement로부터 각 요소의 고유 정보는 각 인터페이스로부터 제공받는다. 70 | 71 |
72 | 73 | ## 39.2 요소 노드 취득 74 | 75 |
76 | 77 | - HTML요소를 동적으로 조작하기위해 특정 요소 노드를 취득할 수 있다. 78 | 79 |
80 | 81 | ### id를 이용한 요소 노드 취득 82 | - Document.prototype.getElementById를 사용 83 | - 인수로 전달한 문자열을 id 어트리뷰트로 갖는 첫번째 요소를 반환한다. 84 | - 해당하는 요소가 존재하지 않으면 null을 반환한다. 85 | - Document.prototype의 메서드이므로 문서노드인 document 객체를 통해 호출해야한다. 86 | - HTML요소에 id 어트리뷰트를 부여하면 id값과 동일한 이름의 전역변수에 해당 노드 객체가 할당된다. 87 | - id값에 식별자 규칙을 준수하지 않는 문자열이 들어가는 경우는 해당하지 않는다. 88 | - 이미 id값과 동일한 이름의 전역 변수가 있어도 해당하지 않음. 89 | 90 |
91 | 92 | ### 태그 이름을 이용한 요소 노드 취득 93 | - Document.prototype(or Element.prototype).getElementsByTagName 94 | - 인수로 전달한 태그 이름을 갖는 모든 요소 노드를 HTMLCollection객체로 반환한다. 95 | - HTMLCollection은 유사배열이면서 이터러블이다. 96 | - 모든 요소 노드를 취득하려면 인수로 '*'를 전달한다. 97 | - Document.prototype.getElementsByTagName은 문서 전체를 탐색, Element.prototype.getElementsByTagName은 특정 엘리먼트의 자손을 탐색한다. 98 | - 해당하는 요소가 없다면 빈 HTMLCollection객체를 반환한다. 99 | 100 |
101 | 102 | ### class를 이용한 요소 노드 취득 103 | - Document.prototype(or Element.prototype).getElementsByClassName 104 | - 인수로 전달한 class 어트리 뷰트를 갖는 모든 요소 노드를 HTMLCollection 객체로 반환한다. 105 | - 이하 성질은 getElementsByTagName과 동일하다. 106 | 107 |
108 | 109 | ### CSS 선택자를 이용한 요소 노드 취득 110 | - Document.prototype(or Element.prototype).querySelector 사용 111 | - 인수로 전달한 CSS 선택자를 만족시키는 요소 노드 중 첫번째를 반환한다. 112 | - 만족하는 노드가 없다면 null을 반환한다. 113 | - 인수로 전달한 CSS 선택자가 문법에 맞지 않는 경우 DOMException에러가 발생한다. 114 | 115 |
116 | 117 | - Document.prototype(or Element.prototype).querySelectorAll을 사용 할 수도 있다. 118 | - 이때는 인수로 전달한 CSS 선택자를 만족시키는 모든 요소 노드를 NodeList 객체로 반환한다. 119 | - NodeList객체도 HTMLCollection과 마찬가지로 유사배열이면서 이터러블이다. 120 | - 만족하는 요소가 없다면 빈 NodeList를 반환한다. 121 | - 인수로 전달한 CSS 선택자가 문법에 맞지 않는 경우 DOMException에러가 발생한다. 122 | 123 |
124 | 125 | ### 특정 요소 노드를 취득할 수 있는지 확인 126 | - Element.prototype.matches 메서드 사용 127 | - 인수로 전달한 CSS 선택자를 통해 특정 요소 노드를 취득할 수 있는지 확인하고 결과를 boolean값으로 반환한다. 128 | 129 |
130 | 131 | ### HTMLCollection과 NodeList 132 | - HTMLCollection과 NodeList 모두 DOM API가 여러개의 결과 값을 반환하기 위해 사용하는 DOM 컬렉션 객체이다. 133 | - HTMLCollection과 NodeList 모두 유사 배열이면서 이터러블이다. 134 | - HTMLCollection은 항상 live 객체로 동작한다. 135 | - NodeList의 경우 대부분 non-live 객체로 동작하지만 childNodes 프로퍼티에 의해 참조되는 NodeList의 경우 live 객체로 동작한다. 136 | 137 |
138 | 139 | > live 객체 140 | > 특정 DOM 컬렉션이 각 노드 객체의 상태 변화를 실시간으로 반영할 때 이를 live 객체라 부른다. 141 | 142 |
143 | 144 | ### 39.3 노드 탐색 145 | 146 | - Node와 Elementsms DOM 트리를 탐색 할 수 있는 접근자 프로퍼티를 제공한다. 147 | 148 | #### 1. 자식 노드 탐색 149 | - Node.prototype.childNodes 150 | - 모든 자식 모드를 NodeList로 반환 151 | - 텍스트 및 빈 텍스트 노드도 포함한다. 152 | - Element.prototype.children 153 | - 자식 노드 중 요소 노드만 HTMLCollection에 담아 반환 154 | - Node.prototype.fitstChild 155 | - 첫 번째 자식 노드 반환 156 | - 텍스트 노드일 수도 있다. 157 | - Node.prototype.lastChild 158 | - 마지막 자식 노드 반환 159 | - 텍스트 노드일 수 있다. 160 | - Element.prototype.firstElementChild 161 | - 첫 번째 자식 '요소' 노드를 반환 162 | - Element.ptototype.lastElementChild 163 | - 마지막 자식 '요소' 노드를 반환 164 | - Node.prototype.hasChildNodes() 165 | - 자식노드의 존재여부를 boolean으로 반환 166 | - 이때, 텍스트노드도 포함해 조회한다. 167 | - Element.prototype.childElementCount 168 | - 자식 '요소' 노드의 수를 반환 169 | 170 |
171 | 172 | #### 2. 부모 노드 탐색 173 | - Node.prototype.parentNode 174 | - 부모 요소 노드를 반환 175 | 176 | #### 3. 형제 노드 탐색 177 | - Node.prototype.previousSibling 178 | - 부모가 같은 형제 노드중 자신의 이전 형제 노드를 반환 179 | - 텍스트 노드일 수 있다. 180 | - 해당하는 노드가 없으면 null을 반환 181 | - Node.prototype.nextSibling 182 | - 부모가 같은 형제 노드 중에서 자신의 다음 형제 노드를 반환 183 | - 텍스트 노드일 수 있다. 184 | - 해당하는 노드가 없으면 null을 반환 185 | - Element.prototype.previousElementSibling 186 | - 부모가 같은 형제 노드중 자신의 이전 형제 '요소' 노드를 반환 187 | - 해당하는 노드가 없으면 null을 반환 188 | - Element.prototype.nextElementSibling 189 | - 부모가 같은 형제 노드중 자신의 다음 형제 '요소' 노드를 반환 190 | - 해당하는 노드가 없으면 null을 반환 191 | 192 |
193 | 194 | ### 39.4 노드 정보 취득 195 | 196 | - Node.prototype.nodeType 197 | - 노드의 타입을 나타내는 상수를 반환 198 | - 노드 타입 상수는 Node에 정의 되어있다. 199 | ex) 200 | Node.ELEMENT_NODE === 1 201 | Node.TEXT_NODE === 3 202 | Node.DOCUMENT === 9 203 | ... 204 | - Node.prototype.nodeName 205 | - 노드의 이름을 문자열로 반환 206 | ex) 207 | 요소 노드 - 대문자 문자열로 태그 이름 반환 ("UL, "LI", ...) 208 | 텍스트 노드 - "#text" 반환 209 | 문서 노드 - "#document" 반환 -------------------------------------------------------------------------------- /41장-타이머/README.md: -------------------------------------------------------------------------------- 1 | # 41장 타이머 2 | 3 | ## 41.1 호출 스케줄링 4 | 5 | - 함수를 명시적으로 호출하면 함수가 즉시 실행된다. 만약 함수를 명시적으로 호출하지 않고 일정 시간이 경과된 이후에 호출되도록 함수 호출을 예약하려면 타이머 함수를 사용한다. 이를 **호출 스케줄링**이라 한다. 6 | - 자바스크립트는 타이머 함수 setTimeout, setInterval, 타이머를 제거할 수 있는 clearTimeout, clearInterval을 제공한다. 타이머 함수는 ECMAScript에 정의된 빌트인 함수가 아니다. 하지만 브라우저 환경, Node.js 환경에서 모두 전역 객체의 메서드로 타이머 함수를 제공한다. 7 | - 즉, 타이머 함수는 호스트 객체다. 8 | - 자바스크립트 엔진은 단 하나의 실행 컨텍스트 스택을 갖기 때문에 두 가지 이상의 태스크를 동시에 실행할 수 없다. 즉, 자바스크립트 엔진은 **싱글 스레드**로 동작한다. 이런 이유로 타이머 함수 setTimeout과 setInterval은 **비동기 처리 방식으로 동작한**다. 9 | 10 | ## 41.2 타이머 함수 11 | 12 | ### 41.2.1 setTimeout / clearTimeout 13 | 14 | - setTimeout 함수는 인수로 전달받은 시간으로 단 한 번 동작하는 타이머를 생성한다. 이후 타이머가 만료되면 첫 번째 인수로 전달받은 콜백 함수가 호출 된다. 즉 setTimeout 함수의 콜백 함수는 두 번째 인수로 전달받은 시간 이후 단 한 번 실행되도록 호출 스케줄링된다. 15 | 16 | ```js 17 | // 1초(1000ms) 후 타이머가 만료되면 콜백 함수가 호출된다. 18 | setTimeout(() => console.log("Hi!"), 1000); 19 | 20 | // 1초(1000ms) 후 타이머가 만료되면 콜백 함수가 호출된다. 21 | // 이때 콜백 함수에 'Lee'가 인수로 전달된다. 22 | setTimeout((name) => console.log(`Hi! ${name}.`), 1000, "Lee"); 23 | 24 | // 두 번째 인수(delay)를 생략하면 기본값 0이 지정된다. 25 | setTimeout(() => console.log("Hello!")); 26 | ``` 27 | 28 | - setTimeout 함수는 생성된 타이머를 식별할 수 있는 고유한 타이머 id를 반환한다. 29 | - setTimeout 함수가 반환한 타이머 id를 clearTimeout 함수의 인수로 전달하여 타이머를 취소할 수 있다. 즉, clearTimeout 함수는 호출 스케줄링을 취소한다. 30 | 31 | ```js 32 | // 1초(1000ms) 후 타이머가 만료되면 콜백 함수가 호출된다. 33 | // setTimeout 함수는 생성된 타이머를 식별할 수 있는 고유한 타이머 id를 반환한다. 34 | const timerId = setTimeout(() => console.log("Hi!"), 1000); 35 | 36 | // setTimeout 함수가 반환한 타이머 id를 clearTimeout 함수의 인수로 전달하여 타이머를 37 | // 취소한다. 타이머가 취소되면 setTimeout 함수의 콜백 함수가 실행되지 않는다. 38 | clearTimeout(timerId); 39 | ``` 40 | 41 | ### 41.2.2 setInterval / clearInterval 42 | 43 | - setInterval 함수는 두 번째 인수로 전달받은 시간으로 반복 동작하는 타이머를 생성한다. 44 | - 이후 타이머가 만료될 때마다 첫 번째 인수로 전달받은 콜백 함수가 반복 호출된다. 45 | - setInterval 함수는 생성된 타이머를 식별할 수 있는 고유한 타이머 id를 반환한다. 46 | - 타이머 id를 clearInterval 함수의 인수로 전달하여 타이머를 취소할 수 있다. 47 | 48 | ```js 49 | let count = 1; 50 | 51 | // 1초(1000ms) 후 타이머가 만료될 때마다 콜백 함수가 호출된다. 52 | // setInterval 함수는 생성된 타이머를 식별할 수 있는 고유한 타이머 id를 반환한다. 53 | const timeoutId = setInterval(() => { 54 | console.log(count); // 1 2 3 4 5 55 | // count가 5이면 setInterval 함수가 반환한 타이머 id를 clearInterval 함수의 56 | // 인수로 전달하여 타이머를 취소한다. 타이머가 취소되면 setInterval 함수의 콜백 함수가 57 | // 실행되지 않는다. 58 | if (count++ === 5) clearInterval(timeoutId); 59 | }, 1000); 60 | ``` 61 | 62 | ## 41.3 디바운스와 스로틀 63 | 64 | - scroll, resize, input, mousemove 같은 이벤트는 짧은 시간 간격으로 연속해서 발생한다. 이러한 이벤트에 바인딩한 이벤트 핸들러는 과도하게 호출되어 성능 문제를 일으킬 수 있다. 디바운스와 스로틀은 이런 이벤트를 처리할 때 매우 유용하다. 디바운스와 스로틀의 구현에는 타이머 함수가 사용된다. 65 | 66 | ### 41.3.1 디바운스 67 | 68 | - 디바운스는 짧은 시간 간격으로 이벤트가 연속해서 발생하면 이벤트 핸들러를 호출하지 않다가 일정 시간이 경과한 이후에는 이벤트 핸들러가 한 번만 호출되도록 한다. 69 | - 즉, 디바운스는 짧은 시간 간격으로 발생하는 이벤트를 그룹화해서 마지막에 한 번만 이벤트 핸들러가 호출되도록 한다. 70 | 71 | ```html 72 | 73 | 74 | 75 | 76 |
77 | 100 | 101 | 102 | ``` 103 | 104 | - input 이벤트는 사용자가 텍스트 입력 필드에 값을 입력할 때마다 연속해서 발생한다. 105 | - 이 때 debounce를 사용하여 debounce 함수에 두 번째 인수로 전달한 시간보다 짧은 간격으로 이벤트가 발생하면 이전 타이머를 취소하고 새로운 타이머를 재설정한다. 106 | - 디바운스는 resize 이벤트, input, 버튼 중복 클릭 방지 처리 등에 유용하게 사용된다. 107 | 108 | ### 41.3.2 스로틀 109 | 110 | - 스로틀은 짧은 시간 간격으로 이벤트가 발생하더라도 일정 시간 간격으로 이벤트 핸들러가 최대 한번만 호출되도록 한다. 111 | - 스로틀은 짧은 시간 간격으로 연속해서 발생하는 이벤트를 그룹화해서 일정 시간 단위로 이벤트 핸들러가 호출되도록 호출 주기를 만든다. 112 | 113 | ```html 114 | 115 | 116 | 117 | 130 | 131 | 132 |
133 |
134 |
135 |
136 | 일반 이벤트 핸들러가 scroll 이벤트를 처리한 횟수: 137 | 0 138 |
139 |
140 | 스로틀 이벤트 핸들러가 scroll 이벤트를 처리한 횟수: 141 | 0 142 |
143 | 144 | 182 | 183 | 184 | ``` 185 | 186 | - 이처럼 짧은 시간 간격으로 이벤트 핸들러를 호출하는 스로틀은 scroll 이벤트 처리나 무한 스크롤 UI 구현 등에 유용하게 사용된다. 187 | -------------------------------------------------------------------------------- /42장-비동기-프로그래밍/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # 42장 - 비동기 프로그래밍 4 | 5 | ### 42.1 동기 처리와 비동기 처리 6 | 7 | - 함수를 호출하면 함수 코드가 평가되어 함수 실행 컨텍스트가 실행된다. 8 | - 이때 생성된 함수 실행 컨텍스트는 실행 컨텍스트 스택(콜스택이라고도 한다)에 푸시되고 함수 코드가 실행된다. 9 | - 함수 코드의 실행이 종료하면 함수 실행 컨텍스트는 실행 컨텍스트 스택에서 제거된다. 10 | 11 |
12 | 13 | - **자바스크립트 엔진은 단 하나의 실행 컨텍스트 스택을 갖는다** 14 | - 함수를 실행할 수 있는 창구가 단 하나이다. 15 | - 동시에 2개 이상의 함수를 동시에 실행할 수 없다. 16 | - 예를 들어, 실행 컨텍스트 스택의 최상위 요소인 "실행 중인 실행 컨텍스트"를 제외한 모든 실행 컨텍스트는 모두 실행 대기중인 Task들이다. 대기 중인 Task들은 현재 실행 중인 컨텍스트가 팝되어 실행 컨텍스트에서 제거되면 비로소 실행되기 시작한다. 17 | - 이 의미는 자바스크립트 엔진은 한 번에 하나의 Task만 실행할 수 있는 **싱글 스레드** 방식으로 동작한다. 18 | 19 |
20 | 21 | - **자바스크립트 엔진은 싱글 스레드 방식으로 동작한다** 22 | 23 | - 싱글 스레드 방식은 한 번에 하나의 Task만 실행할 수 있기 때문에 처리에 시간이 걸리는 Task를 실행하는 경우 작업중단이 발생한다. 24 | 25 | - 아래의 예를 살펴보면, sleep 함수는 3초 후에 foo 함수를 호출한다. 이 때 bar 함수는 sleep 함수의 실행이 종료된 이후에 호출되므로 3초 이상 호출되지 못하고 작업이 중단된다. 이처럼 실행 중인 Task가 종료할 떄까지 다음에 실행될 Task가 대기하는 방식을 **동기 처리**라고 한다. 26 | 27 | ```javascript 28 | // sleep 함수는 일정 시간(delay)이 경과한 이후에 콜백 함수(func)를 호출한다. 29 | function sleep(func, delay) { 30 | // Date.now()는 현재 시간을 숫자(ms)로 반환한다.("30.2.1. Date.now" 참고) 31 | const delayUntil = Date.now() + delay; 32 | 33 | // 현재 시간(Date.now())에 delay를 더한 delayUntil이 현재 시간보다 작으면 계속 반복한다. 34 | while (Date.now() < delayUntil); 35 | // 일정 시간(delay)이 경과한 이후에 콜백 함수(func)를 호출한다. 36 | func(); 37 | } 38 | 39 | function foo() { 40 | console.log('foo'); 41 | } 42 | 43 | function bar() { 44 | console.log('bar'); 45 | } 46 | 47 | // sleep 함수는 3초 이상 실행된다.. 48 | sleep(foo, 3 * 1000); 49 | // bar 함수는 sleep 함수의 실행이 종료된 이후에 호출되므로 3초 이상 블로킹된다. 50 | bar(); 51 | // (3초 경과 후) foo 호출 -> bar 호출 52 | ``` 53 | 54 | - 위 예제를 타이머 함수인 setTimeout을 사용하여 수정한 코드가 밑의 코드이다. 55 | 56 | ```javascript 57 | function foo() { 58 | console.log('foo'); 59 | } 60 | 61 | function bar() { 62 | console.log('bar'); 63 | } 64 | 65 | // 타이머 함수 setTimeout은 일정 시간이 경과한 이후에 콜백 함수 foo를 호출한다. 66 | // 타이머 함수 setTimeout은 bar 함수를 블로킹하지 않는다. 67 | setTimeout(foo, 3 * 1000); 68 | bar(); 69 | // bar 호출 -> (3초 경과 후) foo 호출 70 | ``` 71 | 72 | - setTimeout 함수 이후의 Task를 중단하지 않고 곧바로 실행한다. 이처럼 현재 실행 중인 Task가 종료 되지 않은 상태라 해도 다음 Task를 곧바로 실행하는 방식을 **비동기 처리**라고 한다. 73 | 74 | - **타이머 함수인 setTimeout과 setInterval, HTTP 요청, 이벤트 핸들러는 비동기 처리 방식으로 동작한다.** 75 | 76 |
77 | 78 | ### 42.2 이벤트 루프와 태스크 큐 79 | 80 | --- 81 | 82 | - 자바스크립트 엔진은 싱글 스레드이다. 하지만 브라우저가 동작하는 것을 살펴보면 많은 태스크가 동시에 처리되는 것처럼 느껴진다. 83 | - 이처럼 자바스크립트의 동시성을 지원하는 것이 바로 **이벤트 루프**다. 84 | - **이벤트 루프** 브라우저에 내장되어 있는 기능 중 하나 85 | - 브라우저 환경을 그림으로 표현하면 아래와 같다. 86 | 87 | ![image](https://user-images.githubusercontent.com/64825713/140919789-78b95dca-3705-4b69-bbab-bd5b4e519fa8.png) 88 | 89 | 90 | - 구글의 V8 자바스크립트 엔진을 비롯한 대부분의 자바스크립트 엔진은 크게 2개의 영역으로 구분할 수 있다. 91 | - 콜스택 92 | - 소스 코드 평가 과정에서 생성된 실행 컨텍스트가 추가되고 제거되는 스택 자료구조인 실행 컨텍스트 스택이 바로 콜스택이다. 93 | - 힙 94 | - 객체가 저장되는 메모리 공간 95 | - 콜 스택의 요소인 실행 컨텍스트는 힙에 저장된 객체를 참조 96 | 97 |
98 | 99 | - 비동기 처리에서 소스코드의 평가와 실행을 제외한 모든 처리는 자바스크립트 엔진을 구동하는 환경인 브라우저 또는 Node.js가 담당한다. 100 | - 이를 위해 브라우저 환경은 태스크 큐와 이벤트 루프를 제공한다. 101 | - 태스크 큐 102 | - setTimeout이나 setInterval과 같은 비동기 함수의 콜백 함수 또는 이벤트 핸들러가 일시적으로 보관되는 영역이다. 103 | - 이벤트 루프 104 | - 콜 스택에 현재 실행 중인 실행 컨텍스트가 있는지, 그리고 태스크 큐에 대기중인 함수가 있는지 반복해서 확인한다. 만약 콜스택이 비어있고 태스크 큐에 대기 중인 함수가 있다면 이벤트 루프는 순차적으로 태스크 큐에 대기중인 함수를 콜 스택으로 이동시킨다. 105 | 106 |
107 | 108 | - 다음 예제를 살펴보면서 브라우저 환경에서 foo 함수와 bar 함수 중에서 먼저 실행될 함수는 무엇일지 살펴보자 109 | 110 | ```javascript 111 | function foo() { 112 | console.log('foo'); 113 | } 114 | 115 | function bar() { 116 | console.log('bar'); 117 | } 118 | 119 | setTimeout(foo, 0); // 0초(실제는 4ms) 후에 foo 함수가 호출된다. 120 | bar(); 121 | ``` 122 | 123 | 1. 전역 코드가 평가되어 전역 실행 컨텍스트가 생성되고 콜 스택에 푸시된다. 124 | 125 | 2. 전역 코드가 실행되기 시작하여 setTimeout 함수가 호출된다. 이때 setTimeout 함수의 함수 실행 컨텍스트가 생성되고 콜스택에 푸시되어 현재 실행중인 실행 컨텍스트가 된다. 브라우저의 WebAPI인 타이머 함수도 함수이므로 함수 실행 컨텍스트를 생성한다. 126 | 127 | 3. setTimeout 함수가 실행되면 콜백 함수를 호출 스케줄링하고 종료되어 콜 스택에서 팝된다. 128 | 129 | 4. 브라우저가 수행하는 4-1과 자바스크립트 엔진이 수행하는 4-2는 병행 처리된다. 130 | 131 | 4-1. 브라우저는 타이머를 설정하고 타이머의 만료를 기다린다. 이후, 타이머가 만료되면 콜백 함수 foo가 태스크 큐에 푸시된다. 132 | 133 | 4-2. Bar 함수가 호출되어 bar 함수의 함수 실행 컨텍스트가 생성되고 콜 스택에 푸시되어 현재 실행 중인 실행 컨텍스트가 된다. 이후 bar 함수가 종료되어 콜 스택에서 팝된다. 134 | 135 | 5. 전역 코드 실행이 종료되고 전역 실행 컨텍스트가 콜 스택에서 팝된다. 콜 스택에는 아무런 실행 컨텍스트도 존재하기 않게 된다. 136 | 6. 이벤트 루프에 의해 콜 스택이 비어 있음이 감지되고 태스크 큐에서 대기 중인 콜백 함수 foo가 이벤트 루프에 의해 콜 스택에 푸시된다. 이후 foo 함수가 종료되어 콜 스택에서 팝된다. 137 | 138 | 139 | 140 |
141 | 142 | - 자바스크립트는 싱글 스레드 방식으로 동작한다. 하지만, 브라우저는 멀티 스레드로 동작한다. 143 | 144 | -------------------------------------------------------------------------------- /43장-Ajax/README.MD: -------------------------------------------------------------------------------- 1 | # 43장 Ajax 2 | 3 | ## 43.1 Ajax란? 4 | 5 | - Asynchronous JavaScript and XML 6 | - Ajax는 자바스크립트를 사용해 브라우저가 서버와 비동기 방식으로 통식하도록 하는 방법이다. 7 | - Ajax는 XMLHttpRequest 객체 혹은 fetch api를 이용해 구현할 수 있다. 8 | - Ajax를 통해 새로운 HTML 문서를 받지 않아도 화면을 새롭게 갱신할 수 있다. 9 | - 전통적인 웹 어플리케이션에서는 작은 변경만 있어도 완전한 HTML 문서를 다시 받아야했다. 10 | - 이때 불 필요한 데이터 통신이 발생할 뿐만 아니라 필연적으로 화면에 깜박거림이 발생한다. 11 | 12 | ## 43.2 JSON 13 | 14 | - JSON(JavaScript Object Notation)은 클라이언트와 서버 간 HTTP 통신을 위한 데이터 포맷 15 | - 처음에는 자바스크립트에서 사용할 목적으로 만들어졌으나 특정 언어에 종속되지 않는 '언어 독립형' 데이터 포맷이므로 많은 프로그래밍 언어에서 사용될 수 있다. 16 | - 자바스크립트의 객체와 유사하게 표현한다. 다만 키와 문자열은 반드시 큰따옴표를 사용해 표현해야한다. 17 | 18 |
19 | 20 | ### JSON의 메서드 21 | 22 | 1. JSON.stringify(value[, replacer, space]) 23 | - 자바스크립트의 값을 JSON 포맷의 문자열로 변환한다. 24 | - 위와 같은 과정을 직렬화(serializing)이라 한다. 25 | ```js 26 | const obj = { 27 | name: "Tanney", 28 | age: 27, 29 | alive: true, 30 | hobby: [], 31 | }; 32 | 33 | const json = JSON.stringify(obj); 34 | 35 | console.log(typeof json, json); 36 | // string {"name": "Tanney", "age": 27, "alive": true, "hobby": []} 37 | ``` 38 | - JSON은 특정 언어에 종속되지 않는 포맷이므로 자바스크립트 고유의 객체는 처리할 수 없다. 39 | - 함수 프로퍼티(메서드), 심볼이 키인 프로퍼티, 값이 undefined인 프로퍼티는 무시된다. 40 | ```js 41 | const user = { 42 | sayHi() { // 무시 43 | alert("Hello"); 44 | }, 45 | [Symbol("id")]: 123, // 무시 46 | something: undefined // 무시 47 | }; 48 | 49 | alert( JSON.stringify(user) ); // {} (빈 객체가 출력됨) 50 | ``` 51 | - 순환 참조가 있을 경우 에러가 발생한다. 52 | - 인자 종류(value, replacer, space) 53 | - value: 인코딩하려는 값 54 | - replacer: JSON으로 인코딩 하길 원하는 프로퍼티를 배열 또는 매핑함수로 결정 55 | - 매핑함수의 경우 프로퍼티 (키, 값) 쌍 전체를 대상으로 호출된다. 56 | - 매핑함수는 기존 프로퍼티 값을 대신할 값을 반환해야한다. 57 | - 매핑함수가 undefined를 반환하면 해당 프로퍼티는 무시된다. 58 | ```js 59 | const obj = { 60 | a: "a", 61 | b: 1, 62 | c: [], 63 | }; 64 | 65 | console.log(JSON.stringify(obj, ["a", "b"])) // { "a": "a", "b": 1 } 66 | console.log(JSON.stringify(obj, (key, value) => { 67 | if (key === "c") { 68 | return; 69 | } 70 | 71 | return value + "!!!"; 72 | })); // { "a": "a!!!", "b": "1!!!" } 73 | ``` 74 | - space: 서식 변경 목적으로 사용할 공백 문자 수 75 | ```js 76 | const obj = { 77 | a: "a", 78 | b: 1, 79 | c: [], 80 | }; 81 | 82 | console.log(JSON.stringify(obj, null, 2)); // JSON 포맷의 문자열로 변환하면서 들여쓰기 83 | /* 84 | string { 85 | "a": "a", 86 | "b": 1, 87 | "c": [], 88 | } 89 | */ 90 | ``` 91 | - 배열도 JSON 문자열로 변환할 수 있다. 92 | 93 |
94 | 95 | 2. JSON.parse 96 | 97 |
98 | 99 | - JSON 포맷의 문자열을 객체로 변환 100 | 101 |
102 | 103 | ### HTTP 요청 104 | 105 |
106 | 107 | - 브라우저에서는 form 태그나 a 태그를 통한 HTTP 요청 전송 기능을 제공한다. 108 | - AJAX통신을 위한 방법으로 XMLHttpRequest 객체와 fetch API를 제공한다. 109 | - XMLHttpRequest의 경우 open => setRequestHeader => send 메서드를 호출해 요청을 전송한다. 110 | - XMLHttpRequest 사용시 onreadystatechange 핸들러를 통해 HTTP 응답을 처리한다. 111 | - fetch api의 경우 fetch 함수를 통해 요청을 보내며 인수로 method, headers 등의 정보를 넘긴다. 112 | - fetch 함수는 http응답을 Promise 객체로 반환한다. -------------------------------------------------------------------------------- /44장-REST-API/README.md: -------------------------------------------------------------------------------- 1 | # 44장 REST API 2 | 3 | - REST : HTTP를 기반으로 클라이언트가 서버의 리소스에 접근하는 방식을 규정한 아키텍처 4 | 5 | - REST API : REST를 기반으로 서비스 API를 구현한 것 6 | 7 | - RESTful : REST의 기본 원칙을 성실히 지킨 서비스 디자인 8 | 9 | 10 | 11 | ## 44.1 REST API의 구성 12 | 13 | | 구성요소 | 내용 | 표현방법 | 14 | | --------------------- | ------------------------------ | ---------------- | 15 | | 자원(resource) | 자원 | URI(엔드포인트) | 16 | | 행위(verb) | 자원에 대한 행위 | HTTP 요청 메소드 | 17 | | 표현(representations) | 자원에 대한 행위의 구체적 내용 | 페이로드 | 18 | 19 | 20 | 21 | ## 44.2 REST API 설계원칙 22 | 23 | 1. URI는 리소스를 표현해야한다. 24 | 25 | 2. 리소스에 대한 행위는 HTTP 요청 메서드로 표현한다. 26 | 27 | | HTTP 요청 메소드 | 종류 | 목적 | 페이로드 | 28 | | ---------------- | -------------- | --------------------- | -------- | 29 | | GET | index/retrieve | 모든/특정 리소스 취득 | X | 30 | | POST | create | 리소스 생성 | O | 31 | | PUT | reaplace | 리소스의 전체 교체 | O | 32 | | PATCH | modify | 리소스의 일부 수정 | O | 33 | | DELETE | delete | 모든/특정 리소스 삭제 | X | 34 | 35 | ``` 36 | # bad : URI에 delete를 명시함 37 | GET /todos/delete/1 38 | 39 | # good 40 | DELETE /todos/1 41 | ``` 42 | 43 | ## 44.3 JSON Server를 이용한 REST API 실습 44 | 45 | - JSON Server : json 파일을 이용하여 가상 REST API 서버를 구축할 수 있는 툴 46 | 47 | 48 | #### 최종 디렉터리 구조 49 | 50 | ![image-20211109112411679](img/README/image-20211109112411679.png) 51 | [레포지토리 링크](https://github.com/ritajeong/rest-api-example.git) 52 | 53 | 54 | #### 과정 55 | 56 | - 디렉터리 루트에 json server 설치 57 | 58 | ``` 59 | $ npm install json-server --save-dev 60 | ``` 61 | 62 | - 디렉터리 루트에 db.json 파일 생성 63 | 64 | ``` 65 | { 66 | "todos": [ 67 | { 68 | "id": 1, 69 | "content": "HTML", 70 | "completed": true 71 | }, 72 | { 73 | "id": 2, 74 | "content": "CSS", 75 | "completed": false 76 | }, 77 | { 78 | "id": 3, 79 | "content": "Javascript", 80 | "completed": true 81 | }, 82 | ] 83 | } 84 | ``` 85 | 86 | - package.json에 스크립트 부분에 json server 실행 명령어 추가->npm start로 실행 87 | 88 | ``` 89 | "scripts": { 90 | "start": "json-server --watch db.json" 91 | }, 92 | ``` 93 | 94 | ## 44.3.4 GET 요청 95 | 96 | - #### public 폴더에 get_index.html 파일 생성 97 | 98 | ```html 99 | 100 | 101 | 102 |

103 |     
124 |   
125 | 
126 | 
127 | ```
128 | 
129 | npm start 명령어로 JSON Server를 실행한다.
130 | 
131 | ![image-20211109112759015](img/README/image-20211109112759015.png)
132 | 
133 | localhost:3000/get_index.html 에 접근하면 db.json에 작성된 모든 todos를 get 요청으로 불러온다.
134 | 
135 | 
136 | 
137 | ![1](img/README/1.jpg)
138 | 
139 | - #### get_retrieve.html 파일 생성
140 | 
141 | ```html
142 | 
143 | 
144 |   
145 |     

146 |     
167 |   
168 | 
169 | 
170 | ```
171 | 
172 | - 핵심코드 : xhr.open("GET", "/todos/1"); 
173 | - todos에서 id가 1번인 특정 todo만 취득한다.
174 | - localhost:3000/get_retrieve.html 에 접근하면 1번 todo만 불러온다.
175 | - ![2](img/README/2.jpg)
176 | 
177 | ## 44.3.5 POST 요청
178 | 
179 | - todos 리소스에 새로운 todo를 생성함
180 | 
181 | ```html
182 | 
183 | 
184 |   
185 |     

186 |     
211 |   
212 | 
213 | 
214 | ```
215 | 
216 | - 주소창에 localhost:3000/post.html 를 입력하면 id가 4인 Angular 객체가 추가된다.
217 | 
218 | - localhost:3000/get_index.html 에 다시 접근해보면 db.json에 추가된 것을 알 수 있다.
219 | 
220 |   ![3](img/README/3.jpg)
221 | 
222 | ## 44.3.6 PUT 요청
223 | 
224 | - put은 특정 리소스 전체를 교체할 때 사용한다.
225 | 
226 | ```html
227 | 
228 | 
229 |   
230 |     

231 |     
256 |   
257 | 
258 | 
259 | ```
260 | 
261 | ## 44.3.7 PATCH 요청
262 | 
263 | - 특정 리소스의 일부를 수정할 때 사용
264 | 
265 | ```html
266 | 
267 | 
268 |   
269 |     

270 |     
295 |   
296 | 
297 | 
298 | ```
299 | 
300 | ![4](img/README/4.jpg)
301 | 
302 | ## 44.3.8 DELETE 요청
303 | 
304 | - todos리소스에서 id를 사용하여 todo를 삭제한다.
305 | 
306 | ```html
307 | 
308 | 
309 |   
310 |     

311 |     
332 |   
333 | 
334 | 
335 | ```
336 | 
337 | ![5](img/README/5.jpg)
338 | 


--------------------------------------------------------------------------------
/44장-REST-API/img/README/1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ritajeong/javascript-study/2d7cbc387fb4bff05963e8bbe93b601a945f8cbe/44장-REST-API/img/README/1.jpg


--------------------------------------------------------------------------------
/44장-REST-API/img/README/2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ritajeong/javascript-study/2d7cbc387fb4bff05963e8bbe93b601a945f8cbe/44장-REST-API/img/README/2.jpg


--------------------------------------------------------------------------------
/44장-REST-API/img/README/3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ritajeong/javascript-study/2d7cbc387fb4bff05963e8bbe93b601a945f8cbe/44장-REST-API/img/README/3.jpg


--------------------------------------------------------------------------------
/44장-REST-API/img/README/4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ritajeong/javascript-study/2d7cbc387fb4bff05963e8bbe93b601a945f8cbe/44장-REST-API/img/README/4.jpg


--------------------------------------------------------------------------------
/44장-REST-API/img/README/5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ritajeong/javascript-study/2d7cbc387fb4bff05963e8bbe93b601a945f8cbe/44장-REST-API/img/README/5.jpg


--------------------------------------------------------------------------------
/44장-REST-API/img/README/image-20211109112411679.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ritajeong/javascript-study/2d7cbc387fb4bff05963e8bbe93b601a945f8cbe/44장-REST-API/img/README/image-20211109112411679.png


--------------------------------------------------------------------------------
/44장-REST-API/img/README/image-20211109112759015.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ritajeong/javascript-study/2d7cbc387fb4bff05963e8bbe93b601a945f8cbe/44장-REST-API/img/README/image-20211109112759015.png


--------------------------------------------------------------------------------
/45장-프로미스/README.md:
--------------------------------------------------------------------------------
  1 | # 45장 프로미스
  2 | 
  3 | - 자바스크립트는 비동기 처리를 위한 하나의 패턴으로 콜백 함수를 사용한다. 하지만 전통적인 콜백 패턴은 콜벡 헬로 인해 가독성이 나쁘고 비동기 처리 중 발생한 에러의 처리가 곤란하며 여러 개의 비동기 처리를 한 번에 처리하는 데도 한계가 있다.
  4 | - ES6에서는 비동기 처리를 위한 또 다른 패턴으로 프로미스를 도입했다. 프로미스는 전통적인 콜백 패턴이 가진 단점을 보완하며 비동기 처리 시점을 명확하게 표현할 수 있다.
  5 | 
  6 | ## 45.1 비동기 처리를 위한 콜백 패턴의 단점
  7 | 
  8 | ### 45.1.1 콜백 헬
  9 | 
 10 | ```js
 11 | // GET 요청을 위한 비동기 함수
 12 | const get = url => {
 13 |   const xhr = new XMLHttpRequest();
 14 |   xhr.open('GET', url);
 15 |   xhr.send();
 16 | 
 17 |   xhr.onload = () => {
 18 |     if (xhr.status === 200) {
 19 |       // 서버의 응답을 콘솔에 출력한다.
 20 |       console.log(JSON.parse(xhr.response));
 21 |     } else {
 22 |       console.error(`${xhr.status} ${xhr.statusText}`);
 23 |     }
 24 |   };
 25 | };
 26 | 
 27 | // id가 1인 post를 취득
 28 | get('https://jsonplaceholder.typicode.com/posts/1');
 29 | /*
 30 | {
 31 |   "userId": 1,
 32 |   "id": 1,
 33 |   "title": "sunt aut facere ...",
 34 |   "body": "quia et suscipit ..."
 35 | }
 36 | */
 37 | ```
 38 | 
 39 | - 위 예제의 get 함수는 비동기 함수다. 비동기 함수를 호출하면 함수 내부의 비동기로 동작하는 코드가 완료되지 않았다 해도 즉시 종료된다.
 40 | - **즉, 비동기 함수 내부의  비동기로 동작하는 코드는 비동기 함수가 종료된 이후에 완료된다. 따라서 비동기 함수 내부의 비동기로 동작하는 코드에서 처리 결과를 외부로 반환하거나 상위 스코프의 변수에 할당하면 기대한 대로 동작하지 않는다.**
 41 | - 비동기 함수는 비동기 처리 결과를 외부에 반환할  수 없고, 상위 스코프의 변수에 할당할 수도 없다. 따라서 비동기 함수의 처리 결과에 대한 후속 처리는 비동기 함수 내부에서 수행해야 한다. 이때  비동기 함수를 범용적으로 사용하기 위해 콜백 함수를 전달하는 것이 일반적이다.
 42 | - 필요에 따라 비동기 처리가 성공하면 호출될 콜백 함수와 비동기 처리가 실패하면 호출될 콜백 함수를 전달할 수 있다.
 43 | 
 44 | ```js
 45 | // GET 요청을 위한 비동기 함수
 46 | const get = (url, successCallback, failureCallback) => {
 47 |   const xhr = new XMLHttpRequest();
 48 |   xhr.open('GET', url);
 49 |   xhr.send();
 50 | 
 51 |   xhr.onload = () => {
 52 |     if (xhr.status === 200) {
 53 |       // 서버의 응답을 콜백 함수에 인수로 전달하면서 호출하여 응답에 대한 후속 처리를 한다.
 54 |       successCallback(JSON.parse(xhr.response));
 55 |     } else {
 56 |       // 에러 정보를 콜백 함수에 인수로 전달하면서 호출하여 에러 처리를 한다.
 57 |       failureCallback(xhr.status);
 58 |     }
 59 |   };
 60 | };
 61 | 
 62 | // id가 1인 post를 취득
 63 | // 서버의 응답에 대한 후속 처리를 위한 콜백 함수를 비동기 함수인 get에 전달해야 한다.
 64 | get('https://jsonplaceholder.typicode.com/posts/1', console.log, console.error);
 65 | /*
 66 | {
 67 |   "userId": 1,
 68 |   "id": 1,
 69 |   "title": "sunt aut facere ...",
 70 |   "body": "quia et suscipit ..."
 71 | }
 72 | */
 73 | ```
 74 | 
 75 | - 콜백 함수를 통해 비동기 처리 결과에 대한 후속 처리의 후속 처리를 하려고 한다면 함수 호출이 중첩되어 복잡도가 높아지는 현상이 발생하는데 이를 **콜백 헬**이라 한다.
 76 | 
 77 | - 콜백 헬은 가독성을 나쁘게 하며 실수를 유발하는 원인이 된다. 다음은 콜백 헬이 발생하는 전형적인 사례다.
 78 | 
 79 | ```js
 80 | get('/step1', a => {
 81 |   get(`/step2/${a}`, b => {
 82 |     get(`/step3/${b}`, c => {
 83 |       get(`/step4/${c}`, d => {
 84 |         console.log(d);
 85 |       });
 86 |     });
 87 |   });
 88 | });
 89 | ```
 90 | 
 91 | ### 45.1.2 에러 처리의 한계
 92 | 
 93 | - 비동기 처리를 위한 콜백 패턴의 문제점 중 가장 심각한 것은 에러 처리가 곤란하다는 것이다. 
 94 | 
 95 | ```js
 96 | try {
 97 |   setTimeout(() => { throw new Error('Error!'); }, 1000);
 98 | } catch (e) {
 99 |   // 에러를 캐치하지 못한다
100 |   console.error('캐치한 에러', e);
101 | }
102 | ```
103 | 
104 | - setTimeout 함수는 1초 후에 실행되고 이후 에러를 발생시킨다. 하지만 이 에러는 catch 코드 블록에서 캐치되지 않는다.
105 | - 에러는 호출자 방향으로 전파된다. 즉, 콜 스택의 아래 방향으로 전파된다. 하지만 setTimeout함수의 콜백 함수를 호출한 것은 setTimeout 함수가 아니다. 따라서 setTimeout 함수의 콜백 함수가 발생시킨 에러는 catch 블록에서 캐치되지 않는다.
106 | - 비동기 처리를 위한 콜백 패터은 콜백 헬이나 에러 처리가 곤란하다는 문제가 있다. 이를 극복하기 위해 ES6에서 프로미스가 도입되었다.
107 | 
108 | ## 45.2 프로미스의 생성
109 | 
110 | - Promise 생성자 함수를 new 함수와 함께 호출하면 Promise 객체를 생성한다. Promise 생성자 함수는 비동기 처리를 수행할 콜백 함수를 인수로 받는다. 이 콜백 함수는 resolve와 reject 함수를 인수로 전달받는다.
111 | 
112 | ```js
113 | // 프로미스 생성
114 | const promise = new Promise((resolve, reject) => {
115 |   // Promise 함수의 콜백 함수 내부에서 비동기 처리를 수행한다.
116 |   if (/* 비동기 처리 성공 */) {
117 |     resolve('result');
118 |   } else { /* 비동기 처리 실패 */
119 |     reject('failure reason');
120 |   }
121 | });
122 | ```
123 | 
124 | - Promise 생성자 함수가 인수로 전달받은 콜백 함수 내부에서 비동기 처리를 수행한다. 이때 비동기 처리가 성공하면 resolve 함수를 호출하고, 비동기 처리가 실패하면 reject 함수를 호출한다.
125 | - 프로미스는 다음과 같이 현재 비동기 처리가 어떻게 진행되고 있는지를 나타내는 상태 정보를 갖는다.
126 | 
127 | | 상태 정보 | 의미                                  | 상태 변경 조건                   |
128 | | --------- | ------------------------------------- | -------------------------------- |
129 | | pending   | 비동기 처리가 아직 수행되지 않은 상태 | 프로미스가 생성된 직후 기본 상태 |
130 | | fulfilled | 비동기 처리가 수행된 상태(성공)       | resolve 함수 호출                |
131 | | rejected  | 비동기 처리가 수행된 상태(실패)       | reject 함수 호출                 |
132 | 
133 | - fulfilled 또는 rejected 상태를 settled 상태라고 한다. settled 상태는 fulfilled 또는 rejected 상태와 상관없이 pending이 아닌 상태로 비동기 처리가 수행된 상태를 말한다.
134 | - 프로미스는 pending 상태에서 settled 상태로 변활 수 있다. 하지만 settled 상태가 되면 더는 다른 상태로 변화할 수 없다.
135 | 
136 | ## 45.3 프로미스의 후속 처리 메서드
137 | 
138 | - 프로미스의 비동기 처리 상태가 변화하면 이에 따른 후속 처리를 해야 한다. 
139 | - 프로미스의 비동기 처리 상태가 변화하면 후속 처리 메서드에 인수로 전달한 콜백 함수가 선택적으로 호출된다.
140 | 
141 | ### 45.3.1 Promise.prototype.then
142 | 
143 | - then 메서드는 두 개의 콜백 함수를 인수로 전달 받는다.
144 |   - 첫 번째 콜백 함수는 프로미스가 fulfilled 상태가 되면 호출된다.
145 |   - 두 번째 콜백 함수는 프로미스가 rejected 상태가 되면 호출된다.
146 | 
147 | ```js
148 | // fulfilled
149 | new Promise(resolve => resolve('fulfilled'))
150 |   .then(v => console.log(v), e => console.error(e)); // fulfilled
151 | 
152 | // rejected
153 | new Promise((_, reject) => reject(new Error('rejected')))
154 |   .then(v => console.log(v), e => console.error(e)); // Error: rejected
155 | ```
156 | 
157 | ### 45.3.2 Promise.prototype.catch
158 | 
159 | - catch 메서드는 한 개의 콜백 함수를 인수로 전달받는다. catch 메서드의 콜백 함수는 프로미스가 rejected 상태인 경우만 호출된다.
160 | 
161 | ```js
162 | // rejected
163 | new Promise((_, reject) => reject(new Error('rejected')))
164 |   .catch(e => console.log(e)); // Error: rejected
165 | ```
166 | 
167 | ### 45.3.3 Promise.prototype.finally
168 | 
169 | - finally 메서드는 한 개의 콜백 함수를 인수로 전달 받는다. finally 메서드의 콜백 함수는 프로미스의 성공, 실패와 상관없이 무조건 한 번 호출된다.
170 | 
171 | ```js
172 | new Promise(() => {})
173 |   .finally(() => console.log('finally')); // finally
174 | ```
175 | 
176 | ## 45.4 프로미스의 에러 처리
177 | 
178 | - 비동기 처리에서 발생하는 에러는 then 메서드의 두 번째 콜백 함수로 처리할 수 있다.
179 | 
180 | ```js
181 | const wrongUrl = 'https://jsonplaceholder.typicode.com/XXX/1';
182 | 
183 | // 부적절한 URL이 지정되었기 때문에 에러가 발생한다.
184 | promiseGet(wrongUrl).then(
185 |   res => console.log(res),
186 |   err => console.error(err)
187 | ); // Error: 404
188 | ```
189 | 
190 | - 또는, catch를 사용해 처리할 수도 있다,
191 | 
192 | ```js
193 | const wrongUrl = 'https://jsonplaceholder.typicode.com/XXX/1';
194 | 
195 | // 부적절한 URL이 지정되었기 때문에 에러가 발생한다.
196 | promiseGet(wrongUrl)
197 |   .then(res => console.log(res))
198 |   .catch(err => console.error(err)); // Error: 404
199 | ```
200 | 
201 | - then 메서드의 두 번쨰 콜백 함수는 첫 번째 콜백 함수에서 발생한 에러를 캐치하지 못 하고 코드가 복잡해져 가독성이 좋지 않다.
202 | 
203 | ```js
204 | promiseGet('https://jsonplaceholder.typicode.com/todos/1').then(
205 |   res => console.xxx(res),
206 |   err => console.error(err)
207 | ); // 두 번째 콜백 함수는 첫 번째 콜백 함수에서 발생한 에러를 캐치하지 못한다.
208 | ```
209 | 
210 | - catch 메서드를 사용하면 then 메서드를 호출한 이후에 발생한 에러 뿐만 아니라 then 메서드 내부에서 발생한 에러까지 모두 캐치할 수 있다.
211 | - 따라서 에러 처리는 catch를 사용하는 것을 권장한다.
212 | 
213 | ```js
214 | promiseGet('https://jsonplaceholder.typicode.com/todos/1')
215 |   .then(res => console.xxx(res))
216 |   .catch(err => console.error(err)); // TypeError: console.xxx is not a function
217 | ```
218 | 
219 | ## 45.5 프로미스 체이닝
220 | 
221 | - 45.1.1절에서 구현한 콜백 헬을 프로미스를 사용해 다음과 같이 구현할 수 있다.
222 | 
223 | ```js
224 | const url = 'https://jsonplaceholder.typicode.com';
225 | 
226 | // id가 1인 post의 userId를 취득
227 | promiseGet(`${url}/posts/1`)
228 |   // 취득한 post의 userId로 user 정보를 취득
229 |   .then(({ userId }) => promiseGet(`${url}/users/${userId}`))
230 |   .then(userInfo => console.log(userInfo))
231 |   .catch(err => console.error(err));
232 | ```
233 | 
234 | - then, catch, finally 메서드느 언제나 프로미스를 반환하므로 연속적으로 호출할 수 있다.
235 | - 프로미스는 프로미스 체이닝을 통해 비동기 처리 결과를 전달받아 후속 처리를 하므로 비동기 처리를 위한 콜백 패턴에서 발생하던 콜백 헬이 발생하지 않는다. 다만 프로미스도 콜백 패턴을 사용하므로 콜백 함수를 사용하지 않는 것은 아니다.
236 | 
237 | - 콜백 패턴은 가독성이 좋지 않다. 이 문제는 ES8에서 도입된 async/await를 통해 해결할 수 있다. 
238 | 
239 | ```js
240 | const url = 'https://jsonplaceholder.typicode.com';
241 | 
242 | (async () => {
243 |   // id가 1인 post의 userId를 취득
244 |   const { userId } = await promiseGet(`${url}/posts/1`);
245 | 
246 |   // 취득한 post의 userId로 user 정보를 취득
247 |   const userInfo = await promiseGet(`${url}/users/${userId}`);
248 | 
249 |   console.log(userInfo);
250 | })();
251 | ```
252 | 
253 | ## 45.6 프로미스의 정적 메서드
254 | 
255 | - Promise는 주로 생성자 함수로 사용되지만 함수도 객체이므로 메서드를 가질 수 있다. 
256 | - Promise가 가지는 메서드는 다음과 같다.
257 |   - Promise.resolve / Promise.reject
258 |   - Promise.all
259 |   - Promise.race
260 |   - Promise.allSettled
261 | 
262 | > 책에 나오지 않지만 ES2021 스펙으로 Promise.any 라는 메서드가 추가되었습니다.
263 | >
264 | > https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/any
265 | 
266 | ## 45.7 마이크로태스크 큐
267 | 
268 | - 다음 예제를 살펴보고 어떤 순서로 로그가 출력될지 생각해보자.
269 | 
270 | ```js
271 | setTimeout(() => console.log(1), 0);
272 | 
273 | Promise.resolve()
274 |   .then(() => console.log(2))
275 |   .then(() => console.log(3));
276 | ```
277 | 
278 | - 1 -> 2 -> 3 의 순으로 출력될 것처럼 보이지만 2 -> 3 -> 1의 순으로 출력된다.
279 | - 그 이유는 프로미스의 메서드는 태스크 큐가 아니라 마이크로태스크 큐에 저장되기 때문이다.
280 | - **마이크로 태스크 큐는 태스크 큐보다 우선순위가 높다.**
281 | 
282 | ## 45.8 fetch
283 | 
284 | - fetch 함수는 XMLHttpRequest 객체와 마찬가지로 HTTP 요청 전송 기능을 제공하는 클라이언트 사이드 Web API다.
285 | - **fetch 함수는 HTTP 응답을 나타내는 Response 객체를 래핑한 Promise 객체를 반환한다.**
286 | 
287 | 


--------------------------------------------------------------------------------
/46장-6-async-await(46.6~)/README.md:
--------------------------------------------------------------------------------
  1 | ## 46.6 async/await
  2 | 
  3 | - ES8(ECMAScript 2017)에 도입됨
  4 | - 제너레이터보다 간단하고 가독성 좋게 비동기 처리를 동기 처리처럼 동작하도록 구현할 수 있음
  5 | 
  6 | - 프로미스를 기반으로 동작함
  7 | 
  8 | - 프로미스의 then/catch/finally 후속 처리 메서드에 콜백 함수를 전달해서
  9 | 
 10 |   비동기 처리 결과를 후속 처리할 필요없이 마치 동기 처리처럼 프로미스를 사용할 수 있음
 11 | 
 12 |   즉, 프로미스의 후속 처리 메서드없이 마치 동기 처리처럼 프로미스가 처리 결과를 반환하도록 구현할 수 있음.
 13 | 
 14 | ```javascript
 15 | const fetch = require('node-fetch');
 16 | 
 17 | async function fetchTodo() {
 18 |   const url = 'https://jsonplaceholder.typicode.com/todos/1';
 19 | 
 20 |   const response = await fetch(url);
 21 |   const todo = await response.json();
 22 |   console.log(todo);
 23 |   // {userId: 1, id: 1, title: 'delectus aut autem', completed: false}
 24 | }
 25 | 
 26 | fetchTodo();
 27 | ```
 28 | 
 29 | 
 30 | 
 31 | ## 46.6.1 async 함수
 32 | 
 33 | - async 함수
 34 |   - async 키워드를 사용해 정의함.
 35 |   - 언제나 프로미스를 반환함
 36 |   - 명시적으로 프로미스를 반환하지 않더라도 암묵적으로 반환값을 resolve하는 프로미스를 반환함.
 37 | - await 키워드
 38 |   - 반드시 async 함수 내부에서 사용해야함
 39 | 
 40 | ```javascript
 41 | // async 함수 선언문
 42 | async function foo(n) { return n; }
 43 | foo(1).then(v => console.log(v)); // 1
 44 | 
 45 | // async 함수 표현식
 46 | const bar = async function (n) { return n; };
 47 | bar(2).then(v => console.log(v)); // 2
 48 | 
 49 | // async 화살표 함수
 50 | const baz = async n => n;
 51 | baz(3).then(v => console.log(v)); // 3
 52 | 
 53 | // async 메서드
 54 | const obj = {
 55 |   async foo(n) { return n; }
 56 | };
 57 | obj.foo(4).then(v => console.log(v)); // 4
 58 | 
 59 | // async 클래스 메서드
 60 | class MyClass {
 61 |   async bar(n) { return n; }
 62 | }
 63 | const myClass = new MyClass();
 64 | myClass.bar(5).then(v => console.log(v)); // 5
 65 | ```
 66 | 
 67 | 
 68 | 
 69 | 클래스의 constructor 메서드는 async 메서드가 될 수 없다.
 70 | 
 71 | 클래스의 constructor 메서드는 인스턴스를 반환해야하지만 async 함수는 언제나 프로미스를 반환해야한다.
 72 | 
 73 | ```javascript
 74 | class MyClass {
 75 |   async constructor() { }
 76 |   // SyntaxError: Class constructor may not be an async method
 77 | }
 78 | 
 79 | const myClass = new MyClass();
 80 | ```
 81 | 
 82 | 
 83 | 
 84 | ## 46.6.2 await 키워드
 85 | 
 86 | - 프로미스가 settled상태(비동기 처리가 수행된 상태)가 될 때까지 대기하다가 
 87 | 
 88 |   settled 상태가 되면 프로미스가 resolve한 처리 결과를 반환한다. 
 89 | 
 90 | - await 키워드는 반드시 프로미스 앞에서 사용해야한다.
 91 | 
 92 | ```javascript
 93 | const fetch = require('node-fetch');
 94 | 
 95 | const getGithubUserName = async id => {
 96 |   const res = await fetch(`https://api.github.com/users/${id}`); // (1)
 97 |   const { name } = await res.json(); // (2)
 98 |   console.log(name); // Ungmo Lee
 99 | };
100 | 
101 | getGithubUserName('ungmo2');
102 | ```
103 | 
104 | - 위의 코드에서 (1)의 fetch 함수가 수행한 HTTP 요청에 대한 서버의 응답이 도착해서
105 | 
106 |   fetch 함수가 반환한 프로미스가 settled 상태가 될 때까지 (1)은 대기하게 됨
107 | 
108 |   이후 프로미스가 setteld 상태가 되면 프로미스가 resolve한 처리 결과가 res  변수에 할당됨.
109 | 
110 | 
111 | 
112 | - await 키워드는 다음 실행을 일시 중지시켰다가 
113 | 
114 |   프로미스가 settled상태가 되면 다시 재개한다. (아래코드 참고)
115 | 
116 |   ```javascript
117 |   async function foo() {
118 |     const a = await new Promise(resolve => setTimeout(() => resolve(1), 3000));
119 |     const b = await new Promise(resolve => setTimeout(() => resolve(2), 2000));
120 |     const c = await new Promise(resolve => setTimeout(() => resolve(3), 1000));
121 |   
122 |     console.log([a, b, c]); // [1, 2, 3]
123 |   }
124 |   
125 |   foo(); // 약 6초 소요된다.
126 |   ```
127 | 
128 | - 주의사항
129 | 
130 |   모든 프로미스에 await 키워드를 사용하는 것은 주의해야함.
131 | 
132 |   위의 코드는 foo 함수는 종료될 때까지 6초가 소요됨.
133 | 
134 |   첫번째 프로미스는 settled 상태가 될 때까지 3초, 두번째는 2초, 세번째는 1초가 소요되기 때문.
135 | 
136 | - 그러나 foo 함수가 수행하는 3개의 비동기 처리는 
137 | 
138 |   **서로 연관이 없이 개별적으로 수행되는 비동기 처리이므로** 순차적으로 처리할 필요가 없다. 
139 | 
140 |   (아래 코드에서 리팩터링)
141 | 
142 | ```javascript
143 | async function foo() {
144 |   const res = await Promise.all([
145 |     new Promise(resolve => setTimeout(() => resolve(1), 3000)),
146 |     new Promise(resolve => setTimeout(() => resolve(2), 2000)),
147 |     new Promise(resolve => setTimeout(() => resolve(3), 1000))
148 |   ]);
149 | 
150 |   console.log(res); // [1, 2, 3]
151 | }
152 | 
153 | foo(); // 약 3초 소요된다.
154 | ```
155 | 
156 | 
157 | 
158 | - 아래 코드의 bar 함수는 앞선 비동기 처리의 결과를 가지고 다음 비동기 처리를 수행해야한다.
159 | 
160 |   **따라서 비동기 처리의 처리 순서가 보장되어야하므로** 모든 promise에 await 키워드를 써서 순차적으로 처리해야함
161 | 
162 | ```javascript
163 | async function bar(n) {
164 |   const a = await new Promise(resolve => setTimeout(() => resolve(n), 3000));
165 |   // 두 번째 비동기 처리를 수행하려면 첫 번째 비동기 처리 결과가 필요하다.
166 |   const b = await new Promise(resolve => setTimeout(() => resolve(a + 1), 2000));
167 |   // 세 번째 비동기 처리를 수행하려면 두 번째 비동기 처리 결과가 필요하다.
168 |   const c = await new Promise(resolve => setTimeout(() => resolve(b + 1), 1000));
169 | 
170 |   console.log([a, b, c]); // [1, 2, 3]
171 | }
172 | 
173 | bar(1); // 약 6초 소요된다.
174 | ```
175 | 
176 | 
177 | 
178 | ## 46.6.3 에러 처리
179 | 
180 | - 비동기 처리를 위한 콜백 패턴의 단점 : 에러처리가 곤란함
181 |   - 에러는 호출자(caller) 방향으로 전파된다. (45.1.2절 "에러처리의 한계" 참고)
182 |   - 즉, 콜 스택의 아래 방향(실행 중인 실행 컨텍스트가 푸시되기 직전에 푸시된 실행 컨텍스트 방향)으로 전파된다.
183 | - 하지만 비동기 함수의 콜백 함수를 호출한 것은 비동기 함수가 아니기 때문에 try...catch 문을 사용해 에러를 캐치할 수 없다.(아래 코드 참고)
184 | 
185 | ```javascript
186 | try {
187 |   setTimeout(() => { throw new Error('Error!'); }, 1000);
188 | } catch (e) {
189 |   // 에러를 캐치하지 못한다
190 |   console.error('캐치한 에러', e);
191 | }
192 | ```
193 | 
194 | 
195 | 
196 | - async/await 에서 에러처리는 try...catch문을 사용할 수 있다.
197 | 
198 | - 프로미스를 반환하는 비동기함수는 명시적으로 호출할 수 있기 때문에 호출자가 명확하기 때문이다.
199 | 
200 |   (콜백 함수를 인수로 전달받는 비동기 함수와의 차이점)
201 | 
202 | ```javascript
203 | const fetch = require('node-fetch');
204 | 
205 | const foo = async () => {
206 |   try {
207 |     const wrongUrl = 'https://wrong.url';
208 | 
209 |     const response = await fetch(wrongUrl);
210 |     const data = await response.json();
211 |     console.log(data);
212 |   } catch (err) {
213 |     console.error(err); // TypeError: Failed to fetch
214 |   }
215 | };
216 | 
217 | foo();
218 | 
219 | ```
220 | 
221 | 
222 | 
223 | - 위 코드의 foo 함수의 catch문은 HTTP 통신에서 발생한 네트워크 에러뿐 아니라
224 | 
225 |   try 코드 블록 내의 모든 문에서 발생한 일반적인 에러까지 모두 캐치할 수 있다.
226 | 
227 | - async 함수 내에서 catch문을 사용해서 에러처리를 하지 않으면
228 | 
229 |   async 함수는 발생한 에러를 reject 하는 프로미스를 반환한다.
230 | 
231 | - 따라서 async 함수를 호출하고 Promise.prototype.catch 후속 처리 메서드를 사용해 에러를 캐치할 수도 있다. 
232 | 
233 | ```javascript
234 | const fetch = require('node-fetch');
235 | 
236 | const foo = async () => {
237 |   const wrongUrl = 'https://wrong.url';
238 | 
239 |   const response = await fetch(wrongUrl);
240 |   const data = await response.json();
241 |   return data;
242 | };
243 | 
244 | foo()
245 |   .then(console.log)
246 |   .catch(console.error); // TypeError: Failed to fetch
247 | ```
248 | 
249 | 


--------------------------------------------------------------------------------
/47장-에러-처리/README.md:
--------------------------------------------------------------------------------
  1 | # 47장 에러 처리
  2 | 
  3 | ### 47.1장 에러 처리의 필요성
  4 | 
  5 | - 에러가 발생하지 않는 코드를 작성하는 것은 불가능하기 때문에 발생한 에러에 대해 대처하는 방법이 중요하다.
  6 | 
  7 | - try....catch문을 사용해 발생한 에러에 적절하게 대응하면 프로그램이 강제 종료되지 않고 계속해서 코드를 실행시킬 수 있다.
  8 | 
  9 | - 아래의 코드를 살펴보자.
 10 | 
 11 |   - querySelector 메서드는 인수로 전달한 CSS 선택자 문자열로 DOM에서 요소 노드를 찾을 수 없는 경우 에러를 발생시키지 않고 null을 반환한다. 따로 에러 처리를 해주지 않으면 아래 코드에서 에러로 이어질 가능성이 크고 프로그램은 강제 종료될 것이다.
 12 | 
 13 |   ```javascript
 14 |   // DOM에 button 요소가 존재하는 경우 querySelector 메서드는 에러를 발생시키지 않고 null을 반환한다.
 15 |   const $button = document.querySelector('button'); // null
 16 |   $button?.classList.add('disabled');
 17 |   ```
 18 | 
 19 |   - 기본적으로 에러가 발생하지 않는 코드를 짜면 괜찮겠지만, 현실적으로 불가능하다. 따라서 작성한 코드에서는 언제나 에러나 예외적인 상황이 발생할 수 있다는 것을 전제하고 이에 대응하는 코드를 작성하는 것이 중요하다.
 20 | 
 21 | 
 22 | 
 23 | 
24 | 25 | ### 47.2장 try...catch...finally 문 26 | 27 | --- 28 | 29 | - 기본적으로, 에러 처리를 구현하는 방법은 크게 두 가지가 존재한다. 30 | 1. querySelector나 Array#find 메서드처럼 예외적인 상황이 발생하면 반환하는 값을 if문이나 단축 평가 또는 옵셔널 체이닝 연산자를 통해 확인해서 처리하는 방법 31 | 2. 에러 처리 코드를 미리 등록해두고 에러가 발생하면 에러 처리 코드로 점프하도록 하는 방법 32 | - try...catch...finally문이 여기에 해당한다. -> 일반적으로, 이 방법을 에러 처리라고 한다. 33 | 34 | 35 | 36 | - **try...catch...finally문** 37 | 38 | - try...catch...finally문은 3개의 코드 블록으로 구성된다. 39 | 40 | ```javascript 41 | try { 42 | // 실행할 코드(에러가 발생할 가능성이 있는 코드) 43 | } catch (err) { 44 | // try 코드 블록에서 에러가 발생하면 이 코드 블록의 코드가 실행된다. 45 | // err에는 try 코드 블록에서 발생한 Error 객체가 전달된다. 46 | } finnaly { 47 | // 에러 발생과 상관없이 반드시 한 번 실행된다. 48 | } 49 | ``` 50 | 51 | - 먼저, try 코드 블록이 실행된다. 이때 try 코드 블록에 포함된 문 중에서 에러가 발생하면 발생한 에러는 catch문의 err 변수에 전달되고 catch 코드 블록이 실행된다. 52 | 53 | - catch문의 err 변수(어떠한 이름도 상관없다)는 try 코드 블록에 포함된 문 중에서 에러가 발생하면 생성되고 catch 코드 블록에서만 유효하다. 54 | 55 | - Finally 코드 블록은 에러 발생과 상관없이 반드시 한 번 실행된다. 56 | 57 | 58 | 59 |
60 | 61 | ### 47.3 Error 객체 62 | 63 | --- 64 | 65 | - Error 생성자 함수는 에러 객체를 생성한다. 66 | 67 | - Error 생성자 함수에는 에러를 상세히 설명하는 에러 메세지를 인수로 전달할 수 있다. 68 | 69 | ```javascript 70 | const error = new Error('invalid'); 71 | ``` 72 | 73 | - Error 생성자 함수가 생성한 에러 객체는 message 프로퍼티와 stack 프로퍼티를 갖는다. 74 | - Message 프로퍼티의 값은 Error 생성자 함수에 인수로 전달한 에러 메세지이다. 75 | - stack 프로퍼티의 값은 에러를 발생시킨 콜스택의 호출 정보를 나타내는 문자열이며 디버깅 목적으로 사용한다. 76 | 77 |
78 | 79 | ### 47.4 throw 문 80 | 81 | --- 82 | 83 | - Error 생성자 함수로 에러 객체를 생성한다고 에러가 발생하는 것은 아니다. 즉 에러 객체 생성과 에러 발생은 의미가 다르다. 84 | 85 | - 에러를 발생시키려면 try 코드 블록에서 throw문으로 에러 객체를 던져야 한다. 86 | 87 | ```javascript 88 | try { 89 | // 에러 객체를 던지면 catch 코드 블록이 실행되기 시작한다. 90 | throw new Error('something wrong'); 91 | } catch (error) { 92 | console.log(error); 93 | } 94 | ``` 95 | 96 |
97 | 98 | ### 47.5 에러의 전파 99 | 100 | --- 101 | 102 | - 에러는 호출자 방향으로 전파된다. 103 | 104 | - 즉, 콜 스택의 아래 방향(실행 중인 실행 컨텍스트가 푸시되기 직전에 푸시된 실행 컨텍스트 방향)으로 전파된다. 105 | 106 | ```javascript 107 | const foo = () => { 108 | throw Error('foo에서 발생한 에러'); // ④ 109 | }; 110 | 111 | const bar = () => { 112 | foo(); // ③ 113 | }; 114 | 115 | const baz = () => { 116 | bar(); // ② 117 | }; 118 | 119 | try { 120 | baz(); // ① 121 | } catch (err) { 122 | console.error(err); 123 | } 124 | ``` 125 | 126 | - 1에서 baz 함수를 호출하면 2에서 bar함수가 호출되고 3에서 foo함수가 호출되고 foo함수는 4에서 에러를 throw한다. 이때 foo함수가 throw한 에러는 다음과 같이 호출자에게 전파되어 전역에서 캐치된다. 127 | 128 | - throw된 에러를 캐치하지 않으면 호출자 방향으로 전파된다. 이때 throw된 에러를 캐치하여 적절히 대응하면 프로그램을 강제 종료 시키지 않고 코드의 실행 흐름을 복구할 수 있다. 129 | 130 | - throw된 에러를 어디에서도 캐치하지 않으면 프로그램은 강제 종료된다. -------------------------------------------------------------------------------- /48장-모듈/READMD.md: -------------------------------------------------------------------------------- 1 | # 48장 모듈 2 | 3 | ## 48.1 모듈의 일반적인 의미 4 | 5 |
6 | 7 | - 모듈은 앱을 구성하는 개별요소로 재사용할 수 있는 코드 조각을 의미한다. 8 | - 모듈은 자기만의 모듈 스코프를 가져야한다. 9 | - 보통 모듈은 파일 단위로 분리한다. 10 | - 모듈은 선택적으로 자기가 가진 리소스를 공개할 수 있다. (export) 11 | - 모듈의 사용자는 모듈이 공개한 리소스 중 일부 또는 전체를 자신의 스코프 내로 불러들여 사용할 수 있다. (import) 12 | 13 |
14 | 15 | ## 48.2 자바스크립트 모듈 16 | 17 |
18 | 19 | - 기존 자바스크립트는 모듈 시스템을 가지고 있지 않았다. 20 | - script 태그로 불러온 여러 스크립트들은 마치 하나의 파일안에 작성된 코드들처럼 동작한다. 21 | - 자바스크립트에서 모듈시스템을 사용하기 위해 CommonJS와 AMD의 방식이 제안되기도 했고, ES6에서는 ESM 방식이 도입되었다. 22 | 23 |
24 | 25 | ## 48.3 ES6 모듈(ESM) 26 | 27 |
28 | 29 | - 특정 스크립트를 ESM으로 취급하기 위해서는 script태그에 type="module" 어트리뷰트를 추가해야한다. 30 | - ESM에서는 기본적으로 strict mode가 적용된다. 31 | - ESM은 독자적인 모듈 스코프를 제공한다. 32 | - export 키워드를 통해 특정 식별자를 공개할 수 있다. 33 | ```js 34 | export const a = 1; 35 | export function f() {} 36 | export class C {} 37 | 38 | // 혹은 하나의 객체로 묶어 공개할 수도 있다. 39 | const a = 1; 40 | function f() {} 41 | class C {} 42 | 43 | export { a, f, C }; 44 | ``` 45 | - 모듈안에서 하나의 값에 한해 default export 값을 정할 수 있다. 46 | ```js 47 | const a = 1; 48 | 49 | export default a; 50 | 51 | // 혹은 이러한 방식도 가능하다. 52 | 53 | export default x => x + 1; 54 | ``` 55 | - default 키워드를 사용하는 경우 const, let, var 키워드는 함께 사용할 수 없다. 56 | - import 키워드를 통해 다른 모듈이 공개한 값을 가져올 수 있다. 57 | - 기본적으로 export한 식별자의 이름을 그대로 사용해 import 해야한다. 58 | ```js 59 | import { a, f, C } from "./someModule.js" 60 | ``` 61 | - as 키워드를 통해 식별자 이름은 변경하여 import 할 수 있다. 62 | ```js 63 | import { a as A, f as F, C as CC } from "./someModule.js" 64 | ``` 65 | - '*'를 통해 모든 리소스를 한 번에 import 할 수도 있다. 이때, 반드시 as 키워드를 통해 식별자 명을 지정해줘야한다. 66 | ```js 67 | import * as someModule from "./someModule.js" 68 | ``` 69 | - default export의 경우 {} 없이 임의의 이름으로 import 한다. 70 | ```js 71 | // someModule.js 72 | const a = 1; 73 | 74 | export default a; 75 | 76 | // index.js 77 | import A from "./someModule.js" 78 | ``` 79 | 80 | -------------------------------------------------------------------------------- /49장-Babel과-Webpack/READMD.md: -------------------------------------------------------------------------------- 1 | # 49장 Babel과 Webpack을 이용한 ES6+/ES.NEXT 개발환경 구축 2 | 3 | ## 49.1 Babel 4 | 5 |
6 | 7 | - 바벨은 트랜스파일러다. 8 | - 트랜스파일한다는 건 같은 추상화 수준의 언어로 변환하는 것을 의미한다. 9 | - 예를 들어 자바스크립트 최신 문법이 구형 브라우저에서도 동작하도록 하거나 typescript, jsx를 자바스트립트 코드로 변환할 때 사용한다. 10 | 11 |
12 | 13 | ### 바벨 기본 동작 14 | 바벨은 다음 세 단계로 동작한다. 15 | 16 |
17 | 18 | 1. 추상구문트리(AST)로 파싱 19 | 2. AST를 변환 20 | 3. 결과물 코드를 출력 21 | 22 |
23 | 24 | ### 플러그인(Plugin) 25 | 26 | - 이때, AST를 변환하는 일은 babel의 plugin이 수행한다. 27 | - babel 플러그인은 'visitor'라는 프로퍼티를 가진 객체를 반환하는 함수이다. 28 | - visitor의 값은 여러 메서드를 객체이다. 29 | - visitor의 메서드를 원하는 동작으로 재정의해 사용한다. 30 | - 커스텀 플러그인을 구현하지 않더라도 babel에서 제공하는 플러그인을 사용할 수 있다.(홈페이지에서 검색 가능) 31 | 32 | ### 바벨 간단한 사용 33 | - 바벨을 사용하기 위해선 babel-core와 babel-cli 패키지를 설치해야한다. 34 | ``` 35 | npm i -D @babel/core @babel/cli 36 | ``` 37 | - 그리고 사용하고자 하는 플러그인을 설치한다. 38 | - 이번 예시에서는 const, let 키워드를 var로 바꿔주는 block-scoping 플러그인을 사용하도록 하겠다. 39 | ``` 40 | npm install -D @babel/plugin-transform-block-scoping 41 | ``` 42 | - 설치한 플러그인은 다음과 같이 사용할 수 있다. 43 | ```js 44 | // app.js 45 | const a = 1; 46 | ``` 47 | 48 | ``` 49 | npx babel app.js --plugins @babel/plugin-transform-block-scoping 50 | 51 | var a = 1; 52 | ``` 53 | - babel.config.json(혹은 .babelrc)파일을 이용하면 사용하고자하는 플러그인을 미리 지정할 수 있다. 54 | ```json 55 | // babel.config.json 56 | { 57 | "plugins": ["@babel/plugin-transform-block-scoping"] 58 | } 59 | ``` 60 | - 뿐만아니라 변환시 여러옵션을 줄 수 있는데 이번에는 결과물을 파일로 생성하는 옵션만 알아보도록 하겠다. 61 | ``` 62 | // -d 또는 --out-dir 63 | npx babel app.js -d dist/js 64 | ``` 65 | ```js 66 | // dist/js/app.js 67 | var a = 1; 68 | ``` 69 | 70 |
71 | 72 | ### 프리셋(Preset) 73 | 74 |
75 | 76 | - 사용하는 바벨 플러그인의 양이 많다면 프리셋을 고려해도 좋다. 77 | - 프리셋은 목적에 맞게 여러 플러그인을 모아 놓은 것을 의미한다. 78 | - 프리셋은 기본적으로 다음과 같이 생성한다. 79 | ```js 80 | // mypreset.js 81 | module.exports = function mypreset() { 82 | return { 83 | // 원하는 플러그인들을 작성 84 | plugins: [ 85 | "@babel/plugin-transform-arrow-functions", 86 | "@babel/plugin-transform-block-scoping", 87 | "@babel/plugin-transform-strict-mode", 88 | ], 89 | } 90 | } 91 | ``` 92 | - 그리고 babel 설정 파일을 통해 프리셋을 사용하도록 지정할 수 있다. 93 | ```json 94 | { 95 | presets: ["./mypreset.js"] 96 | } 97 | ``` 98 | - 바벨은 목적에 따라 여러가지 프리셋을 제공한다. 이를 패키지로 설치해 사용할 수 있다. 99 | - 예를 들어 다음과 같은 프리셋들을 제공한다. 100 | preset-env, preset-react, preset-typescirpt, .... 101 | - 프리셋은 총 세가지 방식으로 전달할 수 있다. 102 | ```json 103 | { 104 | "presets": [ 105 | "presetA", // bare string 106 | ["presetA"], // wrapped in array 107 | ["presetA", {}] // 2nd argument is an empty options object 108 | ] 109 | } 110 | ``` 111 | - 이때, 배열로 프리셋을 전달하면 두번째요소에 option객체를 전달할 수 있다. 112 | - 실제로 env 프리셋의 경우 브라우저를 특정하기 위한 target 옵션과 폴리필 지정을 위한 옵션 등을 제공한다. 113 | 114 | > 폴리필
115 | > 자바스크립트 명세에는 새로운 문법이나 기존에 없던 내장 함수들이 추가되곤한다. 116 | > 새로운 문법은 트랜스 파일러를 통해 변환할 수 있지만 새로운 함수는 직접 명세에 맞게 정의해주어야한다. 117 | > 이를 지원하기 위해 폴리필을 사용한다. 118 | > 폴리필은 기존 함수를 수정하거나 새롭게 구현한 함수의 스크립트를 의미한다. 119 | > 120 | 121 |
122 | 123 | ## 49.2 Webpack 124 | 125 |
126 | 127 | - Webpack은 의존관계에 있는 자바스크립트, CSS, 이미지 등의 리소스를 하나의 파일로 번들링하는 모듈 번들러이다. 128 | - webpack, wepack-cli 패키지를 설치해 사용한다. 129 | - 바벨과 마친가지로 cli 명령어를 통해 사용한다. 이때 여러 옵션을 줄 수 있지만 webpack.config.js 파일을 통해 옵션을 지정하는게 일반적이다. 130 | - webpack 설정 파일에서 기본적으로 3가지 옵션을 설정해야한다. 131 | ```js 132 | // webpack.config.js 133 | const path = require("path") 134 | 135 | module.exports = { 136 | mode: "development", 137 | entry: { 138 | main: "./src/app.js", 139 | }, 140 | output: { 141 | filename: "[name].js", 142 | path: path.resolve("./dist"), 143 | }, 144 | } 145 | ``` 146 | - mode는 production과 development 중 하나로 지정한다. 147 | - entry는 번들 진입점이 될 파일을 의미한다. 해당 파일을 기준으로 의존성을 따라 번들링한다. 148 | - output은 번들 결과물에 대한 정보를 의미한다. 위 코드에서는 파일 이름과 경로를 지정해주었다. 149 | - 'name'은 entry에서 지정한 각 파일의 key값을 의미한다. 150 | - 위 설정에 경우 src 폴더 안에 있는 app.js를 기준으로 번들링해 그 결과물을 dist 폴더 안에 app.js 파일로 생성한다. 151 | 152 |
153 | 154 | ### 로더(Loader) 155 | 156 |
157 | 158 | - 자바스크립트 뿐만 아니라 CSS, 이미지 등도 함께 번들링하려면 반드시 로더가 필요하다. 159 | - 즉, 로더는 다른 언어(예를 들어 typescript)를 자바스크립트로 변환해주거나, 이미지를 dataURL로 바꿔주는 등 모든 리소스를 js 파일 안에서 사용할 수 있도록 바꾼다. 160 | - 로더는 파일의 내용을 받아 변환 내용을 반환하는 함수를 내보내는 모듈이다. 161 | - 로더를 사용하기 위해 rules 옵션을 사용한다. 사용하고자하는 로더 정보 객체를 rules 배열에 추가한다. 162 | - CSS파일을 위한 로더로 예를 들어보자 163 | ```js 164 | // npm i -D style-loader css-loader 165 | 166 | module.exports = { 167 | rules: [ 168 | { 169 | test: /\.css$/, 170 | use: ["style-loader", "css-loader"] 171 | } 172 | ] 173 | } 174 | ``` 175 | - test는 로더를 적용하고자 하는 파일을 지정한다. 파일명이나 정규표현식 패턴을 지정할 수 있다. 176 | - use는 실제로 적용할 로더의 배열을 의미한다. 177 | - use 배열의 로더들은 뒤에서부터 차례로 적용된다. 178 | - css 파일을 번들링 하기 위해서는 반드시 css-loader를 먼저 적용하고 style-loader를 적용해야한다. 따라서 위와 같은 순서를 반드시 지켜야한다. 179 | - 로더를 사용하면 바벨도 함께 적용할 수 있다. 이를 위해 babel-loader를 사용한다. 180 | 181 |
182 | 183 | ### 플러그인 184 | 185 |
186 | 187 | - 로더가 파일 단위로 '번들링 과정'에서 적용된다면 플러그인은 번들 결과물에 대해 적용된다. 188 | - plugin은 클래스로 구현하며 apply메서드를 작성해야한다. 189 | - 자세한 내용은 [웹팩 문서](https://webpack.js.org/contribute/writing-a-plugin/)를 통해 확인할 수 있다. 190 | - 바벨과 마찬가지로 웹팩 플러그인도 패키지로 설치해 사용할 수 있다. 191 | - 웹팩 설정 파일에 plugins 옵션을 추가해 사용한다. 192 | - plugins 배열에는 각 플러그인의 인스턴스가 담겨야한다. 193 | - 예시 194 | ```js 195 | const webpack = require("webpack") 196 | 197 | export default { 198 | // 웹팩에서 제공하는 define plugin 199 | plugins: [new webpack.DefinePlugin({})], 200 | } 201 | ``` -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 🔎 스터디 소개 2 | 3 |
4 | 내용 보기 5 |
6 | 7 | 8 | ### 📌 Introduction 9 | 10 | 한줄요약 : **자바스크립트** 개념을 **깃허브에 정리**하고 **질의응답**하기 11 | 12 | 'FE재남'님이 진행하시는 [Javascript Deep Dive 줌스터디](https://www.inflearn.com/studies/290611)(이하 줌스터디)에 속한 **복습스터디**입니다. 13 | 14 | Deep Dive책을 다시 살펴보는 목적을 가지며, 15 | 16 | 종종 줌스터디에서 언급된 내용에 대해 토의하는 시간을 가질 예정입니다. 17 | 18 | ### 📌 When, Where 19 | 20 | - **매주 월요일 7시~8시**, **Discord** 21 | 22 | 줌스터디(월 8시반)시작 전에 미리 만나서 **한정적인 시간** 안에 **질의응답**을 하는 컨셉입니다. 23 | 24 | - 기간 : **9월 13일 월요일**부터 두 달간 예정. 25 | 26 | (진도에 따라 변동이 생길 수 있습니다.) 27 | 28 | ### 📌 Why 29 | 30 | Javascript의 작동원리를 근본적으로 이해하고, 코드를 작성할 때 도움이 되기 위함입니다. 31 | 32 | 줌스터디에서 진도가 나간 부분을 다시금 복습합니다. 33 | 34 | 굳이 스터디의 성격을 정하자면 면접스터디입니다. 35 | 36 | ### 📌 What 37 | 38 | 일주일간 **대주제** 하나를 공부합니다. 39 | 40 | **소주제**를 1/n로 분배하고, 각자 맡은 파트를 정리합니다. 41 | 42 | 맡은 소주제와 연관된 **질문 2가지**를 정합니다. 43 | 44 | ### 예시 45 | 46 | **대주제** : 스코프 47 | 48 | **소주제** : 스코프의 종류, 스코프 체인, 함수레벨 스코프, 렉시컬 스코프 49 | 50 | **질문** : 변수를 참조할 때 자바스크립트 엔진이 작동하는 과정은? 51 | 52 | ### 📌 How 53 | 54 | - **Github Repository**에 마크다운으로 작성한 글을 올립니다. 55 | 56 | 예시) https://github.com/baeharam/Must-Know-About-Frontend/blob/main/Notes/javascript/scope.md 57 | 58 | **“스터디 repository를 fork떠서 맡으신 파트 작성하시고 PR 날려주세요”** 59 | 60 | PR 날리기~~ 61 | 62 | https://wayhome25.github.io/git/2017/07/08/git-first-pull-request-story/ 63 | 64 | - **checklist 질문**을 **각자 2개씩** 만듭니다. 65 | 66 | 예시)checklist 부분만 참고해주세요 67 | 68 | https://github.com/Knowre-Dev/WebDevCurriculum/tree/master/Quest03 69 | 70 | - **Discord**에서 월요일 7시에 만나 **질의응답 시간**을 가집니다. 71 | 72 | - 화상은 켜지 않고 음성으로 진행하며, 필요한 경우 화면공유는 자유롭게 합니다. 73 | 74 | - 4명*질문 2개 = **총 8개의 질문**으로 구성된 리스트에서 75 | 76 | - 사다리타기로 질문에 대답할 사람을 정합니다. 77 | 78 | - 즉, **1인당 2개의 질문에 답변**하게 됩니다. 79 | 80 | (따라서 소주제를 맡아서 작성하더라도 대주제 전체를 공부하게 됩니다.) 81 | 82 | - 답변이 끝나면 약간의 **토의시간**을 가집니다. 83 | 84 |
85 |
86 | 87 | ## 📖 진행 방식 및 커밋 컨벤션 88 | 89 |
90 | 내용 보기 91 |
92 | 93 | ### 진행 방식 94 | 95 | - **매주 월요일 오후 7시**에 스터디를 진행한다. 96 | - **매주 토요일**까지 각자 맡은 파트를 정리해서 PR을 보낸다. 97 | - 스터디 시작 전까지 다른 사람의 PR을 읽고 코멘트를 남긴다. 98 | - 자신의 PR에 모두 리뷰가 달리면 PR을 스스로 머지한다.(Squash and Merge) 99 | - `README.md` 파일에 매 주차 진행사항을 정리한다. 100 | - **매주 일요일**까지 자신이 맡은 파트에 대한 질문 2가지를 issue에 올린다. 101 | 102 | ### 커밋 컨벤션 103 | 104 | - 커밋 메세지는 한글로 작성한다. 105 | - 커밋의 의도에 따라 아래 prefix를 붙인다. 106 | - create: 글을 새로 추가하는 경우 107 | - update: 기존에 작성한 글을 수정하는 경우 108 | - delete: 기존에 작성한 글을 삭제하는 경우 109 | - docs: `README.md` 파일을 수정하는 경우 110 | - chore: 그 외의 경우 111 | 112 |
113 |
114 | 115 | ## 📚 진행 상황 116 | 117 | ### 1주차 변수 118 | 119 |
120 | 내용 보기 121 |
122 | 123 | - 상민: 4장 변수 124 | - 진주: 13장 스코프 125 | - 윤호: 14장 전역변수의 문제점 126 | - 태은: 15장 let, const 키워드와 블록 레벨 스코프 127 | 128 | 129 |
130 |
131 | 132 | ### 2주차 타입, 실행 컨텍스트 133 | 134 |
135 | 내용 보기 136 |
137 | 138 | - 진주: 6장 데이터 타입 139 | - 윤호: 9장 타입 변환과 단축 평가 140 | - 상민: 11장 원시 값과 객체의 비교 141 | - 태은: 23장 실행 컨텍스트 142 | 143 | 144 |
145 |
146 | 147 | ### 3주차 JavaScript 기초 ,함수 148 | 149 |
150 | 내용 보기 151 |
152 | 153 | - 진주: 5장 표현식과 문 + 16장 프로퍼티 어트리뷰트 154 | - 윤호: 7장 연산자 + 12장 함수 155 | - 태은: 8장 제어문 + 10장 객체 리터럴 156 | - 상민: 17장 생성자 함수에 의한 객체 생성 + 18장 함수와 일급 객체 157 | 158 |
159 |
160 | 161 | ### 4주차 빌트인 객체, strict mode, 프로토타입, this 162 | 163 |
164 | 내용 보기 165 |
166 | 167 | - 윤호: 19장 프로토타입 전반부 ( ~ 19.7) 168 | - 진주: 19장 프로토타입 후반부 (19.8 ~ ) 169 | - 상민: 20장 strict mode + 21장 빌트인 객체 170 | - 태은: 22장 this 171 | 172 | 173 |
174 |
175 | 176 | 177 | ### 5주차 클로저, 클래스, 함수 178 | 179 |
180 | 내용 보기 181 |
182 | 183 | - 윤호: 24장 클로저 184 | - 진주: 25장 클래스(25.1 ~ 25.6) 185 | - 상민: 25장 클래스(25.7 ~ 25.8) 186 | - 태은: 26장 ES6 함수의 추가 기능 187 | 188 | 189 |
190 |
191 | 192 | ### 6주차 Symbol, 이터러블, 스프레드, 디스트럭처링, Set, Map 193 | 194 |
195 | 내용 보기 196 |
197 | 198 | 199 | 200 | - 태은: 33장 Symbol, 34장 이터러블 201 | - 윤호: 35장 스프레드 문법, 36장 디스트럭처링 할당 202 | - 진주: 37장 Set과 Map 203 | 204 | 205 |
206 |
207 | 208 | ### 7주차 브라우저의 렌더링 과정, DOM, 이벤트 209 | 210 |
211 | 내용 보기 212 |
213 | 214 | 215 | 216 | - 진주: 38장 브라우저의 렌더링 과정 217 | - 태은: 39장 DOM(39.1 ~ 39.4) 218 | - 상민: 39장 DOM(39.5 ~ 39.9) 219 | - 윤호: 40장 이벤트 220 | 221 | 222 |
223 |
224 | 225 | ### 8주차 비동기 프로그래밍 226 | 227 |
228 | 내용 보기 229 |
230 | 231 | - 윤호: 41장 타이머 232 | - 상민: 42장 비동기 프로그래밍 233 | - 태은: 43장 AJAX 234 | - 진주: 44장 REST API 235 | 236 |
237 |
238 | 239 | ### 9주차 프로미스, 제네레이터, async/await, 모듈 240 | 241 |
242 | 내용 보기 243 |
244 | 245 | - 윤호: 45장 프로미스 246 | - 진주: 46장 제네레이터와 async/await 247 | - 상민: 47장 에러 처리 248 | - 태은: 48장 모듈, 49장 Babel과 Webpack을 이용한 ES6+/ES.NEXT 개발 환경 구축 249 | 250 |
251 |
252 | --------------------------------------------------------------------------------