├── .gitignore ├── 10회차 ├── yesol.md ├── 김록원.md ├── 박상범.md └── 병현.md ├── 11회차 ├── yesol.md ├── 김록원.md ├── 김민지.md ├── 병현.md └── 상범.md ├── 12회차 ├── 김록원.md ├── 박상범.md ├── 박수진.md ├── 병현.md └── 천승아.md ├── 1회차 ├── README.md ├── 김록원.md ├── 김민지.md ├── 김효진.md ├── 박상범.md ├── 박수진.md ├── 이병현.md ├── 이상조.md └── 천승아.md ├── 2회차 ├── README.md ├── img.png ├── img_1.png ├── img_2.png ├── img_3.png ├── img_4.png ├── 김록원.md ├── 김민지.md ├── 김효진.md ├── 박수진.md ├── 송희면.md ├── 이병현.md ├── 이상조.md ├── 정예솔.md └── 천승아.md ├── 3회차 ├── README.md ├── 김민지.md ├── 김효진.md ├── 박상범.md ├── 박수진.md ├── 이병현.md ├── 이상조.md ├── 정예솔.md └── 천승아.md ├── 4회차 ├── README.md ├── img.png ├── img_1.png ├── img_2.png ├── img_3.png ├── img_4.png ├── img_5.png ├── yesol.md ├── 김록원.md ├── 김민지.md ├── 김효진.md ├── 박수진.md ├── 병현.md ├── 상범.md └── 이상조.md ├── 5회차 ├── README.md ├── yesol.md ├── 김록원.md ├── 김민지.md ├── 박상범.md ├── 박수진.md ├── 이병현.md └── 천승아.md ├── 6회차 ├── README.md ├── yesol.md ├── 김록원.md ├── 김효진.md ├── 박수진.md └── 이병현.md ├── 7회차 ├── README.md ├── 김록원.md ├── 김민지.md ├── 김효진.md ├── 박상범.md ├── 박수진.md ├── 이병현.md └── 천승아.md ├── 8회차 ├── yesol.md ├── 김록원.md ├── 김민지.md ├── 박상범.md ├── 박수진.md └── 천승아.md ├── 9회차 ├── README.md ├── 김록원.md ├── 김민지.md ├── 박상범.md ├── 박수진.md ├── 이병현.md └── 천승아.md ├── README.md ├── images └── img.png └── study-rules.md /.gitignore: -------------------------------------------------------------------------------- 1 | .idea -------------------------------------------------------------------------------- /12회차/김록원.md: -------------------------------------------------------------------------------- 1 | # 42장 - 비동기 프로그래밍 2 | js는 단 하나의 실행 컨택스트 스택(콜 스택)을 가진다. 3 | 이는 함수를 실행할 수 있는 창구가 단 하나이며, 동시에 2개 이상의 함수를 실행할 수 없다는 것을 의미힌다. 4 | 5 | 이처럼 js 엔진은 한 번에 하나의 태스크만 실행할 수 있는 싱글 스레드 방식이다. 6 | 때문에 처리에 시간이 걸리는 태스크를 실행하는 경우 블로킹(작업 중단)이 발생한다.(동기 처리) 7 | 8 | 하지만 선행 태스크가 종료되지 않은 상태에서 이후의 태스크들을 블로킹 하지 않고 곧바로 실행하는 방식인 비동기 처리도 지원한다. 9 | 비동기 처리는 js의 이벤트루프와 태스크 큐와 깊은 관계가 있다. 10 | 11 |
12 | 13 | > **💡 동기 처리** 14 | 실행 중인 테스크가 종료할 때까지 다음에 실행될 테스크가 대기하는 방식 15 | 실행 순서가 보장된다는 장점이 있지만 앞선 태스크가 종료할 때까지 이후 태스크들이 블로킹되는 단점이 있다. 16 | 17 |
18 | 19 | > **💡 비동기 처리** 20 | 함수 이후의 태스크를 블로킹하지 않고 곧바로 실행하는 방식 21 | 현재 실행 중인 태스크가 종료되지 않은 상태라 해도 다음 태스크를 곧바로 실행하는 방식이다. 22 | 비동기처리는 실행 순서가 보장되지 않는다는 단점이 있다. 이런 단점보완을 위해 전통적으로 콜백함수를 사용하지만 콜백함수가 중첩될때 콜백 헬을 발생시켜 가독성을 나쁘게 하고, 비동기 처리 중 발생한 에러의 예외 처리가 곤란하다 23 | 24 |
25 | 26 | ### 이벤트 루프와 태스크 큐 27 | js에서 동시성을 지원하는 것이 이벤트 루프이다. 28 | 이벤트 루프는 브라우저에 내장되어 있는 기능 중 하나다.(Node.js에도 내장) 29 | 30 | 1. **`js 엔진(V8)`** 31 | - `콜 스택(실행 컨텍스트 스택)` 32 | - `힙` : 객체가 저장되는 메모리 공간, 실행 컨텍스트는 힙에 저장된 객체를 참조한다. 33 | 2. **`태스크 큐`** 34 | - setTimeout, setInterval과 같은 비동기 함수의 콜백함수, 이벤트 핸들러가 일시적으로 보관되는 영역 35 | - 이와 별도로 프로미스의 후속 처리 메서드의 콜백함수가 일시적으로 보관되는 마이크로태스크 큐도 존재 36 | 3. **`이벤트 루프(브라우저 내장)`** 37 | - 콜스택에 현재 실행중인 실행컨택스트가 있는지, 태스크큐에 대기 중인 함수가 있는지 반복해서 확인하는 역할 38 | - 콜 스택이 비어있고 태스크 큐에 대기중인 함수가 있다면 이벤트 루프는 순차적으로 태스크 큐에 대기중인 함수를 콜 스택으로 이동시킨다. 39 | 40 |
41 | 42 | 주의할점은 js 엔진은 싱글 스레드로 동작하지만 브라우저나 Node.js는 싱글 스레드가 아니라는 것이다. 43 | 만약 모든 js 코드가 js엔진에서 싱글 스레드 방식으로 동작한다면 js는 비동기로 동작할 수 없다. 44 | **js 엔진(V8)은 싱글 스레드로 동작하지만 브라우저(or Node.js)는 멀티 스레드로 동작한다.** 45 | 46 |
47 | 48 | ### 이벤트 루프 참고자료 49 | 50 | [Node.js 이벤트 루프](https://www.korecmblog.com/node-js-event-loop/) 51 | [Node.js Event Loop 파헤치기](https://medium.com/zigbang/nodejs-event-loop%ED%8C%8C%ED%97%A4%EC%B9%98%EA%B8%B0-16e9290f2b30) 52 | [로우 레벨로 살펴보는 Node.js 이벤트 루프](https://evan-moon.github.io/2019/08/01/nodejs-event-loop-workflow/) 53 | [브라우저와 Node.js의 이벤트 루프 차이점](https://yceffort.kr/2021/08/browser-nodejs-event-loop) 54 | [이벤트 루프 관점에서 보는 for와 forEach]() 55 | 56 |


57 | 58 | # 43장 - Ajax 59 | > **💡 Ajax란** 60 | Ajax(Asynchronous JavaScript and XML)는 js를 사용하여 브라우저가 서버에게 비동기 방식으로 데이터를 요청하고 응답받아 웹페이지를 동적으로 갱신하는 프로그래밍 방식이다. 61 | Ajax는 Web API인 XMLHttpRequest 객체를 기반으로 동작한다. 62 | 63 |
64 | 65 | ### Ajax가 가져온 변화 66 | Ajax 이전에는 완전한 HTML을 전송받아 웹페이지 자체를 다시 렌더링했다. 이는 아래와 같은 단점이 있다. 67 | - 완전한 HTML을 매번 다시 전송받기 때문에 불필요한 데이터 통신이 발생 68 | - 변경할 필요가 없는 부분도 처음부터 렌더링 -> 화면이 순간적으로 깜박이는 현상 69 | - 클라이언트와 서버와의 통신이 동기 방식으로 동작 -> 응답받기 전까지 다음 처리가 블로킹됨 70 | 71 |
72 | 73 | Ajax의 등장은 아래와 같은 장점을 가져올 수 있었다. 74 | - 변경할 부분만 서버로 부터 응답받음 75 | - 변경할 필요가 없는 부분은 리렌더링하지 않음 76 | - 클라이언트와 서버가 비동기 방식으로 동작 77 | 78 |
79 | 80 | ### JSON 81 | > **💡 JSON이란** 82 | JSON(JavaScript Object Notation)은 HTTP 통신을 위한 텍스트 데이터 포맷이다. 83 | js에 종속되지 않는 언어 독립형 데이터 포멧이다. 84 | 85 |
86 | 87 | **`JSON.stringify`** 88 | - 객체를 JSON 포맷의 문자열로 변환한다. 89 | - 서버로 객체를 전송하려면 객체를 문자열화해야 하는데 이를 직렬화라고 한다. 90 | - 객체뿐만 아니라 배열도 JSON 포맷의 문자열로 변환한다. 91 | 92 |
93 | 94 | **`JSON.parse`** 95 | - JSON 포맷의 문자열을 객체로 변환한다. 96 | - 서버로부터 전송받은 JSON 데이터는 문자열이다. 이를 객체를 사용해 객체화시킨다. 역직렬화라고 부른다. 97 | - 배열이 JSON 포맷의 문자열인경우엔 배열 객체로 변환한다. 98 | 99 |
100 | 101 | ### XMLHttpRequest 102 | 브라우저는 HTTP 요청을 전송하려면 `XMLHttpRequest` 객체를 사용해야 한다. 103 | Web API인 이 객체는 HTTP 요청과 응답을 위한 다양한 메서드와 프로퍼티를 제공한다. 104 | 105 | - [Ajax API 비교 - XMLHttpRequest vs Fetch](https://junhobaik.github.io/ajax-xhr-fetch/) 106 | 107 |


108 | 109 | # 44장 - REST API 110 | > **💡 REST** 111 | REST(REpresentational State Transfer)는 HTTP/1.0과 1.1의 스펙 작성에 참여했다. 112 | REST를 처음소개한 로이 필딩은 당시 웹이 HTTP를 제대로 사용하지 못하고 있는 상황을 보고 **HTTP의 장점을 최대한 활용할 수 있는 아키텍쳐로 REST를 소개**했다. 113 | > 114 | > **`REST`** : HTTP를 기반으로 클라이언트가 서버의 리소스에 접근하는 방식을 규정한 아키텍쳐 115 | **`RESTful`** : REST 아키텍쳐의 기본 원칙을 성실히 지킨 서비스를 지칭하는 말 116 | **`REST API`** : REST를 기반으로 서비스 API를 구현한 것 117 | 118 |
119 | 120 | ### REST API의 구성 121 | REST API는 자원, 행위, 표현 이렇게 3가지 요소로 구성된다. 122 | 123 |
124 | 125 | |구성 요소 | 내용 | 표현 방법| 126 | |:---|:---|:---| 127 | |자원 | 자원의 위치 | URI | 128 | |행위 | 자원에 대한 행위 | HTTP 요청 메서드 | 129 | |표현 | 행위에 대한 구체적인 내용 | 페이로드 | 130 | 131 |
132 | 133 | ### REST의 기본원칙 134 | - [RESTful 문서](https://restfulapi.net/) 135 | 136 |
137 | 138 | ### REST API 설계원칙 139 | 1. URI는 리소스를 표현해야한다. 140 | - 리소스를 표현하는데 중점을 두어야 한다. 141 | - 리소소를 식별할 수 있는 일므은 동사보다는 명사를 사용한다. 142 | 2. 리소스에 대한 행위는 HTTP 요청 메서드로 표현한다. 143 | 144 |


145 | 146 | # 45장 - 프로미스 147 | 전통적인 콜백 패턴은 아래와 같은 문제가 있다. 148 | - 비동기 처리 결과값을 반환하기 힘듬 149 | - 비동기 함수의 처리 결과에 대한 후속 처리는 비동기 함수 내부에서 수행해야 한다. 150 | - 범용적으로 사용하기 위해서 후속 처리를 수행하는 콜백함수를 전달함. 151 | - 콜백 헬로 인해 가독성이 나쁨 152 | - 콜백 함수 호출이 중첩되면 복잡도가 높아지는 현상 153 | - 비동기 처리 중 발생한 에러의 처리가 곤란 154 | ```js 155 | try { 156 | setTimeout(() => { throw new Error('Error!'); }, 1000); 157 | } catch (e) { 158 | // 에러를 캐치하지 못한다 159 | console.error('캐치한 에러', e); 160 | } 161 | ``` 162 | - 비동기 처리를 한 번에 처리하는 데도 한계 163 | 164 |
165 | 166 | ### 프로미스의 등장 167 | 비동기 처리를 위한 콜백 패턴은 콜백 헬이나 에러 처리가 곤란한다는 문제가 있다. 168 | 이런 문제를 해결하기 위해 ES6부터는 프로미스가 등장하였다. 169 | 170 | Promise 생성자 함수는 비동기 처리를 수행할 콜백함수를 인수로 전달받는다. 171 | 이 콜백함수에는 `resolve`와 `reject` 함수를 인수로 전달받는다. 172 | 173 | 전달 받은 콜백 함수 내부에서 비동기 처리를 수행한다. 174 | 이때 비동기 처리가 성공하면 인수로 전달받은 `resolve` 함수를 호출하고 175 | 실행하면 `reject` 함수를 호출한다. 176 | 177 | ```js 178 | // GET 요청을 위한 비동기 함수 179 | const promiseGet = url => { 180 | return new Promise((resolve, reject) => { 181 | const xhr = new XMLHttpRequest(); 182 | xhr.open('GET', url); 183 | xhr.send(); 184 | 185 | xhr.onload = () => { 186 | if (xhr.status === 200) { 187 | // 성공적으로 응답을 전달받으면 resolve 함수를 호출한다. 188 | resolve(JSON.parse(xhr.response)); 189 | } else { 190 | // 에러 처리를 위해 reject 함수를 호출한다. 191 | reject(new Error(xhr.status)); 192 | } 193 | }; 194 | }); 195 | }; 196 | 197 | // promiseGet 함수는 프로미스를 반환한다. 198 | promiseGet('https://jsonplaceholder.typicode.com/posts/1'); 199 | ``` 200 | 201 |
202 | 203 | 프로미스는 현재 비동기 처리가 어떻게 진행되고 있는 나타내는 상태정보를 가지고 있다. 204 | `pending` : 비동기 처리가 아직 수행되지 않은 상태 205 | `fulfilled` : 비동기 처리가 수행된 상태(성공) -> resolve 함수 호출 되었을때의 상태 206 | `rejected` : 비동기 처리가 수행된 상태(실패) -> reject 함수 호출 되었을때의 상태 207 | 208 |
209 | 210 | ### 프로미스의 후속 처리 메서드 211 | 프로미스의 처리 상태가 변화하면 이에 따른 후속 처리를 해야한다. 212 | `fulfilled` 상태라면 처리 결과를 가지고 있고 `rejected` 상태라면 에러를 가지고 있으니 에러처리를 해야한다. 213 | 후속 처리를 위해 `then`, `catch`, `finally` 메서드를 제공한다. 214 | 215 | 1. **`then`** 216 | - 두 개의 콜백함수를 인수로 전달받는다. 217 | - `fulfilled` 상태가 되면 호출되는 함수(프로미스 처리 결과를 인수로 받음) 218 | - `rejected` 상태가 되면 호출되는 함수(프로미스 처리 결과, 즉 에러를 인수로 받음) 219 | - 언제나 프로미스를 반환한다. 콜백함수의 반환값이 프로미스가 아니라면 프로미스로 생성해 반환한다. 220 | 2. **`catch`** 221 | - 한 개의 콜백함수를 인수로 받는다. 222 | - `rejected` 상태일 경우만 호출된다. 223 | - `then`과 마찬가지로 언제나 프로미스를 반환한다. 224 | - `then` 이후에 작성한다면 `then` 메서드 내부에서 발생한 에러까지 모두 캐치할 수 있다. 225 | 3. **`finally`** 226 | - 한 개의 콜백함수를 인수로 받는다. 227 | - 성공이냐 실패하냐와 상관없이 무조건 한 번 호출된다. 228 | - 프로미스 상태와 상관없이 공통적으로 수행해야 할 처리 내용이 있을 때 유용하다. 229 | - 마찬가지로 언제나 프로미스를 반환한다. 230 | 231 |
232 | 233 | ### 프로미스의 정적 메서드 234 | 1. **`Promise.resolve / Promise.reject`** 235 | - 이미 존재하는 값을 래핑하여 프로미스를 생성하기 위해 사용한다. 236 | 2. **`Promise.all`** 237 | - 여러 개의 비동기 처리를 모두 병렬로 처리할때 사용한다. 238 | - 모두 `fulfilled`가 되면 종료한다. 239 | - 하나라도 `rejected` 상태가 되면 나머지를 기다리지 않고 즉시 종료한다. 240 | 3. **`Promise.race`** 241 | - 여러 개의 비동기 처리를 모두 병렬로 처리하며 가장 먼저 처리된 결과를 프로미스로 반환한다. 242 | - 하나라도 `rejected` 상태가 되면 즉시 종료한다. 243 | 4. **`Promise.allSettled`** 244 | - 여러 개의 비동기 처리를 모두 병렬로 처리하며 모두 settled 상태(`fulfilled` or `rejected`)가 되면 처리결과를 배열로 반환한다. 245 | 246 |
247 | 248 | ### 마이크로태스크 큐 249 | 태스크 큐와 별도로 마이크로태스크 큐가 존재한다. 250 | 프로미스의 후속 처리 메서드의 콜백함수는 태스크 큐가 아니라 마이크로태스크 큐에 저장이되며 이 큐는 태스크 큐보다 우선순위가 높다. 251 | 마이크로태스크 큐가 비면 태스크 큐에서 대기하고 있는 함수를 가져와 실행한다. 252 | 253 | - [이벤트 루프와 태스크 큐 참고 블로그](https://evan-moon.github.io/2019/08/01/nodejs-event-loop-workflow/) 254 | 255 |
256 | 257 | ### fetch 258 | fetch는 최근에 추가된 Web API로 대부분의 모던 브라우저에서 제공한다. 259 | fetch 함수는 HTTP 응답을 나타내는 `Response` 객체를 래핑한 `Promise` 객체를 반환한다. 260 | `Response` 의 프로토타입에는 HTTP 응답 몸체를 위한 다양한 메서드를 제공한다. 261 | - `Response.prototype.json` : HTTP 응답 몸체를 취득하여 역직렬화 262 | 263 | 264 |
265 | 266 | **fetch 사용시 주의할 점** 267 | - fetch 함수가 반환하는 프로미스는 404, 500과 같은 HTTP 에러가 발생해도 reject하지 않음 268 | - reponse 객체의 프로퍼티인 ok를 false로 설정한 Response 객체를 resolve 한다. 269 | - 오프라인 등 네트워크 장애나 CORS 에러에 의해 요청이 완료되지 못한 경우에만 프로미스를 reject한다. 270 | 271 |
272 | 273 | > **💡 axios** 274 | 모든 HTTP 에러를 reject하는 프로미스를 반환한다. 따라서 모든 에러를 catch에서 처리할 수 있어 편리하다. 275 | 인터셉터, 요청 설정 등 fetch 보다 다양한 기능을 지원한다. 276 | 277 | -------------------------------------------------------------------------------- /12회차/박수진.md: -------------------------------------------------------------------------------- 1 | # 42장 비동기 프로그래밍 2 | 3 | ## 42.1 동기 처리와 비동기 처리 4 | 5 | ```jsx 6 | const foo = () => {}; 7 | const bar = () => {}; 8 | 9 | foo(); 10 | bar(); 11 | ``` 12 | 13 | <사진> 14 | 15 | 자바스크립트 엔진은 한 번에 하나의 태스크만 실행할 수 있는 **싱글 스레드 방식**으로 동작한다. 16 | 17 | 싱글 스레드 방식은 한 번에 하나의 태스크만 실행할 수 있기 때문에 처리에 시간이 걸리는 태스크를 실행하는 경우 **블로킹 (작업 중단)** 이 발생한다. 18 | 19 | ```jsx 20 | // sleep 함수는 일정 시간(delay)이 경과한 이후에 콜백 함수(func)를 호출한다. 21 | function sleep(func, delay) { 22 | // Date.now()는 현재 시간을 숫자(ms)로 반환한다.("30.2.1. Date.now" 참고) 23 | const delayUntil = Date.now() + delay; 24 | 25 | // 현재 시간(Date.now())에 delay를 더한 delayUntil이 현재 시간보다 작으면 계속 반복한다. 26 | while (Date.now() < delayUntil); 27 | // 일정 시간(delay)이 경과한 이후에 콜백 함수(func)를 호출한다. 28 | func(); 29 | } 30 | 31 | function foo() { 32 | console.log('foo'); 33 | } 34 | 35 | function bar() { 36 | console.log('bar'); 37 | } 38 | 39 | // sleep 함수는 3초 이상 실행된다.. 40 | sleep(foo, 3 * 1000); 41 | // bar 함수는 sleep 함수의 실행이 종료된 이후에 호출되므로 3초 이상 블로킹된다. 42 | bar(); 43 | // (3초 경과 후) foo 호출 -> bar 호출 44 | ``` 45 | 46 | 현재 실행 중인 태스크가 종료할 때까지 다음에 실행될 태스크가 대기하는 방식을 **동기 처리** 라고 한다. 47 | 48 | → 동기 처리 방식은 태스크를 순서대로 하나씩 처리하므로 실행 순서가 보장되지만, 태스크가 종료할 때까지 이후 태스크들이 블로킹되는 단점이 있다. 49 | 50 | <사진> 51 | 52 | ```jsx 53 | function foo() { 54 | console.log('foo'); 55 | } 56 | 57 | function bar() { 58 | console.log('bar'); 59 | } 60 | 61 | // 타이머 함수 setTimeout은 일정 시간이 경과한 이후에 콜백 함수 foo를 호출한다. 62 | // 타이머 함수 setTimeout은 bar 함수를 블로킹하지 않는다. 63 | setTimeout(foo, 3 * 1000); 64 | bar(); 65 | // bar 호출 -> (3초 경과 후) foo 호출 66 | ``` 67 | 68 | setTimeout 함수는 setTimeout 함수 이후의 태스크를 블로킹하지 않고 곧바로 실행한다. 69 | 70 | 현재 실행 중인 태스크가 종료되지 않은 상태라 해도 다음 태스크를 곧바로 실행하는 방식을 **비동기 처리** 라고 한다. 71 | 72 | <사진> 73 | 74 | **타이머 함수인 setTimeout, setInterval, HTTP 요청, 이벤트 핸들러는 비동기 처리 방식으로 동작한다.** 75 | 76 | ## 42.2 이벤트 루프와 태스크 큐 77 | 78 | **이벤트 루프** 79 | 80 | 자바스크립트의 동시성을 지원하는 것 81 | 82 | HTML 요소가 애니메이션 효과를 통해 움직이면서 이벤트를 처리하기도 하고, HTTP 요청을 통해 서버로부터 데이터를 가지고 오면서 렌더링하기도 하는 것이다. 83 | 84 | <사진> 85 | 86 | # 43장 Ajax 87 | 88 | ## 43.2 JSON 89 | 90 | - 클라이언트와 서버 간의 HTTP 통신을 위한 텍스트 데이터 포맷 91 | - 자바스크립트에 종속되지 않는 언어 독립형 데이터 포맷으로 대부분의 프로그래밍 언어에서 사용할 수 있다. 92 | 93 | ### 43.2.1 JSON 표기 방식 94 | 95 | ```jsx 96 | { 97 | "name": "Lee", 98 | "age": 20, 99 | "alive": true, 100 | "hobby": ["traveling", "tennis"] 101 | } 102 | ``` 103 | 104 | 키는 반드시 큰따옴표로 묶어야 한다. 값은 객체 리터럴과 같은 표기법을 그대로 사용할 수 있다. 105 | 106 | ⚠️ 작은따옴표 사용 불가 107 | 108 | ### 43.2.2 JSON.stringify 109 | 110 | JSON.stringify 메서드는 객체를 JSON 포맷의 문자열로 변환한다. 111 | 112 | **직렬화란?** 113 | 114 | 클라이언트가 서버로 객체를 전송하기 위해 객체를 문자열화하는 것 115 | 116 | ```jsx 117 | const obj = { 118 | name: 'Lee', 119 | age: 20, 120 | alive: true, 121 | hobby: ['traveling', 'tennis'] 122 | }; 123 | 124 | // 객체를 JSON 포맷의 문자열로 변환한다. 125 | const json = JSON.stringify(obj); 126 | console.log(typeof json, json); 127 | // string {"name":"Lee","age":20,"alive":true,"hobby":["traveling","tennis"]} 128 | 129 | // 객체를 JSON 포맷의 문자열로 변환하면서 들여쓰기 한다. 130 | const prettyJson = JSON.stringify(obj, null, 2); 131 | console.log(typeof prettyJson, prettyJson); 132 | /* 133 | string { 134 | "name": "Lee", 135 | "age": 20, 136 | "alive": true, 137 | "hobby": [ 138 | "traveling", 139 | "tennis" 140 | ] 141 | } 142 | */ 143 | 144 | // replacer 함수. 값의 타입이 Number이면 필터링되어 반환되지 않는다. 145 | function filter(key, value) { 146 | // undefined: 반환하지 않음 147 | return typeof value === 'number' ? undefined : value; 148 | } 149 | 150 | // JSON.stringify 메서드에 두 번째 인수로 replacer 함수를 전달한다. 151 | const strFilteredObject = JSON.stringify(obj, filter, 2); 152 | console.log(typeof strFilteredObject, strFilteredObject); 153 | /* 154 | string { 155 | "name": "Lee", 156 | "alive": true, 157 | "hobby": [ 158 | "traveling", 159 | "tennis" 160 | ] 161 | } 162 | */ 163 | ``` 164 | 165 | **배열 → JSON 포맷의 문자열로 변환하는 예시** 166 | 167 | ```jsx 168 | const todos = [ 169 | { id: 1, content: 'HTML', completed: false }, 170 | { id: 2, content: 'CSS', completed: true }, 171 | { id: 3, content: 'Javascript', completed: false } 172 | ]; 173 | 174 | // 배열을 JSON 포맷의 문자열로 변환한다. 175 | const json = JSON.stringify(todos, null, 2); 176 | console.log(typeof json, json); 177 | /* 178 | string [ 179 | { 180 | "id": 1, 181 | "content": "HTML", 182 | "completed": false 183 | }, 184 | { 185 | "id": 2, 186 | "content": "CSS", 187 | "completed": true 188 | }, 189 | { 190 | "id": 3, 191 | "content": "Javascript", 192 | "completed": false 193 | } 194 | ] 195 | */ 196 | ``` 197 | 198 | ### 43.2.3 JSON.parse 199 | 200 | **역직렬화** 201 | 202 | 문자열을 객체로서 사용하려면 JSON 포맷의 문자열을 객체화하는 것 203 | 204 | ```jsx 205 | const obj = { 206 | name: 'Lee', 207 | age: 20, 208 | alive: true, 209 | hobby: ['traveling', 'tennis'] 210 | }; 211 | 212 | // 객체를 JSON 포맷의 문자열로 변환한다. 213 | const json = JSON.stringify(obj); 214 | 215 | // JSON 포맷의 문자열을 객체로 변환한다. 216 | const parsed = JSON.parse(json); 217 | console.log(typeof parsed, parsed); 218 | // object {name: "Lee", age: 20, alive: true, hobby: ["traveling", "tennis"]} 219 | ``` 220 | 221 | JSON 포맷의 문자열 → 배열로 변환 222 | 223 | ```jsx 224 | const todos = [ 225 | { id: 1, content: 'HTML', completed: false }, 226 | { id: 2, content: 'CSS', completed: true }, 227 | { id: 3, content: 'Javascript', completed: false } 228 | ]; 229 | 230 | // 배열을 JSON 포맷의 문자열로 변환한다. 231 | const json = JSON.stringify(todos); 232 | 233 | // JSON 포맷의 문자열을 배열로 변환한다. 배열의 요소까지 객체로 변환된다. 234 | const parsed = JSON.parse(json); 235 | console.log(typeof parsed, parsed); 236 | /* 237 | object [ 238 | { id: 1, content: 'HTML', completed: false }, 239 | { id: 2, content: 'CSS', completed: true }, 240 | { id: 3, content: 'Javascript', completed: false } 241 | ] 242 | */ 243 | ``` 244 | 245 | ### 43.3 XMLHttpRequest 246 | 247 | 자바스크립트를 사용하여 HTTP 요청을 전송하기 위해 XMLHttpRequest 객체를 사용한다. 248 | 249 | Web API 인 XMLHttpRequest 객체는 HTTP 요청 전송, HTTP 응답 수신을 윟나 다양한 메서드와 프로퍼티가 있다. 250 | 251 | 255 | 256 | ### 43.3.4 HTTP 응답 처리 257 | 258 | XMLHttpRequest 객체는 `onreadystatechange, onload, onerror` 같은 이벤트 핸들러 프로퍼티를 갖는다. 259 | 260 | HTTP 요청의 현재 상태를 readyState 프로퍼티 값이 변경된 경우 발생하는 readystatechange 이벤트를 캐치하여 HTTP 응답을 처리할 수 있다. 261 | 262 | ```jsx 263 | // XMLHttpRequest 객체 생성 264 | const xhr = new XMLHttpRequest(); 265 | 266 | // HTTP 요청 초기화 267 | // https://jsonplaceholder.typicode.com은 Fake REST API를 제공하는 서비스다. 268 | xhr.open('GET', 'https://jsonplaceholder.typicode.com/todos/1'); 269 | 270 | // HTTP 요청 전송 271 | xhr.send(); 272 | 273 | // readystatechange 이벤트는 HTTP 요청의 현재 상태를 나타내는 readyState 프로퍼티가 274 | // 변경될 때마다 발생한다. 275 | xhr.onreadystatechange = () => { 276 | // readyState 프로퍼티는 HTTP 요청의 현재 상태를 나타낸다. 277 | // readyState 프로퍼티 값이 4(XMLHttpRequest.DONE)가 아니면 서버 응답이 완료되지 상태다. 278 | // 만약 서버 응답이 아직 완료되지 않았다면 아무런 처리를 하지 않는다. 279 | if (xhr.readyState !== XMLHttpRequest.DONE) return; 280 | 281 | // status 프로퍼티는 응답 상태 코드를 나타낸다. 282 | // status 프로퍼티 값이 200이면 정상적으로 응답된 상태이고 283 | // status 프로퍼티 값이 200이 아니면 에러가 발생한 상태다. 284 | // 정상적으로 응답된 상태라면 response 프로퍼티에 서버의 응답 결과가 담겨 있다. 285 | if (xhr.status === 200) { 286 | console.log(JSON.parse(xhr.response)); 287 | // {userId: 1, id: 1, title: "delectus aut autem", completed: false} 288 | } else { 289 | console.error('Error', xhr.status, xhr.statusText); 290 | } 291 | }; 292 | ``` 293 | 294 | # 44장 REST API 295 | 296 | **REST** 는 HTTP 를 기반으로 클라이언트가 서버의 리소스에 접근하는 방식을 규정한 아키텍처 297 | 298 | **REST API** 는 REST 를 기반으로 서비스 API 를 구현한 것 299 | 300 | ## 44.1 REST API 의 구성 301 | 302 | REST API 는 자원, 행위, 표현의 3가지 요소로 구성된다. REST 는 자체 표현 구조로 구성되어 REST API 만으로 HTTP 요청의 내용을 이해할 수 있다. 303 | 304 | | 구성 요소 | 내용 | 표현 방법 | 305 | | --- | --- | --- | 306 | | 자원 (resource) | 자원 | URI (엔드포인트) | 307 | | 행위 (verb) | 자원에 대한 행위 | HTTP 요청 메서드 | 308 | | 표현 (representations) | 자원에 대한 행위의 구체적 내용 | 페이로드 | 309 | 310 | ## 44.2 REST API 설계 원칙 311 | 312 | **REST 에서 가장 중요한 기본적인 원칙 두 가지** 313 | 314 | - URI 는 리소스를 표현하는 데 집중 315 | - 행위에 대한 정의는 HTTP 요청 메서드를 통해 하는 것 316 | 317 | 1. URI 는 리소스를 표현해야 한다. 318 | - URI 는 리소스를 표현하는 데 중점을 두어야 한다. 319 | - 리소스를 식별할 수 있는 이름은 동사보다는 명사를 사용한다. 320 | 321 | ```jsx 322 | # bad 323 | GET /getTodos/1 324 | GET /todos/show/1 325 | 326 | # good 327 | GET /todos/1 328 | ``` 329 | 330 | 331 | 1. 리소스에 대한 행위는 HTTP 요청 메서드로 표현한다. 332 | 333 | **클라이언트가 서버에게 요청의 종류와 목적을 알리는 방법** 334 | 335 | | HTTP 요청 메서드 | 종류 | 목적 | 페이로드 | 336 | | --- | --- | --- | --- | 337 | | GET | index/retrieve | 모든/특정 리소스 취득 | X | 338 | | POST | create | 리소스 생성 | O | 339 | | PUT | replace | 리소스의 전체 교체 | O | 340 | | PATCH | modify | 리소스의 일부 수정 | O | 341 | | DELETE | delete | 모든/특정 리소스 삭제 | X | 342 | 343 | 리소스에 대한 행위는 URI 에 표현하지 않고, 요청 메서드로 리소스에 대한 행위를 명확히 표현한다. 344 | 345 | 346 | ## 44.3 JSON Server 를 이용한 REST API 실습 347 | 348 | ### GET 요청 349 | 350 | todo 리소스에서 모든 todo 를 취득 (index) 하거나 id 를 사용하여 특정 todo 를 취득 (retrieve) 할 수 있다. 351 | 352 | ### POST 요청 353 | 354 | POST 요청 시에는 setRequestHeader 메서드를 사용하여 요청 바디에 담아 서버로 전송할 페이로드의 MIME 타입을 지정해야 한다. 355 | 356 | ### PUT 요청 357 | 358 | PUT 은 특정 리소스 전체를 교체할 때 사용한다. 359 | 360 | todo 를 특정하여 id 를 제외한 리소스 전체를 교체하고, setRequestHeader 메서드를 사용하여 요청 바디에 담아 서버로 전송할 페이로드의 MIME 타입을 지정해야 한다. 361 | 362 | ### PATCH 요청 363 | 364 | PATCH 는 특정 리소스의 일부를 수정할 때 사용한다. 365 | 366 | setRequestHeader 메서드를 사용하여 요청 바디에 담아 서버로 전송할 페이로드의 MIME 타입을 지정해야 한다. 367 | 368 | ### DELETE 요청 369 | 370 | todos 리소스에서 id 를 사용하여 todo 를 삭제한다. -------------------------------------------------------------------------------- /12회차/병현.md: -------------------------------------------------------------------------------- 1 | 2 | ## 동기와 비동기 3 | 4 | ```js 5 | // sleep 함수는 일정 시간(delay)이 경과한 이후에 콜백 함수(func)를 호출한다. 6 | function sleep(func, delay) { 7 | // Date.now()는 현재 시간을 숫자(ms)로 반환한다.("30.2.1. Date.now" 참고) 8 | const delayUntil = Date.now() + delay; 9 | 10 | // 현재 시간(Date.now())에 delay를 더한 delayUntil이 현재 시간보다 작으면 계속 반복한다. 11 | while (Date.now() < delayUntil); 12 | // 일정 시간(delay)이 경과한 이후에 콜백 함수(func)를 호출한다. 13 | func(); 14 | } 15 | 16 | function foo() { 17 | console.log('foo'); 18 | } 19 | 20 | function bar() { 21 | console.log('bar'); 22 | } 23 | 24 | // sleep 함수는 3초 이상 실행된다.. 25 | sleep(foo, 3 * 1000); 26 | // bar 함수는 sleep 함수의 실행이 종료된 이후에 호출되므로 3초 이상 블로킹된다. 27 | bar(); 28 | // (3초 경과 후) foo 호출 -> bar 호출 29 | ``` 30 | 31 | 자바스크립트는 하나의 콜 스택을 사용하기 때문에 위 코드와 같이 중간에 작업을 중단시킬 수 있는 동기 적인 형태를 가지고 있다 32 | 33 | ```js 34 | function foo() { 35 | console.log('foo'); 36 | } 37 | 38 | function bar() { 39 | console.log('bar'); 40 | } 41 | 42 | // 타이머 함수 setTimeout은 일정 시간이 경과한 이후에 콜백 함수 foo를 호출한다. 43 | // 타이머 함수 setTimeout은 bar 함수를 블로킹하지 않는다. 44 | setTimeout(foo, 3 * 1000); 45 | bar(); 46 | // bar 호출 -> (3초 경과 후) foo 호출 47 | ``` 48 | 49 | 하지만 위와 같이 `setTimeout` 같은 브라우저가 관리하는 api를 사용하게 되면 비동기로 작동하게 된다 50 | 51 | `setTimeout, setInterval` HTTP요청, 이벤트 핸들러는 비동기로 작동한다. (click, blur, focus, 커스텀 이벤트 등은 동기로 작동한다) 52 | 53 | ## 이벤트 루프와 태스크 큐 54 | 55 | ### 콜 스택 56 | 57 | 소스코드 평가 과정에서 생성된 실행 컨텍스트를 저장하며 순차적으로 함수를 푸시하고 실행된다. 콜 스택이 비워지기 전까지는 다른 태스크는 실행되지 않는다 58 | 59 | ### 힙 60 | 61 | 객체를 저장하는 메모리 공간이다. 콜 스택의 요소인 실행 컨텍스트는 힙에 저장된 객체를 참조한다. 객체는 크기가 정해져 있지 않기 때문에 런타임에 메모리 공간의 크기를 동적으로 할당한다. 원시 값도 결국 객체이기 때문에 힙에 저장된다 볼 수 있다 62 | 63 | 비동기 처리에서 소스코드의 평가와 실행을 제외한 모든 처리는 자바스크립트 엔진을 구동하는 환경인 브라우저 또는 Node.js가 담당한다 64 | 65 | ### 태스크 큐 66 | 67 | `setTimeout` 같은 비동기 함수의 콜백 함수 또는 이벤트 핸들러가 일시적으로 보관되는 영역, 태스크 큐와 별도로 프로미스의 후속 처리 메서드의 함수가 일시적으로 보관되는 마이크로태스크 큐도 존재함 68 | 69 | ### 이벤트 루프 70 | 71 | 콜 스택에 현재 실행 중인 실행 컨텍스트가 있는지, 태스크 큐에 대기 중인 함수가 있는지 확인한다. 콜 스택이 비어 있고 태스크 큐에 대기 중인 함수가 있다면 이벤트 루프는 순차적으로 태스크 큐에 대기 중인 함수를 콜 스택으로 이동시킨다 72 | 73 | --- 74 | 75 | ```js 76 | function foo() { 77 | console.log('foo'); 78 | } 79 | 80 | function bar() { 81 | console.log('bar'); 82 | } 83 | 84 | setTimeout(foo, 0); // 0초(실제는 4ms) 후에 foo 함수가 호출된다. 85 | bar(); 86 | ``` 87 | 88 | 위 코드의 동작 과정을 알아보자 89 | 90 | 1. 전역 코드가 평가되어 전역 실행 컨텍스트가 생성되고 콜 스택에 푸시됨 91 | 92 | 2. 전역 코드가 실행되기 시작하여 `setTimeout` 함수가 호출된다. `setTimeout` 함수의 함수 실행 컨텍스트가 생성되고 콜 스택에 푸쉬되어 실행 중인 컨텍스트가 된다. 브라우저의 API도 함수이므로 함수 실행 컨텍스트를 생성한다 93 | 94 | 3. `setTimeout` 함수가 실행되면 콜백 함수를 호출 스케일링하고 종료되어 콜 스택에서 팝 된다. 이 역할은 브라우저의 역할이다 95 | 96 | 4. 브라우저가 수행하는 4-1과 자바스크립트 엔진이 수행하는 4-2가 병행으로 동작한다 97 | 98 | 4-1. 브라우저는 타이머를 설정하고 만료를 기다린다. 타이머가 만료되면 콜백 함수 `foo` 가 태스크 큐에 푸시된다. 타이머의 지연 시간이 4ms 이하인 경우 최소 지연 시간 4ms가 지정된다. 이후 `foo` 가 태스크 큐에 푸시되어 대기한다 99 | 100 | 4-2. `bar` 함수가 호출되어 실행 컨텍스트가 생성되고 콜 스택에 푸시되어 현재 실행 중인 컨텍스트가 된다. 이후 종료되어 팝 된다. 4ms가 경과했다면 아직 `foo` 함수는 아직 태스크 큐에서 대기 중이다 101 | 102 | 5. 전역 코드 실행이 종료되고 팝 되고 콜 스택이 비게된다 103 | 104 | 6. 이벤트 루프가 콜 스택이 비어 있음을 감지하여 `foo` 함수가 콜 스택에 푸시되고 실행된 후 팝된다. 105 | 106 | 107 | 이러하게 태스크 큐에 들어간 콜백 함수는 콜 스택이 비게 되면, 푸시되어 실행된다. 108 | 109 | 자바스크립트는 싱글 스레드 방식이지만, 브라우저에서는 브라우저의 역할도 있으므로 멀티 스레드로 동작한다. 110 | 111 | ## REST API 112 | 113 | 자원, 행위, 표현으로 API를 구성하는거를 REST API라하고 이런 API 구성을 지키는 것을 RESTful하다고 한다 114 | 115 | URI는 리소스를 표현하는데 집중하고 행위에 대한 정의 HTTP 요청 메서드를 통해 하는게 중심 규칙이다 116 | 117 | ```js 118 | # BAD 119 | 120 | GET /getTodos/1 121 | GET /todos/show/1 122 | 123 | # GOOD 124 | 125 | GET /todos/1 126 | ``` 127 | 128 | ## 비동기 처리 129 | 130 | ```js 131 | let todos; 132 | 133 | // GET 요청을 위한 비동기 함수 134 | const get = url => { 135 | const xhr = new XMLHttpRequest(); 136 | xhr.open('GET', url); 137 | xhr.send(); 138 | 139 | xhr.onload = () => { 140 | if (xhr.status === 200) { 141 | // ① 서버의 응답을 상위 스코프의 변수에 할당한다. 142 | todos = JSON.parse(xhr.response); 143 | } else { 144 | console.error(`${xhr.status} ${xhr.statusText}`); 145 | } 146 | }; 147 | }; 148 | 149 | // id가 1인 post를 취득 150 | get(''); 151 | console.log(todos); // ② undefined 152 | ``` 153 | 154 | `xhr.onload` 가 동작하는 경우에 바인딩된 핸들러는 즉시 실행 하는 것이 아니라 태스크 큐에 저장되어 대기하다가, 콜 스택이 비면 이벤트 루프에 의해 콜 스택으로 푸쉬된다. 따라서 위 코드의 로그에 값이 나오지 않는다. 왜냐하면 콜 스택이 비어야 태스크 큐에서 콜 스택으로 푸쉬되는데 이때는 콘솔 로그가 종료된 이후이기 때문이다 155 | 156 | ```js 157 | // GET 요청을 위한 비동기 함수 158 | const get = (url, successCallback, failureCallback) => { 159 | const xhr = new XMLHttpRequest(); 160 | xhr.open('GET', url); 161 | xhr.send(); 162 | 163 | xhr.onload = () => { 164 | if (xhr.status === 200) { 165 | // 서버의 응답을 콜백 함수에 인수로 전달하면서 호출하여 응답에 대한 후속 처리를 한다. 166 | successCallback(JSON.parse(xhr.response)); 167 | } else { 168 | // 에러 정보를 콜백 함수에 인수로 전달하면서 호출하여 에러 처리를 한다. 169 | failureCallback(xhr.status); 170 | } 171 | }; 172 | }; 173 | 174 | // id가 1인 post를 취득 175 | // 서버의 응답에 대한 후속 처리를 위한 콜백 함수를 비동기 함수인 get에 전달해야 한다. 176 | get('', console.log, console.error); 177 | /* 178 | { 179 | "userId": 1, 180 | "id": 1, 181 | "title": "sunt aut facere ...", 182 | "body": "quia et suscipit ..." 183 | } 184 | */ 185 | ``` 186 | 187 | 위와 같이 처리가 성공 혹은 실패 했을 경우의 콜백 함수를 넘기는 방법을 사용할 수 있다 188 | 189 | ```js 190 | try { 191 | setTimeout(() => { throw new Error('Error!'); }, 1000); 192 | } catch (e) { 193 | // 에러를 캐치하지 못한다 194 | console.error('캐치한 에러', e); 195 | } 196 | ``` 197 | 198 | `setTimeout` 함수의 콜백 함수가 실행될 때는 `setTimeout` 가 콜 스택에서 제거된 상태다. 따라서, 콜백 함수를 호출한 것이 `setTimeout` 함수가 아니다. 에러는 호출자 방향으로 전파되기 때문에 위 코드의 경우 에러를 캐치하지 못하게 된다 199 | 200 | ## Promise 201 | 202 | `Promise` 는 `ES6` 에 도입된 표준 빌트인 객체다 203 | 204 | ```js 205 | // 프로미스 생성 206 | const promise = new Promise((resolve, reject) => { 207 | // Promise 함수의 콜백 함수 내부에서 비동기 처리를 수행한다. 208 | if (/* 비동기 처리 성공 */) { 209 | resolve('result'); 210 | } else { /* 비동기 처리 실패 */ 211 | reject('failure reason'); 212 | } 213 | }); 214 | ``` 215 | 216 | ### Promise.prototype.then / Promise.prototype.catch 217 | 218 | ```js 219 | // fulfilled 220 | new Promise(resolve => resolve('fulfilled')) 221 | .then(v => console.log(v), e => console.error(e)); // fulfilled 222 | 223 | // rejected 224 | new Promise((_, reject) => reject(new Error('rejected'))) 225 | .then(v => console.log(v), e => console.error(e)); // Error: rejected 226 | 227 | ``` 228 | 229 | 첫 번째 인수에는 `fulfilled` 되었을 때 처리될 콜백 함수를, 두 번째 인수에는 `rejected` 되었을 때 처리될 콜백 함수를 넣을 수 있다 230 | 231 | `then` 은 언제나 프로미스를 반환한다. 콜백 함수가 프로미스를 반환하면 그 프로미스를 그대로 반환하고 프로미스가 아닌 값을 반환하면 암묵적으로 `resolve` 또는 `rejected` 하여 프로미스를 생성해 반환한다 232 | 233 | ```js 234 | // rejected 235 | new Promise((_, reject) => reject(new Error('rejected'))) 236 | .catch(e => console.log(e)); // Error: rejected 237 | ``` 238 | 239 | 프로미스가 `rejeccted` 인 경우 호출되는 된다 240 | 241 | ```js 242 | // rejected 243 | new Promise((_, reject) => reject(new Error('rejected'))) 244 | .then(undefined, e => console.log(e)); // Error: rejected 245 | ``` 246 | 247 | 위 코드도 같은 동작을 한다 248 | 249 | ## 프로미스의 에러처리 250 | 251 | ```js 252 | promiseGet('').then( 253 | res => console.xxx(res), 254 | err => console.error(err) 255 | ); // 두 번째 콜백 함수는 첫 번째 콜백 함수에서 발생한 에러를 캐치하지 못한다. 256 | ``` 257 | 258 | `then` 에서 두 인수로 처리 가능하지만 첫 번째 콜백 함수의 에러를 캐치하지 못하기 때문에 아래 코드와 같이 사용하는 것이 좋다 259 | 260 | ```js 261 | promiseGet('') 262 | .then(res => console.xxx(res)) 263 | .catch(err => console.error(err)); // TypeError: console.xxx is not a function 264 | ``` 265 | 266 | ## 마이크로태스크 큐 267 | 268 | ```js 269 | setTimeout(() => console.log(1), 0); 270 | 271 | Promise.resolve() 272 | .then(() => console.log(2)) 273 | .then(() => console.log(3)); 274 | ``` 275 | 276 | 위의 코드는 1 → 2 → 3 으로 출력될 것 같지만, 2 → 3 → 1 순으로 출력된다. 프로미스의 후속 처리 메서드의 콜백 함수는 마이크로태스크 큐에 저장되고 태스크 큐보다 우선순위가 높기 때문이다 277 | -------------------------------------------------------------------------------- /1회차/README.md: -------------------------------------------------------------------------------- 1 | ### 5장 표현식과 문 - 완료값에 관하여 2 | - 면접에서 자주 출제되므로 잘 익혀봅시다. 3 | - p.58 참고 4 | 5 | ### 숫자 구분자 추가 Numeric Separotor 6 | - [참고 링크](https://dev.to/suprabhasupi/numeric-separators-in-javascript-3jec) 7 | 8 | ### 타입 캐스팅 더 알아보기 9 | - 타입 캐시팅 시 생성자 함수에 new 키워드를 사용한다면? -> Object화 된다! 10 | - Number와 parseInt의 차이점 11 | - Number('10000원') -> NaN 12 | - parseInt('10000원') -> 10000 (숫자인 접두사만 치환) 13 | 14 | ### 연산자 더 알아보기 15 | - 비트 연산자? 어디서 쓸까 16 | - 리액트 코드를 뜯어보니 비트연산을 이용해 우선순위를 결정 -> 리액트 렌더링을 빠르게 해준다 17 | - 여러개 업적을 변수 하나(비트)로 처리 0과 1을 이용하여 업적 미달성/달성 (저장 공간을 줄일 수 있는 장점을 가짐) 18 | 19 | ### null과 undefined 20 | - undefined 보다 null이 좀 더 빠르다 21 | - undefined는 메모리를 3바이트 더 먹는다 22 | -------------------------------------------------------------------------------- /1회차/김민지.md: -------------------------------------------------------------------------------- 1 | # 변수 및 정적/동적 타입 언어 2 | 3 | ## 인상 깊은 내용: 변수 4 | 5 | ### 변수 6 | 7 | > **변수는 하나의 값을 저장하기 위해 확보한 메모리 공간 자체 또는 그 메모리 공간을 식별하기 위해 붙인 이름을 말한다.** 8 | 9 | ### 값의 할당 10 | 11 | ![variable-allocation](https://user-images.githubusercontent.com/48766355/193458622-f85ec67d-e813-4994-9b72-cef1f61109ea.png) 12 | 13 | 이전 값 `undefined`가 저장되어 있던 메모리 공간을 지우고 그 메모리 공간에 할당 값 80을 새롭게 저장하는 것이 아니라 새로운 메모리 공간을 확보하고 그곳에 할당 값 80을 저장한다. 14 | 15 | ### 값의 재할당 16 | 17 | ![variable-reallocation](https://user-images.githubusercontent.com/48766355/193457061-a694fbb6-defa-42aa-920f-33fd8d18b177.png) 18 | 19 | 현재 `score` 변수의 값은 90이다. `score` 변수의 이전 값인 `undefined`와 80은 어떤 변수도 값으로 갖고 있지 않다. 다시 말해, 어떤 식별자와도 연결되어 있지 않다. 이것은 `undefined`와 80이 더 이상 필요하지 않다는 것을 의미한다. 아무도 사용하고 있지 않으니 필요하지 않은 것이다. 이러한 불필요한 값들은 가비지 콜렉터에 의해 메모리에서 자동 해제된다. 단, 메모리에서 언제 해제될지는 예측할 수 없다. 20 | 21 |
22 | 23 | > **가비지 콜렉터** 24 | 25 | 가비지 콜렉터는 애플리케이션이 할당한 메모리 공간을 주기적으로 검사하여 더 이상 사용되지 않는 메모리를 해제하는 기능을 말한다. 더 이상 사용되지 않는 메모리란 간단히 말하자면 어떤 식별자도 참조하지 않는 메모리 공간을 의미한다. 자바스크립트는 가비지 콜렉터를 내장하고 있는 매니지드 언어로서 가비지 콜렉터를 통해 메모리 누수를 방지한다. 26 | 27 |
28 | 29 | > **언매니지드 언어와 매니지드 언어** 30 | 31 | 프로그래밍 언어는 메모리 관리 방식에 따라 언매니지드 언어와 매니지드 언어로 분류할 수 있다. 32 | 33 | C언어 같은 언매니지드 언어는 개발자가 명시적으로 메모리를 할당하고 해제하기 위해 `malloc()`과 `free()` 같은 저수준 메모리 제어 기능을 제공한다. 언매니지드 언어는 메모리 제어를 개발자가 주도할 수 있으므로 개발자의 역량에 따라 최적의 성능을 확보할 수 있지만 그 반대의 경우 치명적 오류를 생산할 가능성도 있다. 34 | 35 | 자바스크립트 같은 매니지드 언어는 메모리의 할당 및 해제를 위한 메모리 관리 기능을 언어 차원에서 담당하고 개발자의 직접적인 메모리 제어를 허용하지 않는다. 즉, 개발자가 명시적으로 메모리를 할당하고 해제할 수 없다. 더 이상 사용하지 않는 메모리의 해제는 가비지 콜렉터가 수행하며, 이 또한 개발자가 관여할 수 없다. 매니지드 언어는 개발자의 역량에 의존하는 부분이 상대적으로 작아져 어느 정도 일정한 생산성을 확보할 수 있다는 장점이 있지만 성능 면에서 어느 정도의 손실은 감수할 수밖에 없다. 36 | 37 |
38 | 39 | ## 발표 주제: 정적/동적 타입 언어 40 | 41 | 1. 정적 타입 언어 42 | 2. 동적 타입 언어 43 | 3. 동적 타입 언어의 단점 44 | 4. 타입스크립트를 사용하는 이유 45 | 46 |
47 | 48 | ### 1. 정적 타입 언어 49 | 50 | **정적 타입 언어**는 변수를 선언할 때 변수에 할당할 수 있는 값의 종류, 즉 데이터 타입을 사전에 선언해야 한다. 이를 명시적 타입 선언이라 한다. 51 | 52 | 정적 타입 언어는 변수의 타입을 변경할 수 없으며, 변수에 선언한 타입에 맞는 값만 할당할 수 있다. 정적 타입 언어는 컴파일 시점에 **타입 체크**를 수행한다. 만약 타입 체크를 통과하지 못했다면 에러를 발생시키고 프로그램의 실행 자체를 막는다. 이를 통해 타입의 일관성을 강제함으로써 더욱 안정적인 코드의 구현을 통해 런타임에 발생하는 에러를 줄인다. 53 | 54 |
55 | 56 | ### 2. 동적 타입 언어 57 | 58 | 자바스크립트는 변수를 선언할 때 타입을 선언하지 않는다. 자바스크립트의 변수는 정적 타입 언어와 같이 미리 선언한 데이터 타입의 값만 할당할 수 있는 것이 아니다. 어떠한 데이터 타입의 값이라도 자유롭게 할당할 수 있다. 59 | 60 | 자바스크립트의 변수에는 어떤 데이터 타입의 값이라도 자유롭게 할당할 수 있으므로 정적 타입 언어에서 말하는 데이터 타입과 개념이 다르다. 정적 타입 언어는 변수 선언 시점에 변수의 타입이 결정되고 변수의 타입을 변경할 수 없다. 자바스크립트에서는 값을 할당하는 시점에 변수의 타입이 동적으로 결정되고 변수의 타입을 언제든지 자유롭게 변경할 수 있다. 61 | 62 | **자바스크립트의 변수는 선언이 아닌 할당에 의해 타입이 결정(타입 추론)된다. 그리고 재할당에 의해 변수의 타입은 언제든지 동적으로 변할 수 있다.** 이러한 특징을 **동적 타이핑**이라 하며, 자바스크립트를 정적 타입 언어와 구별하기 위해 **동적 타입 언어**라 한다. 63 | 64 |
65 | 66 | ### 3. 동적 타입 언어의 단점 67 | 68 | 자바스크립트는 개발자의 의도와 상관없이 자바스크립트 엔진에 의해 암묵적으로 타입이 자동으로 변환되기도 한다. 동적 타입 언어는 유연성은 높지만 신뢰성은 떨어진다. 69 | 70 |
71 | 72 | ### 4. 타입스크립트를 사용하는 이유 73 | 74 | > **What is TypeScript?** 75 | 76 | [TypeScript: JavaScript With Syntax For Types](https://www.typescriptlang.org/) 77 | 78 | ![typescript](https://user-images.githubusercontent.com/48766355/193456020-59dc39f5-da56-4195-8174-9ee0e994673c.png) 79 | 80 | 1. JavaScript and More 81 | 82 | - IDE와 통합해서 쉽게 오류를 캐치할 수 있다. 83 | 84 | 2. A Result You Can Trust 85 | 86 | - JavaScript가 지원되는 모든 곳에서 실행할 수 있다. 87 | 88 | 3. Safety at Scale 89 | 90 | - 추가적인 코드 없이도 훌륭한 도구를 제공한다. 91 |
92 | 93 | ![typescript](https://user-images.githubusercontent.com/48766355/193455893-f9e10156-725b-401a-8a62-eee1aaf3a26c.png) 94 | -------------------------------------------------------------------------------- /1회차/박상범.md: -------------------------------------------------------------------------------- 1 | # 2회(1,2,3,4,5,6,7,8,9장) 2 | 3 | # 2장 4 | 5 | ## 2.5 자바스크립트 특징 6 | 7 | 자바스크립트는 개발자가 별도의 컴파일 작업을 수행하지 않는 인터프리터 언어이다. ( 개발자도구를 통해 콘솔을 찍어보면 한줄씩 변환하는 작업을 하는게 그 예시이다. ) 대부분의 모던 자바스크립트 엔진 ( 크롬의 v8, 파이어폭스의 spiderMonkey, 사파리의 javascriptCore, 마이크로소프트 엣지의 Chakra) 은 인터프리터와 컴파일러의 장점을 결합해 비교적 처리 속도가 느린 인터프리터의 단점을 해결 했다. ( JIT 컴파일러, 지속적으로 인터프리터가 하다가 자주 사용하는 코드의 정보를 캐시에 담아두었다가 미리 꺼내서 바로 실행함 ) 8 | 9 | [https://www.oowgnoj.dev/review/advanced-js-1](https://www.oowgnoj.dev/review/advanced-js-1) 10 | 11 | # 4장 변수 12 | 13 | 메모리에 저장되어있는 값을 참조하려면 메모리의 주소를 알아야하는데 자바스크립트에서는 메모리주소에 직접적으로 접근하는 것을 허용하지 않기 때문에 변수를 사용하여 메모리 공간에 접근한다. 14 | 15 | **변수**란 하나의 값을 저장하기 위해 확보한 메모리 공간 자체 또는 메모리 공간을 식별하기 위해 붙인 이름이다. 16 | 17 | `10 + 20` 이라는 연산을 하면 아래와 같이 18 | (1) `10, 20 값`이 `메모리`에 저장되고 19 | (2) `CPU`가 이 값을 읽어들여 `+ 연산`을 수행하고, 20 | (3) `30`이라는 결과값이 `메모리`에 저장된다. 21 | 만약 30이라는 값을 재사용하고 싶다면 `메모리 주소`를 알아야, 메모리 공간에 접근할 수 있다. 22 | 23 | - 컴퓨터에서 **연산**은 **CPU**가 담당하고 **기억**은 **메모리**가 한다. 24 | 25 | ## 궁금증 💡 26 | 27 | 자바스크립트에서는 개발자가 메모리 주소를 직접 제어하는 것을 허용하지 않는데 왤까? 28 | 29 | - 메모리 주소값 (예를 들어 0x0669F913)을 통해 값에 접근할 경우, 오타가 나는 등 실수를 할 확률이 높다. 실수로 운영체제가 사용하고 있는 값을 변경하면 시스템이 멈추게 되는 치명적인 오류가 발생할 수도 있다. 30 | - 직접적인 메모리 제어가 가능해도, 값이 저장될 메모리 주소는 코드가 실행될 때 메모리의 상황에 따라 임의로 결정되기 때문에 동일한 컴퓨터에서 동일한 코드를 실행해도 코드가 실행될 때마다 값이 저장되는 메모리 주소가 변경된다. 따라서 코드가 실행되기 이전에는 값이 저장되는 메모리 주소를 알 수 없다. 31 | 32 | 그럼 자바스크립트는 변수들의 메모리 주소를 확인해볼 수 없나? 33 | 34 | | 참고영상 : [https://www.youtube.com/watch?v=Rp_-WJlXqHU](https://www.youtube.com/watch?v=Rp_-WJlXqHU) 35 | 36 | 확인 해 볼수는 있는데 그 주소값이 실제 물리적인 메모리 주소와 같은지는 확신할 수 없다. 그만 알아보자. 37 | 38 | ## 4.2 식별자 39 | 40 | 식별자는 값이 저장되어 있는 메모리 주소와 매핑관계를 맺으며, 이 매핑 정보도 메모리에 저장되어야 한다. 41 | 42 | 식별자는 값이 아니라 메모리 주소를 기억하고 있다. 43 | 44 | ## 4.3 변수 선언 45 | 46 | 값을 저장하기 위한 메모리 공간을 확보하고 변수 이름과 확보된 메모리 공간의 주소를 연결해서 값을 저장할 수 있도록 준비하는 것이다. 확보된 메모리 공간은 자바스크립트 엔진에 의해 undefined 라는 값이 암묵적으로 할당되어 초기화 된다. (var만) 47 | 48 | 자바스크립트 엔진은 변수 선언을 2단계로 나누어 실행한다. 49 | 50 | **선언단계**: 변수 이름을 등록, 변수의 존재를 알린다. 51 | 52 | **초기화 단계**: 값을 저장하기 위한 메모리 공간을 확보하고 암묵적으로 undefined 할당하여 초기화 53 | 54 | var는 선언, 초기화 단계가 동시에 진행된다. 55 | 56 | ## 4.4 변수 호이스팅 57 | 58 | 변수 선언은 런타임이 아니라 그 이전단계에서 먼저 실행된다. 59 | 60 | 변수 선언문이 코드의 선두로 끌어 올려진 것처럼 동작하는 자바스크립트 고유의 특징을 변수 호이스팅 이라고 한다. 61 | 62 | var 뿐만 아니라 let, const 도 호이스팅이 된다. 스코프에 변수를 등록 하고 초기화는 변수 선언문에 도달했을 때 이루어 진다. 63 | 64 | ```jsx 65 | // 내가 짠 코드 66 | console.log(a); 67 | var a; 68 | 69 | // 코드 실행 70 | var a = undefined; 71 | console.log(a); // undefined 72 | 73 | // 내가 짠 코드 74 | console.log(b); 75 | let b; 76 | 77 | // 코드 실행 78 | let b; // let은 var와 다르게 선언, 초기화가 따로 실행 79 | ... // TDZ 80 | console.log(b); // ReferenceError: b is not defined 81 | ``` 82 | 83 | ## 4.5 값의 할당 84 | 85 | 변수 선언은 런타임 이전에 실행, 값의 할당은 런타임에 실행된다. 86 | 87 | ## 4.6 값의 재할당 88 | 89 | let은 재선언 불가, 재할당 가능 90 | 91 | const는 재선언, 재할당 불가 92 | 93 | const 변수의 타입이 객체인 경우는 객체애 대한 참조를 변경하지 못한다는것을 의미한다. 다시 말해 재할당은 불가능하지만 프로퍼티의 추가, 삭제, 값변경은 할 수 있다. 94 | 95 | # 5장 표현식과 문 96 | 97 | **개념을 이해한다는 것은 바로 용어를 정확히 이해하고 설명할 수 있다는 것이다.** 98 | 99 | 값은 식이 평가되어 생성된 결과를 뜻한다. 100 | 101 | ## 5.2 리터럴 102 | 103 | **사람이 이해할 수 있는 문자 또는 약속된 기호를 사용해서 값을 생성하는 표기법** 104 | 105 | 다양한 종료의 리터럴.. 기억하고 의사소통할 때 정확히 사용하자. 106 | 107 | ## 5.3 표현식 108 | 109 | 표현식은 값으로 평가 될 수 있는 문 이다. 110 | 111 | ## 5.5 세미콜론 자동 삽입 기능 112 | 113 | 세미콜론은 생략 가능, 자바스크립트 엔진이 소스코드를 해석할 때 문의 끝이라고 예측되는 지점에 세미콜론을 자동으로 붙여주는 기능이 암묵적으로 수행된다. 근데 가끔 불편할때 있다. 114 | 115 | ```jsx 116 | function foo () { 117 | return 118 | {} 119 | // ASI의 동작 결과 => return; {}; 120 | // 개발자의 예측 => return {}; 121 | } 122 | ``` 123 | 124 | # 6장 데이터 타입 125 | 126 | 데이터 타입은 크게 **원시타입과** **객체타입으로** 분류 127 | 128 | ## 6.5 undefined 타입 129 | 130 | undefined는 변수 선언에 의해 확보된 메모리 공간을 처음 할당이 이루어질 때까지 빈 상태로 내버려 두지않고 자바스크립트 엔진이 undefined로 초기화 한다. 131 | 132 | 이처럼 undefined 는 개발자가 의도적으로 할당하기 위한 값이 아니라 자바스크립트 엔진이 변수를 초기화 할때 사용하는 값이다. 즉, 초기화 되지 않은 변수라는 것을 쉽게 간파할 수 있다. 133 | 134 | 개발자가 변수에 값이 없다는 것을 명시하고 싶을 때는 undefined를 할당하는 것이 아닌 null을 명시해야 undefined의 본래 취지를 더럽히지 않고 혼란을 야기하지도 않는다. 135 | 136 | ## 궁금증 137 | 138 | gc가 처리해줄 수 있도록 객체를 unreachable한 상태로 만들어줘야되는데 그 방법으로 해당 객체에 대한 참조를 없애면 된다. 그렇다면 객체 참조 해제는 null 만 가능한가? undefined는 안되나? 139 | 140 | - 둘 다 가능 141 | - chrome, node의 v8 엔진에서는 null이 아주 약간 더 빠르다는 의견 142 | - 근데 undefined가 null보다 조금 더 길이 길기 때문에 JIT에서 4byte 더 메모리 차지함 143 | 144 | 하지만! null을 사용하자. 아래와 같은 이유때문에 145 | 146 | - null과 undefined의 정의 147 | - 다른 언어와의 호환성 148 | - 개발 관습 149 | 150 | ## 6.7 심볼 타입 151 | 152 | 심볼은 Symbol 함수를 호출해 생성한다. 이때 생성된 심벌값은 외부에 노출되지 않으며, 다른 값과 절대 중복되지 않는 유일무이한 값이다. 153 | 154 | ## 6.10 동적 타이핑 155 | 156 | 정적 타입 언어는 **컴파일 시점에 타입 체크**를 한다. 만약 타입 체크를 통과하지 않았다면 에러를 발생시키고 프로그램의 실행 자체를 막는다. 대표적인 정적 타입 언어로 C, C++, 자바, 코틀린, 고 ,하스켈 ,러스트, 스칼라 등이 있다. 157 | 158 | 그에 반해 자바스크립트 변수는 선언이 아닌 할당에 의해 타입이 결정(추론) 된다. 그리고 재할당에 의해 변수 타입은 언제든지 동적으로 변할 수 있다. 자바스크립트는 동적 타입 언어이고 대표적인 언어로는 파이썬, PHP, 루비, 리스트, 펄 등이 있다. 동적 타입 언어는 유연성은 높지만 신뢰성이 떨어진다. 159 | 160 | ## 궁금증 161 | 162 | 타입스크립트는 변수의 타입이 컴파일 타임에 결정되는 정적 타입 언어이다. vscode에서는 코드를 작성하면 타입스크립트 에러(TSxxxx)를 자동으로 뱉어주는데 이걸 누가 해주는걸까? eslint의 typescript 관련 플러그인이 해주는걸까? .vscode 폴더안의 source.fixAll.eslint 가 파일을 저장할 때 eslint 동작을 하게 했고 설정의 format on save에 delay 1초를 걸어놓고 해당 코드의 타입체크를 해준 것 같다. (추측) 163 | 164 | 코드는 오해하지 않도록 작성해야 된다. 오해는 커뮤니케이션을 어렵게 하는 대표적인 원인으로 생산성을 떨어뜨리는 것은 물론 팀의 사기까지 저하시킨다. 코드는 동작하는 것만이 존재 목적은 아니다. 코드는 개발자를 위한 문서이기도 하다. 따라서 사람이 이해할 수 있는 코드, 즉 **가독성이 좋은 코드가 좋은 코드**다. 165 | 166 | # 7장 연산자 167 | 168 | ## 7.2 할당 연산자 169 | 170 | ```jsx 171 | var a, b, c; 172 | 173 | // 연쇄 할당. 오른쪽에서 왼쪽으로 진행. 174 | // ① c = 0 : 0으로 평가된다 175 | // ② b = 0 : 0으로 평가된다 176 | // ③ a = 0 : 0으로 평가된다 177 | a = b = c = 0; 178 | 179 | console.log(a, b, c); // 0 0 0 180 | ``` 181 | 182 | ## 7.3 비교 연산자 183 | 184 | 동등 비교 연산자(==)는 암묵적 타입 변환을 통해 타입을 일치시킨 후 같은 값인지 비교한다. 185 | 186 | 일치 비교 연산자(===)는 좌항과 우항의 피연산자가 타입도 같고 값도 같은 경우에 한하여 true를 반환한다. 187 | 188 | 여기서 문제는 객체는 참조형이기 때문에 key, value 값 및 프로퍼티의 순서가 같은 object를 일치 비교 연산자로 비교할 수가 없다. 189 | 190 | 방법은 여러가지가 있다. 그중 두가지 방법을 추천한다. 191 | 192 | 1. 간단하고 Object 깊이가 깊지 않은 경우는 JSON.stringify()함수를 톹해 비교 193 | 2. 깊고 복잡한 비교를 했을경우는 Lodash의 isEqual() 적극 활용 194 | 195 | loadsh의 isEqual 함수는 Object 전체 비교하지 않고 비교 중 다른 부분이 있으면 함수를 종료한다고 한다. 196 | 197 | [https://www.delftstack.com/ko/howto/javascript/compare-objects-javascript/](https://www.delftstack.com/ko/howto/javascript/compare-objects-javascript/) 198 | 199 | [https://slee2540.tistory.com/49](https://slee2540.tistory.com/49) 200 | 201 | **또한 NaN은 자신과 일치하지 않는 유일한 값이기 떄문에 일치 비교 연산자를 통해 비교할 수 없다! 따라서 숫자가 NaN인지 조사하려면 Number.isNaN을 활용하자.** 202 | 203 | ## 7.4 삼항 조건 연산자 204 | 205 | 조건에 따라 어떤 값을 결정해야 한다면 if else 문 보다 삼항 조건 연산자 표현식을 사용하는 편이 유리하다. 206 | 하지만 조건에 따라 수행해야 하는 문이 여러개라면 if else 문의 가독성이 더 좋다. 207 | 208 | ## 7.5 논리 연산자 209 | 210 | 논리 연산자로 구성도니 복잡한 표현식은 가독성이 좋지않아 한눈에 이해하기 어려울때가 있는데 이럴때 드모르간의 법칙을 이용하자 211 | 212 | ## 7.6 쉼표 연산자 213 | 214 | 연쇄 할당과 반대로 왼쪽 피연산자부터 차례로 피연산자를 평가한다. 215 | 216 | ``` 217 | var x, y, z; 218 | 219 | x = 1, y = 2, z = 3; // 3 220 | ``` 221 | 222 | ## 7.8 typeof 연산자 223 | 224 | ``` 225 | typeof '' // -> "string" 226 | typeof 1 // -> "number" 227 | typeof NaN // -> "number" 228 | typeof true // -> "boolean" 229 | typeof undefined // -> "undefined" 230 | typeof Symbol() // -> "symbol" 231 | typeof null // -> "object" //!! CAUTION 232 | typeof [] // -> "object" 233 | typeof {} // -> "object" 234 | typeof new Date() // -> "object" 235 | typeof /test/gi // -> "object" 236 | typeof function () {} // -> "function" 237 | ``` 238 | 239 | typeof 연산자로 null을 연산해보면 object 가 반환되는데 이건 자바스크립트의 첫번째 버전의 버그이다. 240 | 이걸 고치게 되면 기존 코드에 영향을 줄 수 있기 때문에 아직까지도 수정되지 못하고 있다고 한다 ㅋㅋ.. 241 | 242 | # 8장 제어문 243 | 244 | ## 8.4 break문 245 | 246 | 레이블 문은 중첩된 for문 외부로 탈출할 때 유용하다. 그 밖의 경우에는 일반적으로 사용하면 프로그램의 흐름이 복잡해져서 가독성이 나빠지고 오류를 발생시킬 가능성이 높아져서 권장하진 않는다! (코테에서 쓰자) 247 | 248 | ```tsx 249 | // outer라는 식별자가 붙은 레이블 for 문 250 | outer: for (var i = 0; i < 3; i++) { 251 | for (var j = 0; j < 3; j++) { 252 | // i + j === 3이면 outer라는 식별자가 붙은 레이블 for 문을 탈출한다. 253 | if (i + j === 3) break outer; 254 | console.log(`inner [${i}, ${j}]`); 255 | } 256 | } 257 | 258 | console.log('Done!'); 259 | ``` 260 | 261 | # 9장 타입 변환과 단축 평가 262 | 263 | ## 9.2 암묵적 타입 변환 264 | 265 | +는 숫**자를 문자열**로 암묵적 타입 변환 후 연산 하고 그외의 연산자(-,*,/,%)는 **문자열을 숫자**로 타입 변환 후 연산한다 266 | 267 | ## 9.4 단축 평가 268 | 269 | ### 9.4.1 논리 연산자를 사용한 단축 평가 270 | 271 | ```tsx 272 | 'Cat' && 'Dog' 273 | ``` 274 | 275 | 논리곱 연산자는 두개의 피연산자가 모두 true로 평가될 때 true를 반환한다. 276 | 277 | 논리곱 연산자와 논리합 연산자는 이처럼 논리 연산의 결과를 결정하는 피연산자를 타입 변환하지 않고 그대로 반환한다. 이를 **단축평가** 라고 한다. 단축 평가는 표현식을 평가하는 도중에 평가 결과가 확정되는 경우 나머지 평가 과정을 생략하는 것을 말한다. 278 | 279 | **객체를 가리키기를 기대하는 변수가 null 또는 undefiend가 아닌지 확인하고 프로퍼티를 참조할 때** 유용하다. 280 | 281 | ```tsx 282 | var elem = null; 283 | // elem이 null이나 undefined와 같은 Falsy 값이면 elem으로 평가되고 284 | // elem이 Truthy 값이면 elem.value로 평가된다. 285 | var value = elem && elem.value; // -> null 286 | ``` 287 | 288 | ### 9.4.2 옵셔널 체이닝 연산자 289 | 290 | 옵셔널 체이닝 연산자 ?.는 객체를 가리키기를 기대하는 변수가 null 또는 undefined가 아닌지를 확인하고 프로퍼티를 참조할 때 유용하다. 291 | 292 | 하지만 옵셔널 체이닝 연산자 ?.는 좌항 피연산자가 falsy로 평가되는 값이라도 null 또는 undefined가 아니면 우항의 프로퍼티를 이어간다. 293 | 294 | ```tsx 295 | var str = ''; 296 | 297 | // 문자열의 길이(length)를 참조한다. 이때 좌항 피연산자가 false로 평가되는 Falsy 값이라도 298 | // null 또는 undefined가 아니면 우항의 프로퍼티 참조를 이어간다. 299 | var length = str?.length; 300 | console.log(length); // 0 301 | ``` 302 | 303 | ### 9.4.3 null 병합 연산자 304 | 305 | 좌항의 피연산자가 null이나 undefined 면 우항의 피연산자를 반환한다. 306 | -------------------------------------------------------------------------------- /1회차/이병현.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # 내용 정리 4 | 5 | ## 변수의 선언, 할당 그리고 호이스팅 6 | 7 | 변수는 선언과 할당으로 나누어지며 선언시에는 메모리에 변수이름을 등록하고 `undefined`로 메모리 공간을 확보함, 할당시에는 메모리에 값을 저장한다. 8 | 9 | 이와 같이 선언과 할당이 나누어지며 코드의 위치에 관계없이 선언을 엔진이 우석 해석하기 때문에 선언 되어지는 코드보다 위에 있는 코드가 문제없이 자바스크립트 해석 엔진이 에러로 다루지 않고 `undefined` 라는 선언이 이루어진 코드로 해석하게 된다. 10 | 11 | 12 | 13 | ```js 14 | console.log(score); // undefined 15 | 16 | var score; // ① 변수 선언 17 | score = 80; // ② 값의 할당 18 | 19 | console.log(score); // 80 20 | ``` 21 | 22 | 23 | ## 표현식과 문 24 | 25 | 문에는 표현식인 문과 표현식이 아닌 문이 있고, 표현식은 값으로 평가될 수 있는 문을 표현식이라 한다. 26 | 27 | 따라서 아래와 같이 표현식인 문을 값처럼 사용할 수도 있다. 28 | 29 | ```js 30 | // 표현식인 문은 값처럼 사용할 수 있다 31 | var foo = x = 100; 32 | console.log(foo); // 100 33 | ``` 34 | 35 | ## 동적 타이핑 36 | 37 | 자바스크립트는 값이 할당되어지는 때에 타입이 결정되는 타입 추론의 형태를 가지고 있으며, 재할당에 의해 변수의 타입에 동적으로 변할 수 있다. 38 | 39 | ```js 40 | var foo; 41 | console.log(typeof foo); // undefined 42 | 43 | foo = 3; 44 | console.log(typeof foo); // number 45 | 46 | foo = 'Hello'; 47 | console.log(typeof foo); // string 48 | 49 | foo = true; 50 | console.log(typeof foo); // boolean 51 | 52 | foo = null; 53 | console.log(typeof foo); // object 54 | 55 | foo = Symbol(); // 심벌 56 | console.log(typeof foo); // symbol 57 | 58 | foo = {}; // 객체 59 | console.log(typeof foo); // object 60 | 61 | foo = []; // 배열 62 | console.log(typeof foo); // object 63 | 64 | foo = function () {}; // 함수 65 | console.log(typeof foo); // function 66 | ``` 67 | 68 | ## typeof와 Object.is 그리고 instanceof 69 | 70 | `typeof`의 경우 `null` 검사 시 `object`로 나오는 버그가 있으므로 71 | 72 | `x === null` 혹은 `Object.is(null, null) 같은 형태로 검사해야 한다. 73 | 74 | 75 | ```js 76 | typeof '' // -> "string" 77 | typeof 1 // -> "number" 78 | typeof NaN // -> "number" 79 | typeof true // -> "boolean" 80 | typeof undefined // -> "undefined" 81 | typeof Symbol() // -> "symbol" 82 | typeof null // -> "object" 83 | typeof [] // -> "object" 84 | typeof {} // -> "object" 85 | typeof new Date() // -> "object" 86 | typeof /test/gi // -> "object" 87 | typeof function () {} // -> "function" 88 | ``` 89 | 90 | 91 | `null` 이외에도 `[], new Date(), /test/gi`가 `object`로 판단되어지기 때문에 `[]` 같이 배열을 배열인지 확인하고 싶다면 `Array.isArray([])`로 검사해야 한다. 92 | 93 | 혹은 클래스의 타입을 검사하기 위해 `instanceof`를 사용 할 수 있다. 94 | 95 | 96 | ```js 97 | Array.isArray([]) 98 | -> true 99 | 100 | new Date() instanceof Date 101 | -> true 102 | 103 | /test/gi instanceof RegExp 104 | -> true 105 | 106 | [] instanceof Array 107 | -> true 108 | ``` 109 | 110 | 하지만, 아래 코드와 같이 `Date` 생성에 실패하더라도 `true`로 판단되어지게 된다. 111 | 112 | ```js 113 | const b = new Date('ab'); 114 | b instanceof Date 115 | -> true 116 | ``` 117 | 118 | 119 | # 공유하고 싶은 것 120 | 121 | ## 비트 연산자 122 | 123 | React에서는 우선순위를 빠른 속도로 계산하기 위해 비트 연산자를 사용한다. 124 | 125 | 코드를 보면 대체 이게뭔,..? 하는 생각이 든다. 126 | 127 | https://github.com/facebook/react/blob/main/packages/react-reconciler/src/ReactFiberLane.new.js 128 | 129 | 여기에 대해 보충 설명이 있으니 궁금하면 읽어 볼 것 130 | 131 | https://medium.com/codex/bitwise-operation-tricks-in-react-source-code-3971f704750e 132 | 133 | ## GC (garbage collector) 134 | 135 | 기본적으로 참조가 되지 않는 변수가 메모리에서 해제된다고 생각하기 쉽지만, 실제로는 순환참조 같은 문제가 발생하기에 더 이상 도달할 수 없는 객체를 해제하는 방식으로 처리한다. 136 | 137 | https://developer.mozilla.org/ko/docs/Web/JavaScript/Memory_Management 138 | 139 | 140 | ## prototype과 __proto__ 의 차이 141 | 142 | `prototype`은 실제로 함수에 정의된 프로퍼티이고 `__proto__` 의 경우 `[[Prototype]]`을 볼 수 있게하는 기능이다. 던더 프로토라고 부른다. 143 | 144 | https://blog.jeongwoo.in/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%ED%94%84%EB%A1%9C%ED%86%A0%ED%83%80%EC%9E%85%EC%97%90-%EB%8C%80%ED%95%9C-%EC%9D%B4%ED%95%B4-savemyammar-50aa64b94331 145 | -------------------------------------------------------------------------------- /1회차/천승아.md: -------------------------------------------------------------------------------- 1 | # ✍️ 공부 내용 정리 2 | 3 | ## 4장 변수 4 | 5 | ### 4-1. 변수란? 6 | 7 | - 변수: 하나의 값을 저장하기 위해 확보한 메모리 공간 자체. 또는 그 메모리 공간을 식별하기 위해 붙인 이름값의 위치를 가리키는 상징적인 이름 8 | - 컴파일러를 통해 값이 저장된 메모리 공간의 주소로 치환되어, 직접 메모리 주소를 조작할 일 없이 변수에 안전하게 접근 9 | 10 | ### 4-2. 식별자 11 | 12 | - 식별자: 어떤 값을 구별해서 식별할 수 있는 고유의 이름 13 | - 식별자는 선언으로 JS 엔진에 식별의 존재를 알린다. 14 | 15 | ### 4-3. 변수 선언 (변수 생성) 16 | 17 | - `var` 키워드: 블록 레벨 스코프만 지원하고 함수 레벨 스코프는 지원하지 않는 등 단점 (의도치 않은 전역 변수 선언) 18 | - 선언 시 확보된 메모리 공간은 `undefined`로 암묵적 초기화 → JS의 독특한 특징. 19 | - 선언 단계 20 | 1. 선언 단계 : 엔진에 변수 존재 알림 21 | 2. 초기화 단계: 메모리 공간 확보 및 `undefined`로 초기화 (쓰레기값 방지) 22 | 23 | ### 4-4. 변수 선언 실행 시점과 변수 호이스팅 24 | 25 | ```js 26 | console.log(score); // undefined. 참조 에러 X 27 | var score; // 변수 선언문 28 | ``` 29 | 30 | - JS 코드 실행 순서: 소스코드 평가 과정(엔진은 선언문을 먼저 실행하고 나머지 내용 실행 준비) → 선언문 제외 후 순차 실행 31 | - 변수 선언은 런타임 이전 단계에서 실행되므로 참조 에러 X 32 | - 호이스팅: 변수 선언문이 선두로 끌어 올려진 것처럼 동작하는 JS 고유의 특징 (모든 식별자에 대해 동일) 33 | 34 | ### 4-5. 값의 할당 35 | 36 | - 변수 선언 시점: 런타임 이전 (`undefined` 초기화 상태) 37 | - 변수 할당 시점: 런타임 38 | - 값 할당 시 기존 메모리 공간을 제거하는 게 아닌 새로운 메모리 공간 확보 후 할당 --> 재할당 개념 39 | 40 | ### 4-6. 값의 재할당 41 | 42 | - 재할당: 새로운 메모리 공간 확보 및 저장 후 그 메모리 주소를 매핑 43 | - JS는 매니지드 언어: 가비지 콜렉터를 내장하여 식별자와 연결되지 않는 이전 값들은 가비지 콜렉터에서 메모리 자동 해제 44 | 45 | --- 46 | 47 | ## 5장 표현식과 문 48 | 49 | ### 5-1. 값 50 | 51 | - 값: 표현식이 평가되어 생성된 결과. 리터럴 사용은 가장 기본적인 값 생성 52 | - 같은 값이라도 다른 데이터 타입이면 다르게 표현 53 | 54 | ### 5-2. 리터럴 55 | 56 | - 사람이 이해할 수 있는 문자 또는 약속된 기호를 사용해 값을 생성하는 표기법 == 미리 약속된 기호 == 그 자체로 표현식 57 | - JS 엔진은 런타임에 리터럴을 평가해 값을 생성 58 | 59 | ### 5-3. 표현식 60 | 61 | - 표현식: `값으로 평가`될 수 있는 문. 표현식이 평가되면 새로운 값을 생성하거나 기존 값을 참조함 62 | - 표현식 == 표현식이 평가된 값 63 | - 표현식은 값처럼 사용 가능 64 | - 식별자 표현식: 식별자 참조하면 변수 값으로 평가됨 65 | 66 | ```js 67 | // 식별자 표현식(선언이 이미 존재한다고 가정) 68 | sum; 69 | person.name; 70 | arr[1]; 71 | 72 | // 함수/메서드 호출 표현식(선언이 이미 존재한다고 가정) 73 | square(); 74 | person.getName(); 75 | ``` 76 | 77 | ### 5-4. 문 (명령문) 78 | 79 | - 문: 프로그램 구성하는 기본 단위이자 최소 실행 단위 80 | - 문의 집합 → 프로그램 81 | - 문을 작성하고 순서에 맞게 나열하는 것 → 프로그래밍 82 | - 문은 여러 토큰으로 구성 83 | - 토큰: 문법적인 의미를 가지며 문법적으로 더이상 나눌 수 없는 코드의 기본 요소 84 | - ex) 키워드, 식별자, 연산자, 리터럴, `;` 등 85 | 86 | ### 5-5. 세미콜론(문의 종료)과 세미콜론 자동 삽입 기능 87 | 88 | - if문, for문, 함수, 코드 블록 뒤에는 세미콜론 X → 문의 종료를 의미하는 자체 종결성을 갖기 때문 89 | - 세미콜론 옵션은 생략 가능함 → JS 엔진에 문 끝 예측해서 삽입하는 세미콜론 자동 삽입 기능 존재 90 | 91 | ### 5-6. 표현식인 문과 표현식이 아닌 문 92 | 93 | - 표현식인 문: 값으로 평가 됨 94 | - 표현식이 아닌 문: 값으로 평가되지 않음 95 | - 방법: 변수에 할당해보는 것. 96 | 97 | ```jsx 98 | // 표현식이 아닌 문은 값처럼 사용할 수 없다. 99 | var foo = var x; // SyntaxError: Unexpected token var 100 | ``` 101 | 102 | ```jsx 103 | // 변수 선언문은 표현식이 아닌 문이다. 104 | var x; 105 | 106 | // 할당문은 그 자체가 표현식이지만 완전한 문이기도 하다. 즉, 할당문은 표현식인 문이다. 107 | x = 100; 108 | ``` 109 | 110 | - 크롬 개발자 도구는 표현식 아닌 문 실행하면 언제나 undefined 반환하고 표현식은 값을 반환한다. (완료값) 111 | 112 | --- 113 | 114 | ## 6장 데이터 타입 == 타입 == 값의 종류 115 | 116 | - ES6 기준 7개의 데이터 타입 117 | - 원시 타입 118 | - `undefined` 타입: `var` 키워드 변수에 암묵적으로 할당되는 값 119 | - `null` 타입: 값이 없다는 것을 의도적으로 명시 120 | - symbol 타입: ES6에서 추가된 타입 121 | - 객체 타입: JS는 객체 기반 언어 —> JS를 이루고 있는 거의 모든 것은 객체 122 | 123 | ### 6-1. 숫자 타입 124 | 125 | - JS는 하나의 숫자 타입만 사용 -> 모두 실수로 처리함 126 | - 10진수 외 데이터 타입 제공하지 않기 때문에 모두 10진수로 해석 (표기법만 다를 뿐 모두 같은 값) 127 | 128 | ```js 129 | // 숫자 타입의 세 가지 특별한 값 130 | console.log(10 / 0); // Infinity 131 | console.log(10 / -0); // -Infinity 132 | console.log(1 * "String"); // NaN 133 | ``` 134 | 135 | ### 6-2. 문자열 타입 136 | 137 | - 텍스트 데이터: 16비트 유니코드 문자(UTF-16) 집합으로 대부분의 문자를 표현 138 | - C: 문자열 타입 없고 배열로 표현 139 | - Java: 문자열 객체로 표현 140 | - Javascript: 문자열은 원시 타입 -> 생성되면 변경 불가 141 | 142 | ### 6-3. 템플릿 리터럴 143 | 144 | - ES6부터 도입 -> 편리한 문자열 처리 기능 제공 → 런타임에 일반 문자열로 변환 145 | - 멀티라인 문자열 기능: 일반 문자열은 공백 등 표현하려면 이스케이프 시퀀스 사용 but 템플릿 리터럴은 그대로 표현 가능 146 | 147 | ### 6-5. undefined 타입 -> undefined가 유일 148 | 149 | - undefined는 `var` 변수 선언처럼 개발자가 의도적으로 할당하는 게 아니라 JS가 변수를 초기화로 쓰는 값 150 | - 개발자가 빈 값 명시할 경우 `null` 권장 151 | 152 | ### 6-6. null 타입 → null이 유일 153 | 154 | - 변수 값 없다는 것 의도적으로 명시할 때 사용 -> 이전 할당된 참조 제거 및 가비지 콜렉션 수행. 155 | - 유효한 값 반환하지 않을 때도 null 활용 156 | 157 | ### 6-7. 심벌 타입 158 | 159 | - 다른 값과 중복되지 않는 값. 이름 충돌 위험이 없는 객체의 유일한 프로퍼티 키 만들기 위해 사용함. 160 | - `Symbol`로 호출함 161 | 162 | ```js 163 | // 심벌 값 생성 164 | var key = Symbol("key"); 165 | console.log(typeof key); // symbol 166 | 167 | // 객체 생성 168 | var obj = {}; 169 | 170 | // 이름이 충돌할 위험이 없는 유일무이한 값인 심벌을 프로퍼티 키로 사용한다. 171 | obj[key] = "value"; 172 | console.log(obj[key]); // value 173 | ``` 174 | 175 | ### 6-9. 데이터 타입의 필요성 176 | 177 | 1. 타입에 의한 메모리 공간 확보와 참조 178 | 179 | - 확보: 메모리 공간 크기 결정 180 | - 참조: 선두 메모리 셀의 주소 찾아가기. 한 번에 읽어들여야 할 셀의 개수 인식 181 | 182 | 2. 데이터 타입에 의한 값의 해석 183 | 184 | - 메모리에서 읽은 2진수 해석 방법 결정 185 | 186 | ### 6-10. 동적 타이핑 187 | 188 | - 정적 타입 언어 == 명시적 타입 선언 189 | - 변수 선언 시 데이터 타입을 사전 선언 → 타입 변경 불가능 및 타입 맞는 값만 할당 가능 190 | - 컴파일 시점에 타입 체크 → 에러 시 실행 X 191 | - 변수 선언 시 변수 타입 결정 192 | - 동적 타입 언어: 타입이 없고 키워드로 변수 선언. 값 할당 자유로움 193 | - 타입 추론: 값 할당 시 변수 타입 동적으로 타입 결정 194 | - 동적 타이핑: 재할당으로 타입 변경 195 | 196 | --- 197 | 198 | ## 7장 연산자 199 | 200 | - 연산자: 하나 이상의 표현식을 산술, 할당, 논리, 지수 연산 등을 수행해 하나의 값을 생성 201 | 202 | ### 7-1. 산술 연산자 203 | 204 | 1. 이항 산술 연산자 205 | 206 | - 2개의 피연산자 산술 연산. 부수 효과 X 새로운 값 생성 207 | 208 | 2. 단항 산술 연산자 209 | 210 | - 1개의 피연산자 산술 연산 211 | - 증가(++), 감소(—-) 연산자: 부수 효과 있음. 피연산자 값 변경하는 암묵적 할당이 이뤄짐. 212 | - ‘+’ 연산자: 피연산자를 숫자 타입으로 변환해 반환. 부수효과 X 213 | 214 | 3. 문자열 연결 연산자 215 | 216 | - JS 엔진이 개발자 의도와 달리 암묵적 타입 변환(타입 강제 변환) 할 수 있음 217 | 218 | ```jsx 219 | // true는 1로 타입 변환된다. 220 | 1 + true; // -> 2 221 | 222 | // false는 0으로 타입 변환된다. 223 | 1 + false; // -> 1 224 | 225 | // null은 0으로 타입 변환된다. 226 | 1 + null; // -> 1 227 | 228 | // undefined는 숫자로 타입 변환되지 않는다. 229 | +undefined; // -> NaN 230 | 1 + undefined; // -> NaN 231 | ``` 232 | 233 | ### 7-2. 할당 연산자 (값으로 평가되는 표현식인 문) 234 | 235 | - 우항에 있는 평가 결과를 좌항에 있는 변수에 할당 → 부수효과 O 236 | - 동일한 값 연쇄 할당 가능 -> `console.log(a, b, c); // 0 0 0` 237 | 238 | ### 7-3. 비교 연산자 239 | 240 | 1. 동등 연산자: 느슨한 비교 → 비교 전 암묵적 타입 변환으로 일치 시킨 후 비교 → 결과 예측 어려움 241 | 2. 일치 연산자: 엄격한 비교 → 타입&값을 비교 -> 결과 예측 쉬움 242 | 243 | - nan은 자신과 일치하지 않는 유일한 값 → `Number.isNaN` 사용 244 | - +0, -0과 같은 값은 `Object.is`로 정확한 비교 245 | 246 | ### 7-4. 삼항 조건 연산자 247 | 248 | - `if-else`: 표현식이 아닌 문. 값처럼 사용 X 249 | - 삼항 연산자: 값으로 평가할 수 있는 표현식인 문. 값처럼 사용 가능 250 | 251 | ### 7-5. 논리 연산자 252 | 253 | - `!` 연산자: 암묵적 불리언 타입 변환 254 | - `논리합(||), 논리곱(&&)` 결과: 피연산자 중 어느 한 쪽으로 단축 평가 됨 255 | 256 | ### 7-8. typeof 연산자 257 | 258 | - 피연산자의 데이터 타입을 문자열로 반환 → 데이터 타입과 정확히 일치하진 않음 259 | 260 | ```js 261 | typeof NaN; // -> "number" 262 | typeof null; // -> "object" 263 | 264 | var foo = null; 265 | foo === null; 266 | ``` 267 | 268 | - `null`은 object 반환 → JS 버그 but 사이드 이펙트 때문에 수정하지 않음. `===` 사용 권장 269 | - 선언하지 않은 식별자를 `typeof` 연산하면 `undefined` 반환 270 | 271 | ### 7-11. 연산자의 부수 효과 272 | 273 | - 다른 코드에 영향을 주는 부수 효과가 있는 연산자 → `=`, `++`/`--`, `delete` 274 | 275 | --- 276 | 277 | ## 8장 제어문 278 | 279 | - 조건에 따라 코드 블록을 실행하거나 반복할 때 사용함. 제어문을 사용하면 위 순차적 코드 실행을 인위적으로 제어할 수 있음. 280 | 281 | ### 8-2. 조건문 282 | 283 | - 조건식 평과 결과에 따라 코드 블록의 실행을 결정함. 불리언 값으로 평가될 수 있는 표현식 284 | 285 | 1. if-else 문 286 | 287 | - 조건식이 불리언이 아닌 값이면 암묵적으로 불리언으로 강제 변환 실행 288 | - if-else는 삼항 조건 연산자로 바꿔쓸 수 있음 → 단순히 값을 결정하여 변수에 할당하는 경우에 유용함 289 | - 논리적 참, 거짓으로 실행 블록 결정 290 | 291 | 2. switch 문 292 | 293 | - 주어진 표현식을 평가하여 그 값과 일치하는 표현식을 갖는 case문으로 실행 흐름을 옮김. `case 표현식1:` 294 | - 보다 다양한 상황(case)에 따라 블록 결정 295 | - fall through: switch문 탈출하지 않고 모든 case, default 실행하는 것 296 | 297 | ### 8-3. 반복문 298 | 299 | 조건식 평과 결과가 거짓일 때 까지 코드 블록 실행을 반복 300 | 301 | 1. for 문: 반복횟수가 명확할 때 주로 사용 302 | 2. while 문: 반복 횟수가 불명확할 때 주로 사용 → 조건식 평과 결과를 불리언 값으로 변환함 303 | 3. do-while 문: 코드 블록을 먼저 실행하고 조건식을 평가함 → 코드 블록은 무조건 한 번 이상 실행 304 | 305 | ### 8-4. break문 306 | 307 | - 레이블 문(식별자가 붙은 문), 반복문에서 코드 블록을 탈출함. 이 외 break문 사용 시 SyntaxError 308 | - 내부에서 외부 for문을 탈출하려면 레이블 문을 사용해야 함 309 | 310 | ```js 311 | // outer라는 식별자가 붙은 레이블 for 문 312 | outer: for (var i = 0; i < 3; i++) { 313 | for (var j = 0; j < 3; j++) { 314 | // i + j === 3이면 outer라는 식별자가 붙은 레이블 for 문을 탈출한다. 315 | if (i + j === 3) break outer; 316 | console.log(`inner [${i}, ${j}]`); 317 | } 318 | } 319 | 320 | console.log("Done!"); 321 | ``` 322 | 323 | --- 324 | 325 | ## 9장 타입 변환과 단축 평가 326 | 327 | ### 9-1. 타입 변환이란 328 | 329 | - 기존 원시 값으로 다른 타입의 새로운 원시 값 생성하는 것 (기존 값 변화 X) 330 | - 타입 변화를 예측할 수 있어야 한다 331 | 332 | 1. 명시적 타입 변환(타입 캐스팅): 개발자가 의도적으로 값의 타입을 변환하는 것 333 | 2. 암묵적 타입 변환(타입 강제 변환): 개발자 의도 관련 없이 엔진에 의해 암묵적으로 타입이 자동 변환되는 것 334 | 335 | ### 9-2. 암묵적 타입 변환 336 | 337 | - 코드 문맥을 고려해 암묵적으로 타입 강제 변환하는 경우 338 | - 가급적 에러를 발생 시키지 않고 원시 타입으로 자동 변환함 339 | 340 | 1. 문자열 타입으로 변환 341 | 342 | - 연산자 `+`를 사용하고 피연산자 중 하나가 문자열이거나, 템플릿 리터럴 표현식 삽입 시 변환함 343 | 344 | ```js 345 | // 숫자 타입 346 | -0 + '' // -> "0" 347 | -Infinity + '' // -> "-Infinity" 348 | 349 | // 심벌 타입 350 | (Symbol()) + '' // -> TypeError: Cannot convert a Symbol value to a string 351 | 352 | // 객체 타입 353 | ({}) + '' // -> "[object Object]" 354 | Math + '' // -> "[object Math]" 355 | [] + '' // -> "" 356 | [10, 20] + '' // -> "10,20" 357 | (function(){}) + '' // -> "function(){}" 358 | Array + '' // -> "function Array() { [native code] }" 359 | ``` 360 | 361 | 2. 숫자 타입으로 변환 362 | 363 | - 산술 연산자 사용 시 피연산자를 숫자 타입으로 변환함 364 | - 변환 불가하면 NaN 반환 365 | 366 | --- 367 | 368 | # 🔗 인사이트 369 | 370 | - numeric-separators: 큰 숫자 리터럴 선언 시 가독성 개선 371 | - [https://dev.to/suprabhasupi/numeric-separators-in-javascript-3jec](https://dev.to/suprabhasupi/numeric-separators-in-javascript-3jec) 372 | - `!!` vs `Boolean()` 373 | -------------------------------------------------------------------------------- /2회차/README.md: -------------------------------------------------------------------------------- 1 | # 2회차 2 | 3 | ## 1. 히든 클래스 4 | 5 | 객체에서 프로퍼티에 접근할 때 히든 클래스를 사용해서 빠른 속도를 보장한다. 6 | 7 | ### 1-1. 관련 자료 8 | 9 | - [Fast properties in V8](https://v8.dev/blog/fast-properties) 10 | - [V8의 히든 클래스 이야기](https://engineering.linecorp.com/ko/blog/v8-hidden-class) 11 | - [자바스크립트 엔진의 최적화 기법 (2) - Hidden class, Inline Caching](https://meetup.toast.com/posts/78) 12 | - [Breaking the JavaScript Speed Limit with V8](https://www.youtube.com/watch?v=UJPdhx5zTaw) 13 | - [JS의 객체는 hash table이 아닙니다!](https://velog.io/@wongue_shin/JS의-객체는-hash-table이-아닙니다) 14 | 15 | ## 2. React에서 함수 선언문/표현식 사용하는 이유 16 | 17 | ### 2-1. 선언문 18 | 19 | 1. React 공식 문서에서 사용한다. 20 | 21 | 2. React.FC를 사용하지 않고 Props에 대한 타입만 설정하기 위해 사용한다. 22 | - 취향 차이: 이렇게 사용하면 Children을 명시적으로 작성하는 것이 귀찮다. 23 | - 추가 의견: 비교적 최근에 사용한다. 24 | 25 | ### 2-2. 표현식 26 | 27 | 1. `useMemo`, `useCallbak`을 사용할 때 화살표 함수가 편리하다. 28 | 29 | 2. React.FC를 사용할 때 편리하다. 30 | - 취향 차이: 이렇게 사용하면 Children을 안 써도 암묵적으로 포함되어 있는 것이 신경쓰인다. 31 | - 추가 의견: 비교적 과거에 사용했다. 32 | 33 | ### 2-3. 추가 내용 34 | 35 | - 회사 컨벤션에 따라 사용한다. 36 | - 호이스팅 때문에 선언식이 문제가 되는 경우가 있다. 그래서 요즘은 표현식을 많이 사용한다. 37 | 38 | > 추가 과제: 히든 클래스 공부하기 39 | -------------------------------------------------------------------------------- /2회차/img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hy57in/study-javascript-deep-dive/aed951621cd80b2830fc238d625027502e88d41c/2회차/img.png -------------------------------------------------------------------------------- /2회차/img_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hy57in/study-javascript-deep-dive/aed951621cd80b2830fc238d625027502e88d41c/2회차/img_1.png -------------------------------------------------------------------------------- /2회차/img_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hy57in/study-javascript-deep-dive/aed951621cd80b2830fc238d625027502e88d41c/2회차/img_2.png -------------------------------------------------------------------------------- /2회차/img_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hy57in/study-javascript-deep-dive/aed951621cd80b2830fc238d625027502e88d41c/2회차/img_3.png -------------------------------------------------------------------------------- /2회차/img_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hy57in/study-javascript-deep-dive/aed951621cd80b2830fc238d625027502e88d41c/2회차/img_4.png -------------------------------------------------------------------------------- /2회차/김록원.md: -------------------------------------------------------------------------------- 1 | # 10장 - 객체 리터럴 2 | js는 원시 값을 제외한 나머지 값(함수, 배열, 정규표현식 등)은 모두 객체이다. 3 | js는 **프로토타입 기반 객체지향 언어**로 클래스 기반 객체지향 언어와는 달리 다양한 객체 생성 방법을 지원한다. 4 |
5 | 6 | **객체 생성 방법** 7 | - 객체 리터럴 8 | - Object 생성자 함수 9 | - 생성자 함수 10 | - Object.create 메서드 11 | - 클래스(ES6) 12 | 13 | **객체 리터럴의 특징** 14 | - 객체 리터럴은 js의 유연함과 강력함을 대표하는 객체 생성 방식.(객체를 생성과 동시에 프로퍼티 만들기, 생성 이후에도 프로퍼티를 동적으로 추가 가능) 15 | - 객체 리터럴을 제외한 다른 객체 생성방식은 모두 함수를 사용함. 16 | ```js 17 | var person = { 18 | name: 'Lee', 19 | sayHeelo: function() { 20 | console.log('Hello! My name is ${}' ) 21 | } 22 | } 23 | ``` 24 |
25 | 26 | ### 객체리터럴의 프로퍼티의 특징 27 | 1. 프로퍼티 키로 모든 문자열 또는 심벌값을 사용할 수 있다. 28 | 2. 프로퍼티 키로 식별자 네이밍 규칙을 지킨다면 따옴표를 사용하지 않고 작성 가능하다. 29 | 3. 문자열로 평가할 수 있는 표현식을 사용해 프로퍼티 키를 동적으로 생성할 수 있다.(대괄호 이용) 30 | 4. 프로퍼티 키로 문자열 혹은 심벌값 이외의 값을 사용하면 암묵적으로 타입 변환을 통해 문자열이 된다. 31 | 5. 프로퍼티 키를 중복선언 시 나중에 선언한 프로퍼티가 덮어쓴다. 32 | 6. 프로퍼티 값으로 함수(메서드)를 사용할 수 있다. js에서 함수는 (일급)객체이므로 값으로 취급한다. 33 | ```js 34 | var person = { 35 | lastName: 'Kim', // 2 36 | 1: 3, // 4 37 | lastName: 'Lee', // 5 38 | say: function () { // 6 39 | console.log(this.lastName); 40 | console.log(this['1'], this[1]); 41 | console.log(this.age); 42 | } 43 | }; 44 | 45 | person['age'] = 21; // 3 46 | person.say(); 47 | ``` 48 |
49 | 50 | 51 | ### 객체리터럴의 프로퍼티 접근 52 | 프로퍼티에 접근하기 위한 방법으로 마침표 표기법과 대괄호 표기법이 존재한다. 53 | 54 | 1. 프로퍼티 키가 식별자 네이밍 규칙을 준수한다면 마침표 표기법을 이용해 접근가능하다. 55 | 2. 대괄호 표기법을 이용해 접근할때는 반드시 따옴표로 감싼 문자열이어야 한다. 56 | 3. delete 연산자를 이용해 객체의 프로퍼티를 삭제할 수 있다. 57 | ```js 58 | var person = { 59 | age: 37 60 | }; 61 | console.log(person.age); 62 | console.log(person[age]) // Reference error 63 | console.log(person['age']); 64 | console.log(person.name) // undefined 65 | 66 | delete person['age']; 67 | console.log(person.age) // undefined 68 | ``` 69 |
70 | 71 | 72 | ### ES6부터 추가된 내용 73 | 1. 프로퍼티 축약 표현(값으로 사용되는 변수 이름과 프로퍼티 키 이름이 같다면 프로퍼티 키 생략 가능) 74 | 2. Computed Property Name(프로퍼티 키를 동적으로 생성가능) 75 | 3. 메서드 축약표현 (function 키워드를 생략한 축약 표현을 사용가능) 76 | ```js 77 | let x = 1, y = 2; 78 | 79 | const obj = { 80 | x, y, 81 | [`${x} + ${y}`]: x + y, 82 | say() { 83 | console.log(obj) // { x: 1, y: 2, '1 + 2': 3, say: [Function: say] } 84 | } 85 | }; 86 | obj.say(); 87 | ``` 88 |
89 | 90 | ### 🤔 템플릿 리터럴이 객체리터럴의 프로퍼티 키가 될 수 없는 이유 91 | [템플릿 리터럴이 객체리터럴의 프로퍼티 키가 될 수 없는 이유](https://stackoverflow.com/questions/33194138/template-string-as-object-property-name) 92 | 템플릿 리터럴이 표현식(값으로 판단)이기 때문! 문자열 리터럴이 아님! 단지 런타임에서 문자열로 변환해주는 것 뿐 93 | 94 |
95 |
96 |
97 | 98 | # 11장 - 원시 값과 객체의 비교 99 | 원시 타입과 객체 타입의 차이 3가지 100 | - 원시값은 불변의 값이며, 객체는 변경 가능한 값이다. 101 | - 원시값을 변수에 할당 시 실제 값이 저장, 객체를 변수에 할당 시 참조 값이 저장된다. 102 | - 원시값을 다른 변수에 할당하면 원시값이 복사되어 전달, 객체는 원본의 참조값이 복사되어 전달 103 |
104 | 105 | ### 원시깂의 특징 106 | 원시값은 변경불가능하다. 만약 변수에 할당 및 재할당을 할 경우 값이 바뀌는 것이 아니라 새로운 공간을 확보 후 할당된 값을 저장, 변수는 새롭게 재할당한 윈시 값을 가리킴. 107 | 즉 변수가 가리키는 메모리 공간이 바뀌며 값은 변하지 않는다. 값의 이러한 특성을 **불변성**이라고 한다. 108 |
109 | 110 | ### js의 문자열 111 | js에서 문자열은 객체가 아닌 원시값이다. 112 | 하지만 유사배열객체로써 인덱스 접근, length 프로퍼티를 가지고 있다. 113 | 다만, 원시값이기때문에 문자열 중 일부 문자열을 변경하는 것은 불가능하다. 114 | ```js 115 | var str = 'string'; 116 | str[0] = 'S'; 117 | console.log(str); // string 118 | ``` 119 |
120 | 121 | ### 다른 언어와 비교하는 값에 의한 전달 122 | python의 경우 처음 변수에 원시 값을 갖는 변수를 할당하는 시점에는 두 변수가 같은 메모리 공간을 가리키고 있다. 두 변수 중 하나에 값을 재할당했을때 비로소 서로 다른 메모리 공간을 갖는다. 123 |
124 | 125 | ### 객체 126 | 프로퍼티의 정해져있지 않으며, 동적으로 추가/삭제 가능하다. 프로퍼티 값에 대한 제약도 없다. 127 | 원시 값과 같이 확보해야 할 메모리 공간의 크기를 사전에 정해 둘 수 없다. 128 | 따라서 원시 값과는 다른 방식으로 동작하도록 설계되어있다. 129 |
130 | 131 | ### js 객체의 관리방식 132 | 해시 테이블이라고 생각할 수 있지만 대부분의 엔진은 성능을 위해 더 나은 방법으로 객체를 구현한다. 133 | js의 객체는 매우 편리하지만(동적 추가/삭제) 성능 면에서는 접근에 비용이 많이든다. 134 | v8엔진은 동적 탐색 대신 히든 클래스라는 방식을 사용해(C++ 객체의 프로퍼티에 접근하는 정도의 성능을 보장한다.) 135 |
136 | 137 | ### 객체 - 변경가능한 값 138 | 객체를 변경할 때마다 원시 값처럼 이전 값을 복사해 새롭게 생성한다면 비용이 많이 들 것이다.(객체의 크기가 매우클수도 있으며 크기도 일정하지 않으므로) 139 | 즉, 메모리의 효율적 소비가 어렵고 성능이 나빠진다. 140 | 객체를 복사해 생성하는 비용을 절약하여 성능을 향상시키기 위해 변경 가능한 값으로 설계되었다. 이에 따른 부작용으로 여러 개의 식별자가 하나의 객체를 공유할 수 있다. 141 | 142 | 객체 생성 시 변수의 메모리 공간에는 객체가 존재하는 메모리 공간에 대한 참조값이 저장된다. 143 | 따라서 다른 변수에 할당시 이 참조값이 해당 변수의 메모리 공간에 들어가게 된다. 144 | 객체의 프로퍼티에 접근 시 해당 참조값을 통해 접근하게 되므로 같은 객체의 프로퍼티를 접근하게 된다. 145 |
146 | 147 | ### 🤔 히든 클래스의 객체 관리 방식 148 | 정적 언어의 경우 각 프로피터마다 offset을 가진다. 프로퍼티를 참조할 경우 offset을 이용하기 때문에 O(1)의 시간복잡도를 갖는다. 149 | js는 동적으로 프로퍼티를 추가/삭제를 할 수 있으므로 애초부터 offset을 가지고 있기 힘들다. 이를 가능하게 하기 위해 Hidden Class + Inline Caching을 이용한다. 150 | https://ui.toast.com/weekly-pick/ko_20210909 151 | https://meetup.toast.com/posts/78 152 | 이것도 결국엔 오버헤드가 존재하기 때문에 정적인 언어라고 생각하고 사용하는 것이 성능상 이점이 많다. 153 |
154 | 155 | ### 🤔 히든 클래스 생성에 따른 메모리 오버헤드? 156 | 157 |
158 |
159 |
160 | 161 | # 12장 - 함수 162 | js에서 함수는 객체 타입의 값이다. 그러므로 함수 또한 함수 리터럴로 생성할 수 있다. 163 | js에서 함수를 정의하는 방법은 다음과 같다. 164 | - 함수 선언문 165 | - 함수 표현식 166 | - Function 생성자 함수 167 | - 화살표 함수(ES6) 168 |
169 | 170 | ### 함수 선언문 171 | - 함수 리터럴은 이름을 생략할 수 있으나 함수 선언문은 생략할 수 없다. 172 | - 함수 선언문은 문이지만 표현식은 아니다. 173 | - 런타임 전 함수 호이스팅으로 선언과 동시에 함수 객체를 생성한다. 174 | - js엔진이 암묵적으로 함수 이름을 식별자로 생성한다. 175 | ```js 176 | function add(x, y) { 177 | return x + y; 178 | } 179 | 180 | console.log(add(2,5)); 181 | ``` 182 |
183 | 184 | ### 함수 표현식 185 | - js의 함수는 값처럼 변수에 할당, 프로퍼티의 값, 배열의 요소가 될 수 있다.(일급 객체) 186 | - 리터럴의 함수 이름은 생략할 수 있다.(익명 함수) 187 | - 표현식은 함수 호이스팅이 아닌 변수 호이스팅의 영향을 받기 때문에 함수 객채가 런타임 전에 생성되지는 않는다. 188 | ```js 189 | var sub = function (x, y) { 190 | return x - y; 191 | } 192 | ``` 193 |
194 | 195 | ### Function 생성자 함수 196 | - Function 생성자 함수에 매개변수 목록과 함수 몸체를 문자열로 전달하여 생성한다. 197 | - 클로저를 생성하지 않는 듯 선언문, 표현식으로 생성한 함수와 다르게 동작한다. 198 | ```js 199 | var add = new Function('x', 'y', 'return x + y'); 200 | console.log(add(2, 5)); 201 | ``` 202 |
203 | 204 | ### 화살표 함수 205 | - function 키워드 대신에 `=>`를 사용해 간략한 방법으로 함수를 선언할 수 있으며 항상 익명 함수로 정의한다. 206 | - 표현만 간략한 것이 아니라 내부 동작또한 간략화 되어 있다.(생성자 함수로 사용X, this 바인딩, prototype 프로퍼티가 없으며 Argument 객체를 생성하지 않음) 207 | ```js 208 | const add = (x,y) => x + y; 209 | ``` 210 |
211 | 212 | 213 | ### js 함수의 특징 214 | - js 엔진은 문맥에따라 함수 선언문인지 함수 리터럴인지를 판단한다. 215 | - 인수가 매개변수의 수를 초과하면 초과된 인수들은 무시된다(argument 객체에 보관함) 216 | - 인수가 매개변수 보다 적으면 나머지 값들은 undefined 값이다. 217 |
218 | 219 | ### IIFE(Immediately Invoked Function Expression) 220 | - 즉시실행 함수는 단 한 번만 호출되며 다시 호출 할 수 없다. 221 | - 즉시실행 함수는 정의되자마자 실행된다. 222 | - 그룹 연산자 `( )`를 이용해 감싸야 한다. 223 | ```js 224 | (function fool() { })(); 225 | ``` 226 |
227 | 228 | 229 | ### 고차함수와 콜백함수 230 | 함수의 매개변수를 통해 다른 함수의 내부로 전달되는 함수를 콜백함수라고 부르며 231 | 외부에서 콜백함수를 전달받은 함수를 고차 함수라고 부른다. 232 | 고차 함수는 콜백 함수를 자신의 일부분으로 합성한다. 233 |
234 | 235 | ### 순수 함수와 비순수 함수 236 | 순수 함수는 어떤 외부 상태에 의존하지 않고 변경하지도 않는 부수 효과가 없는 함수다. 동일한 인수가 전달되면 언제나 동일한 값을 반환하는 함수이다.(외부 상태뿐만이 아닌 호출될때마다 변하는 값(현재 시간 등)에 의존하지 않는다.) 순수 함수의 목적은 부수효과를 최소화에 불변성을 지향하는 것이다. 237 | 238 |
239 |
240 |
241 | 242 | # 13장 - 스코프 243 | 모든 식별자는 자신이 선언된 위치에 의해 다른 코드가 식별자 자신을 참조할 수 있는 유효 범위가 결정된다. 즉, 스코프는 식별자가 유효한 범위를 말한다. 244 | '코드가 어디서 실행되며 주변에 어떤 코드가 있는지'를 **렉시컬 환경** 이라고 부른다. 245 | 코드의 문맥은 렉시컬 환경으로 이뤄진다. 246 | 이러한 코드의 문맥을 구현한 것이 `실행 컨텍스트`이다. 코든 코드들은 실행 컨텍스트에서 평가되고 실행된다. 247 | 248 | ### 스코프의 종류 249 | 변수는 자신이 선언된 위치에 의해 유효 범위인 스코프가 결정된다. 크게 전역과 지역 스코프가 존재한다. 여기서 지역이란 함수 몸체 내부를 뜻한다. 전역변수는 어디서든 참조가 가능하며 지역 변수는 지역 내에서만 참조가 가능하다. 250 |
251 | 252 | ### 스코프 체인 253 | js엔진은 스코프 체인을 통해 변수를 참조하는 코드의 스코프부터 시작해 상위 스코프 방향으로 선언된 변수를 검색한다. 가장 상위는 전역스코프이며 전역 스코프부터 힘수, 중첩함수순으로 체이닝되어 있다. 254 | 전역 변수는 최상위이기에 검색시 가장 늦게 확인하므로 검색속도가 느리다. 255 | (+ 호이스팅은 스코프 단위로 동작한다.) 256 |
257 | 258 | ### 레시컬 스코프 259 | ```js 260 | var x = 1; 261 | 262 | function foo() { 263 | var x = 10; 264 | bar(); 265 | } 266 | 267 | function bar() { 268 | console.log(x); 269 | } 270 | 271 | foo(); // 1 272 | bar(); // 1 273 | ``` 274 | bar 함수의 상위 스코프가 무엇인지에 따라 결과값이 결정된다. 275 | - `동적 스코프` : 함수를 어디서 호출했는지에 따라 상위 스코프 결정 276 | - `렉시컬/정적 스코프` : 함수를 어디에 정의했는지에 따라 상위 스코프 결정 277 | 대부분의 언어는 렉시컬 스코프를 지원하며 js또한 마찬가지이다. 278 | 279 |
280 |
281 |
282 | 283 | # 14장 - 전역 변수의 문제점 284 | 지역변수의 경우 함수가 호출되어 실행되는 동안에만 유효하다. 즉 지역변수의 생명주기는 함수의 생명 주기와 일치한다. 전역변수는 전역객체의 프로퍼티가 된다. 즉 생명주기는 전역 객체의 생명 주기와 일치한다. 285 | - 모든 코드에서 참조하고 변경할 수 있기에 의도치 않은 상태 변경에 쉽게 노출된다. 286 | - 전역객체와 생명주기(프로그램이 동작 종류까지)가 같으므로 오랜 시간 메로리를 차지한다. 287 | - 스코프 체인상 가장 최상단이므로 검색속도가 느리다. 288 | - 파일 내에서 동일한 이름으로 존재하는 변수가 있을 시 예상치 못한 결과를 가져올 수 있다. 289 | 290 | 전역변수 사용 억제하기 291 | - 즉시 실행 함수를 사용하고 내부에 변수를 이용하기 292 | - 모듈 패턴 이용하기 293 | - ES6 모듈(독자적인 모듈 스코프를 제공하기 때문에 모듈 내에 선언해도 전역변수로 선언되지 않는다.) 294 | 295 |
296 |
297 |
298 | 299 | # 15장 - let, const 키워드와 블록 레벨 스코프 300 | `var`의 문제점 301 | - 변수 중복 선언을 허용한다(이미 선언된 것을 모른다면 부작용 발생) 302 | - 함수 레벨 스코프(if나 for문에서는 같은 스코프로 동작함) 303 | - 변수 호이스팅(할당 이전에 값을 불러와 undefined로 연산될 가능성이 높음) 304 | 305 | `let`으로 단점 보완 306 | - 변수 중복 선언을 금지! 307 | - 블록 레벨 스코프로 if, for문에서의 변수도 다른 스코프로 취급 308 | - 변수 호이스팅에 대해 var보다 비교적 안정적(초기화 시작 전까지 변수를 참조할 수 없는 구간존재 - TMZ) 309 | - let으로 선언하면 전역객체의 프로퍼티가 아님 개념적인 블록 내에 존재함 310 | 311 | `const`란 312 | - 코드로는 선언과 동시에 초기화를 해줘야함 313 | - 재할당 금지!(객체는 메모릐 공간의 주소값을 아예 바꾸지 않은 이상 객체 내 프로퍼티 변경은 가능) 314 | 315 | 어떻게 사용할까? 316 | - ES6를 사용한다면 var 키워드 사용하지 않기 317 | - 재할당이 필요한 경우에 한정해 let사용, 변수의 스코프는 최대한 좁게 318 | - 변경이 발생하지 않고 읽기 전용으로 사용하는 경우 const사용하기 -------------------------------------------------------------------------------- /2회차/김효진.md: -------------------------------------------------------------------------------- 1 | # 10. 객체 리터럴 2 | 3 | ## 객체 생성 방식은? 4 | - 객체 리터럴 5 | - Object 생성자 함수 6 | - 생성자 함수 7 | - Object.create 메서드 8 | - 클래스(ES6) 9 | 10 | ## 프로퍼티 11 | 객체는 프로퍼티의 집합이며, 프로퍼티는 키와 값으로 구성된다 12 | `프로퍼티 키` : 빈 문자열을 포함하는 모든 문자열 또는 심벌 값 13 | 프로퍼티 값 : 자바스크립트에서 사용할 수 있는 모든 값 14 | 15 | 식별자 네이밍 규칙을 따른다면 따옴표를 사용하지 않고 프로퍼티 키로 사용할 수 있다. 16 | ```jsx 17 | var person = { 18 | firstName: 'Ung-mo', // 식별자 네이밍 규칙을 준수하는 프로퍼티 키 19 | 'last-name': 'Lee' // 식별자 네이밍 규칙을 준수하지 않는 프로퍼티 키 20 | }; 21 | 22 | console.log(person); // {firstName: "Ung-mo", last-name: "Lee"} 23 | ``` 24 | 25 | ## 프로퍼티 접근 방식 .과 []의 차이 26 | - 마침표 프로퍼티 접근 연산자(`.`)를 사용하는 마침표 표기법 27 | - 대괄호 프로퍼티 접근 연산자(`[...]`)를 사용하는 대괄호 표기법 28 | - [] 내부에 지정하는 프로퍼티 키는 반드시 따옴표로 감싼 문자열이어야 한다 29 | - 따옴표로 감싸지 않은 이름은 식별자로 인식하여 ReferenceError가 발생한다. 식별자를 평가하기 위해 선언된 이름을 찾았지만 찾지 못했기 때문이다. 30 | 31 | ```jsx 32 | var person = { 33 | name: 'Lee', 34 | 1: 10 35 | }; 36 | 37 | // 마침표 표기법에 의한 프로퍼티 접근 38 | console.log(person.name); // Lee 39 | 40 | // 대괄호 표기법에 의한 프로퍼티 접근 41 | console.log(person['name']); // Lee 42 | 43 | console.log(person[name]); // ReferenceError: name is not defined 44 | 45 | console.log(person.age); // undefined 46 | 47 | // 프로퍼티 키가 숫자로 이뤄진 문자열인 경우 따옴표를 생략할 수 있다. 48 | person.1; // -> SyntaxError: Unexpected number 49 | person.'1'; // -> SyntaxError: Unexpected string 50 | person[1]; // -> 10 : person[1] -> person['1'] 51 | person['1']; // -> 10 52 | ``` 53 | 54 | 객체에 존재하지 않는 프로퍼티에 접근하면 undefined를 반환한다. 55 | ReferenceError를 발생하지 않는다는 점에 주의하자. 56 | 57 | 프로퍼티 키가 식별자 네이밍을 준수하지 않는 이름을 사용하면 반드시 대괄호 표기법을 사용해야 한다. 단, 프로퍼티 키가 숫자로 이뤄진 문자열일 경우 따옴표를 생략할 수 있다. 그 외에는 반드시 따옴표로 감싼 문자열이어야 한다 58 | 59 | ```jsx 60 | var person = { 61 | 'last-name': 'Lee', 62 | }; 63 | 64 | person.'last-name'; // -> SyntaxError: Unexpected string 65 | person.last-name; // -> 브라우저 환경: NaN 66 | // -> Node.js 환경: ReferenceError: name is not defined 67 | person[last-name]; // -> ReferenceError: last is not defined 68 | person['last-name']; // -> Lee 69 | ``` 70 | person.last-name 의 실행결과가 브라우저 환경과 Node.js 환경에서 다른 이유는? 71 | 72 | 자바스크립트 엔진은 먼저 person.last를 평가한다. person 객체에는 프로퍼티 키가 last인 프로퍼티가 없기 때문에 person.last는 undefined로 평가된다. 따라서 person.last-name은 undefined - name 과 같다. 다음으로 name을 찾는다. 이 때 name은 따옴표로 감싸져 있지 않고 식별자 네이밍 규칙을 따르므로 프로퍼티 키가 아니라 식별자로 해석된다. 73 | 74 | - 브라우저 환경 : 전역 변수 name은 window를 가리키며 기본값은 빈 문자열이기 때문에 undefined - ''과 같으므로 NaN이 된다 75 | - ![img_4.png](img_4.png) 76 | - Node.js 환경 : 어디에도 name 이라는 식별자가 없으므로 레퍼런스 에러가 발생한다. 77 | 78 | # 11. 원시 값과 객체의 비교 79 | 80 | ### 원시 타입과 객체 타입의 차이점을 설명하시오 81 | - 원시 타입의 값은 변경 불가능한 값(immutable value)이고 객체 타입의 값은 변경 가능한 값(mmutable value)이다 82 | - 원시 값을 변수에 할당하면 변수(확보된 메모리 공간)에는 실제 값이 저장되지만 객체를 변수에 할당하면 변수에는 참조 값이 저장된다. 83 | - 원시 값을 갖는 변수는 다른 변수에 할당하면 원본이 원시 값이 복사되어 전달된다. 이를 값에 의한 전달(pass by value)이라 한다. 객체를 가리키는 변수를 다른 변수에 할당하면 원본의 참조 값이 복사되어 전달된다. 참조에 의한 전달(pass by value) 84 | 85 | ![img.png](img.png)![[Pasted image 20221009201508.png]] 86 | 87 | ![img_1.png](img_1.png)![[Pasted image 20221009201515.png]] 88 | 89 | 변수 값을 변경하기 위해 원시 값을 재할당하면 새로운 메모리 공간을 확보하고 재할당한 값을 저장한 후, 변수가 참조하던 메모리 공간의 주소를 변경한다. 이를 `불변성`이라 한다. 90 | 91 | 92 | ## 값에 의한 전달 93 | 94 | ![img_2.png](img_2.png) 95 | ![img_3.png](img_3.png)![[Pasted image 20221009204322.png]] 96 | 97 | 98 | ```jsx 99 | var score = 80; 100 | 101 | // copy 변수에는 score 변수의 값 80이 복사되어 할당된다. 102 | var copy = score; 103 | 104 | console.log(score, copy); // 80 80 105 | console.log(score === copy); // true 106 | 107 | // score 변수와 copy 변수의 값은 다른 메모리 공간에 저장된 별개의 값이다. 108 | // 따라서 score 변수의 값을 변경해도 copy 변수의 값에는 어떠한 영향도 주지 않는다. 109 | score = 100; 110 | 111 | console.log(score, copy); // 100 80 112 | console.log(score === copy); // false 113 | ``` 114 | 115 | 중요한 것은, 두 변수의 원시 값은 서로 다른 메모리 공간에 저장된 별개의 값이 되어 어느 한쪽에서 재할당을 통해 값을 변경하더라도 서로 간섭할 수 없다 116 | 117 | ## 객체 118 | 해시테이블 119 | https://ko.wikipedia.org/wiki/%ED%95%B4%EC%8B%9C_%ED%85%8C%EC%9D%B4%EB%B8%94 120 | 121 | **해시 테이블**(hash table), **해시 맵**(hash map), **해시 표**는 [컴퓨팅](https://ko.wikipedia.org/wiki/%EC%BB%B4%ED%93%A8%ED%8C%85 "컴퓨팅")에서 [키](https://ko.wikipedia.org/wiki/%EA%B3%A0%EC%9C%A0_%ED%82%A4 "고유 키")를 [값](https://ko.wikipedia.org/wiki/%EA%B0%92_(%EC%BB%B4%ED%93%A8%ED%84%B0_%EA%B3%BC%ED%95%99) "값 (컴퓨터 과학)")에 매핑할 수 있는 구조인, [연관 배열](https://ko.wikipedia.org/wiki/%EC%97%B0%EA%B4%80_%EB%B0%B0%EC%97%B4 "연관 배열") 추가에 사용되는 [자료 구조](https://ko.wikipedia.org/wiki/%EC%9E%90%EB%A3%8C_%EA%B5%AC%EC%A1%B0 "자료 구조")이다. 해시 테이블은 [해시 함수](https://ko.wikipedia.org/wiki/%ED%95%B4%EC%8B%9C_%ED%95%A8%EC%88%98 "해시 함수")를 사용하여 색인(index)을 버킷(bucket)이나 슬롯(slot)의 배열로 계산한다. 122 | ![[Pasted image 20221009210932.png]] 123 | 124 | 자바스크립트 엔진이 객체를 어떻게 관리하는지? 125 | 126 | Fast properties in V8 : https://v8.dev/blog/fast-properties 127 | 128 | V8 히든 클래스 이야기 : https://engineering.linecorp.com/ko/blog/v8-hidden-class/ 129 | 130 | 자바스크립트 엔진의 최적화 기법 (2) : https://meetup.toast.com/posts/78 131 | 132 | Breaking the Javscript Speed Limit with V8 : https://www.youtube.com/watch?v=UJPdhx5zTaw 133 | 134 | 135 | ### 얕은 복사와 깊은 복사 136 | 얕은 복사는 한 단계까지만 복사, 깊은 복사는 객체에 중첩되어 있는 객체까지 모두 복사 137 | ```jsx 138 | const o = { x: { y: 1 } }; 139 | 140 | // 얕은 복사 141 | const c1 = { ...o }; // 35장 "스프레드 문법" 참고 142 | console.log(c1 === o); // false 143 | console.log(c1.x === o.x); // true 144 | 145 | // lodash의 cloneDeep을 사용한 깊은 복사 146 | // "npm install lodash"로 lodash를 설치한 후, Node.js 환경에서 실행 147 | const _ = require('lodash'); 148 | // 깊은 복사 149 | const c2 = _.cloneDeep(o); 150 | console.log(c2 === o); // false 151 | console.log(c2.x === o.x); // false 152 | ``` 153 | 얕은 복사는 객체에 중첩되어 있는 객체의 경우 참조 값을 복사하고 깊은 복사는 객체에 중첩되어 있는 객체까지 모두 복사해서 원시 값처럼 완전한 복사본을 만든다는 차이가 있다 154 | 155 | ```jsx 156 | const v = 1; 157 | 158 | // "깊은 복사"라고 부르기도 한다. 159 | const c1 = v; 160 | console.log(c1 === v); // true 161 | 162 | const o = { x: 1 }; 163 | 164 | // "얕은 복사"라고 부르기도 한다. 165 | const c2 = o; 166 | console.log(c2 === o); // true 167 | ``` 168 | 169 | ### 퀴즈 170 | ```jsx 171 | var person1 = { 172 | name: 'Lee' 173 | }; 174 | 175 | var person2 = { 176 | name: 'Lee' 177 | }; 178 | 179 | console.log(person1 === person2); // ① 180 | console.log(person1.name === person2.name); // ② 181 | ``` 182 | 183 | 1은 false 184 | 2는 true 185 | 186 | ![[Pasted image 20221009212904.png]] 187 | 188 | # 12. 함수 189 | 190 | # 13. 스코프 191 | 192 | # 14. 전역 변수의 문제점 193 | 194 | # 15. let, const 키워드와 블록 레벨 스코프 -------------------------------------------------------------------------------- /2회차/이병현.md: -------------------------------------------------------------------------------- 1 | # 10 ~ 15 2 | 3 | ## 객체 리터럴이란? 4 | 5 | 객체 리터럴이란 객체를 생성하는 방식 중 하나로 문자 또는 약속된 기호를 사용하여 값을 생성하는 표기법을 말한다. 아래와 같이 `{}`를 통해 생성할 수 있다. 6 | 7 | ```js 8 | var person = { 9 | name: 'Lee', 10 | sayHello: function () { 11 | console.log(`Hello! My name is ${this.name}.`); 12 | } 13 | }; 14 | 15 | console.log(typeof person); // object 16 | console.log(person); // {name: "Lee", sayHello: ƒ} 17 | ``` 18 | 19 | ## 객체 프로퍼티 정의 및 접근 시 주의점 20 | 21 | 객체의 프로퍼티를 정의하거나 접근할 때 주의해야 할 점이 몇가지 있다. 22 | 23 | ### 프로퍼티 키의 암묵적 타입 변환 24 | 기본적으로 객체의 프로퍼티는 문자열 또는 심벌 값이다. 하지만 이외의 값을 사용하면 암묵적 타입 변환을 통해 문자열이 되며, 실제 값을 보면 따옴표가 붙지 않지만 문자열로 변환된다. 25 | 26 | ```js 27 | var foo = { 28 | 0: 1, 29 | 1: 2, 30 | 2: 3 31 | }; 32 | 33 | console.log(foo); // {0: 1, 1: 2, 2: 3} 34 | ``` 35 | 36 | ### 중복 프로퍼티 키 37 | 38 | 중복되는 키를 선언하면 나중에 선언한 프로퍼티가 먼저 선언한 프로퍼티를 덮어쓴다. 또한 에러도 발생하지 않는다. 39 | 40 | ```js 41 | var foo = { 42 | name: 'Lee', 43 | name: 'Kim' 44 | }; 45 | 46 | console.log(foo); // {name: "Kim"} 47 | ``` 48 | 49 | 50 | 🧐🧐🧐 51 | _그렇다면 심볼은?_ 52 | 53 | -> 심볼이기에 덮어씌우지 않으며 추가된다. `Symbol.for`로 이미 있는 심볼을 찾아 쓰는 경우면 덮어씌우게 된다. 54 | 55 | ```js 56 | var foo = {}; 57 | 58 | foo[Symbol('foo')] = 'foo'; 59 | foo[Symbol('foo')] = 'bar'; 60 | 61 | console.log(foo); // {Symbol(foo): 'foo', Symbol(foo): 'bar'} 62 | 63 | foo[Symbol.for('hoge')] = 'hoge'; 64 | foo[Symbol.for('hoge')] = 'fuga'; 65 | 66 | console.log(foo); // {Symbol(foo): 'foo', Symbol(foo): 'bar', Symbol(hoge): 'fuga'} 67 | ``` 68 | 69 | 70 | ### 대괄호 접근과 일반 프로퍼티 접근 71 | 72 | 대괄호로 없는 프로퍼티를 접근하게 되면 `ReferenceError`가 나오게 되지만, 일반적인 프로퍼티 접근 방식으로는 `undefined`를 돌려받게 된다. 73 | 74 | ```js 75 | var person = { 76 | name: 'Lee' 77 | }; 78 | 79 | console.log(person[name]); // ReferenceError: name is not defined 80 | ``` 81 | 82 | ```js 83 | var person = { 84 | name: 'Lee' 85 | }; 86 | 87 | console.log(person.age); // undefined 88 | ``` 89 | 90 | ## 메서드 91 | 92 | 객체의 프로퍼티가 함수일 경우 메서드라고 부른다. 93 | 94 | ## ES6에서의 객체 리터럴 확장 기능 95 | 96 | ### 프로퍼티 축약 97 | 98 | 프로퍼티 이름을 변수명으로 사용하게되는 방식을 사용할 수 있다. 99 | 100 | ```js 101 | // ES6 102 | let x = 1, y = 2; 103 | 104 | // 프로퍼티 축약 표현 105 | const obj = { x, y }; 106 | 107 | console.log(obj); // {x: 1, y: 2} 108 | ``` 109 | 110 | ### 계산된 프로퍼티 이름 111 | 112 | 동적으로 프로퍼티 이름을 사용할 수 있다. 113 | 114 | ```js 115 | // ES6 116 | const prefix = 'prop'; 117 | let i = 0; 118 | 119 | // 객체 리터럴 내부에서 계산된 프로퍼티 이름으로 프로퍼티 키 동적 생성 120 | const obj = { 121 | [`${prefix}-${++i}`]: i, 122 | [`${prefix}-${++i}`]: i, 123 | [`${prefix}-${++i}`]: i 124 | }; 125 | 126 | console.log(obj); // {prop-1: 1, prop-2: 2, prop-3: 3} 127 | ``` 128 | 129 | ### 메서드 축약 표현 130 | 131 | 일반 함수를 프로퍼티에 할당 하는 것이 아니라, 함수 그자체를 표현하는 방식이 가능하다. 132 | 133 | ```js 134 | // ES6 135 | const obj = { 136 | name: 'Lee', 137 | // 메서드 축약 표현 138 | sayHi() { 139 | console.log('Hi! ' + this.name); 140 | } 141 | }; 142 | 143 | obj.sayHi(); // Hi! Lee 144 | ``` 145 | 146 | 메서드 축약 표현된 메서드는 프로펕에 할당된 함수와 다르게 작동하는데 26.2절에서 더 자세하게 다룬다. 147 | 148 | 스포일러하자면 `constructor`가 내부에 없어 인스턴스를 생성할 수 없고, `prototype`프로퍼티가 없다. 또한 `super` 키워드로 자신을 바인딩한 객체를 가리키는 내부 슬롯 `[[HomeObject]]` 을 사용할 수 있다. 149 | 150 | ## 값에 의한 전달, 참조에 의한 전달 151 | 152 | 원시 값을 재할당하는 경우 새로운 메모리 주소에 값을 넣고 변수또한 그 메모리 주소를 가리키게 된다. 153 | 154 | 하지만, 객체의 경우 재할당 하더라도 기존에 가지고 있던 값의 메모리 주소에서 값을 변경하게 되며, 메모리 주소가 변경되지 않는다. 155 | 156 | 따라서, 원시 값을 복사하는 경우 값을 복사하여 새로운 메모리 주소를 가리키게 되므로, 복사한 변수들 끼리 같은 메모리 주소를 가리키지 않는다. 157 | 158 | 객체는 얕은 복사와 깊은 복사가 있는데, 얕은 복사의 경우 한 단계까지만 복사를 하며, 같은 값의 메모리 주소를 가리키게 된다. 깊은 복사의 경우 객체의 중첩되어 있는 값들 까지 모두 복사하지만 같은 메모리 주소를 가리키지 않게 된다. 159 | 160 | ## 히든 클래스 161 | 162 | V8 엔진에서는 객체의 프로퍼티에 접근하는 방식을 최적화하기 위해 히든 클래스라는 방식을 사용한다. 자바와 같이 고정된 객체 레이아웃(클래스)과 유사하게 동작한다. 163 | 164 | https://blog.dashlane.com/how-is-data-stored-in-v8-js-engine-memory/ 165 | https://velog.io/@wongue_shin/JS%EC%9D%98-%EA%B0%9D%EC%B2%B4%EB%8A%94-hash-table%EC%9D%B4-%EC%95%84%EB%8B%99%EB%8B%88%EB%8B%A4 166 | 167 | ## 함수 호이스팅 168 | 169 | 함수 선언문의 경우 런타임 이전 엔진에 의해 먼저 실행되어 함수 객체가 생성되기 때문에 호이스팅이 일어나게 된다. 170 | 171 | 하지만, 표현식의 경우 런타임에 평가되기 때문에 호이스팅이 일어나지 않는다. 172 | 173 | 🧐🧐🧐 174 | >함수 호이스팅은 함수를 호출하기 전에 반드시 함수를 선언해야 한다는 당연한 규칙을 무시한다. 이 같은 문제 때문에 의 저자이자 JSON을 창안하 더글라스 크락포트는 함수 선언문 대신 함수 표현식을 사용할 것을 권장한다. 175 | 176 | -> 이런 관점을 생각해보지 못 했던 것 같다. 177 | 178 | 179 | ```js 180 | // 함수 참조 181 | console.dir(add); // ƒ add(x, y) 182 | console.dir(sub); // undefined 183 | 184 | // 함수 호출 185 | console.log(add(2, 5)); // 7 186 | console.log(sub(2, 5)); // TypeError: sub is not a function 187 | 188 | // 함수 선언문 189 | function add(x, y) { 190 | return x + y; 191 | } 192 | 193 | // 함수 표현식 194 | var sub = function (x, y) { 195 | return x - y; 196 | }; 197 | ``` 198 | 199 | https://dev.to/lydiahallie/javascript-visualized-hoisting-478h 200 | 201 | 202 | ## 참조에 의한 전달과 외부 상태의 변경 203 | 204 | 함숨의 매개변수로 들어온 값 또한 값에 의한 전달, 참조에 의한 전달을 그대로 따른다. 205 | 206 | 207 | ```js 208 | // 매개변수 primitive는 원시값을 전달받고, 매개변수 obj는 객체를 전달받는다. 209 | function changeVal(primitive, obj) { 210 | primitive += 100; 211 | obj.name = 'Kim'; 212 | } 213 | 214 | // 외부 상태 215 | var num = 100; 216 | var person = { name: 'Lee' }; 217 | 218 | console.log(num); // 100 219 | console.log(person); // {name: "Lee"} 220 | 221 | // 원시값은 값 자체가 복사되어 전달되고 객체는 참조값이 복사되어 전달된다. 222 | changeVal(num, person); 223 | 224 | // 원시값은 원본이 훼손되지 않는다. 225 | console.log(num); // 100 226 | 227 | // 객체는 원본이 훼손된다. 228 | console.log(person); // {name: "Kim"} 229 | ``` 230 | 231 | 232 | ## 콜백 함수 233 | 234 | 함수의 매개변수를 통해 다른 함수의 내부로 전달되는 함수를 콜백 함수라고 하며, 매개변수를 통해 함수의 외부에서 콜백 함수를 전달받은 함수를 고차 함수라고 한다. 235 | 236 | ```js 237 | // 외부에서 전달받은 f를 n만큼 반복 호출한다 238 | function repeat(n, f) { 239 | for (var i = 0; i < n; i++) { 240 | f(i); // i를 전달하면서 f를 호출 241 | } 242 | } 243 | 244 | var logAll = function (i) { 245 | console.log(i); 246 | }; 247 | 248 | // 반복 호출할 함수를 인수로 전달한다. 249 | repeat(5, logAll); // 0 1 2 3 4 250 | 251 | var logOdds = function (i) { 252 | if (i % 2) console.log(i); 253 | }; 254 | 255 | // 반복 호출할 함수를 인수로 전달한다. 256 | repeat(5, logOdds); // 1 3 257 | ``` 258 | 259 | ## 스코프 체인 260 | 261 | 변수를 참조할 때 자바스크립트 엔진은 스코프 체인을 통해 변수를 참조하는 코드의 스코프에서 시작하여 상위 스코프 방향으로 이동하며 선언된 변수를 검색한다. 262 | 263 | ## 렉시컬 스코프 264 | 265 | 자바스크립트는 함수를 어디서 정의했는지에 따라 함수의 상위 스코프를 결정하며, 이를 렉시컬 스코프라 부른다. 266 | 267 | ```js 268 | var x = 1; 269 | 270 | function foo() { 271 | var x = 10; 272 | bar(); 273 | } 274 | 275 | function bar() { 276 | console.log(x); 277 | } 278 | 279 | foo(); // 1 280 | bar(); // 1 281 | ``` 282 | 283 | 284 | 🧐🧐🧐 285 | _그렇다면, `foo()` 안에서 `bar()` 가 정의된다면?_ 286 | -> 287 | ```js 288 | var x = 1; 289 | 290 | function foo() { 291 | var x = 10; 292 | 293 | function bar() { 294 | console.log(x); 295 | } 296 | 297 | bar(); 298 | } 299 | 300 | 301 | foo(); // 10 302 | 303 | ``` 304 | 305 | `bar()` 가 정의된 스코프는 `foo()` 함수 안에서의 스코프이기 때문에 10이 된다. 306 | 307 | 308 | ## 일시적 사각지대 TDZ 309 | `let, const` 의 경우 `var` 와 다르게 변수의 초기화 전까지 변수를 참조할 수 없어 에러가 나게된다. 이러한 초기화 되기전까지 변수를 참조 할 수 없는 구간을 일시적 사각지대 (TDZ: Temporarl Dead Zone) 이라고 부른다. 310 | 311 | ```js 312 | // 런타임 이전에 선언 단계가 실행된다. 아직 변수가 초기화되지 않았다. 313 | // 초기화 이전의 일시적 사각 지대에서는 변수를 참조할 수 없다. 314 | console.log(foo); // ReferenceError: foo is not defined 315 | 316 | let foo; // 변수 선언문에서 초기화 단계가 실행된다. 317 | console.log(foo); // undefined 318 | 319 | foo = 1; // 할당문에서 할당 단계가 실행된다. 320 | console.log(foo); // 1 321 | ``` 322 | 323 | 324 | -------------------------------------------------------------------------------- /2회차/이상조.md: -------------------------------------------------------------------------------- 1 | 모던 자바스크립트 Deep Dive 스터디 2 | 3 | # 10. 객체 리터럴 4 | 5 | ## 10.1 객체란? 6 | 7 | > 💡 다양한 타입의 값을 하나의 단위로 구성한 복합적인 자료구조 8 | 0개 이상의 프로퍼티로 구성된 집합이며, 키와 값으로 구성된다. 9 | 10 | 자바스크립트를 구성하는 **거의 모든 것은 객체** 11 | **원시 값은 변경 불가능한 값**이지만 **객체는 변경 가능한 값**이다. 12 | 13 | 객체는 프로퍼티와 메서드로 구성된 집합체다. 14 | 15 | - **프로퍼티** : 객체의 상태를 나타내는 값 16 | - `key`와 `value`로 구성 17 | - **메서드** : 프로퍼티를 참조하고 조작할 수 있는 동작 18 | - 프로퍼티 값이 **함수**일 경우, 일반 함수와 구분하기 위해 메서드라 부른다. 19 | 20 | 21 | ## 10.2 객체 리터럴에 의한 객체 생성 22 | > 💡 객체 생성 방법 중에서 가장 일반적이고 간단한 방법 23 | 자바스크립트의 유연함과 강력함을 대표하는 객체 생성 방식 24 | _*리터럴 : 사람이 이해할 수 있는 문자 또는 약속된 기호를 통해 값을 생성하는 표기법_ 25 | 26 | 자바스크립트는 프로토타입 기반 객체지향 언어로서 클래스 기반 객체지향 언어와는 달리 다양한 객체 생성 방법을 지원한다. 27 | - 객체 리터럴 28 | - Object 생성자 함수 29 | - 생성자 함수 30 | - Object.create 메서드 31 | - 클래스(ES6) 32 | 33 | 객체 리터럴은 중괄호(`{}`) 내에 0개 이상의 프로퍼티를 정의한다. 34 | 변수에 할당되는 시점에 자바스크립트 엔진은 객체 리터럴을 해석해 객체를 생성한다. 35 | 중괄호 내에 프로퍼티를 정의하지 않으면 빈 객체가 생성된다. 36 | 37 | 38 | ## 10.3 프로퍼티 39 | > 💡 객체는 프로퍼티의 집합, 프로퍼티는 키와 값으로 구성된다. 40 | 41 | ```js 42 | const obj = { 43 | firstName: 'Sangjo', 44 | 'last-name': 'Lee', // 따옴표를 사용하지 않을 경우, - 연산자가 있는 표현식으로 해석한다. 45 | age: 20 46 | } 47 | ``` 48 | 49 | **프로퍼티 키**는 프로퍼티 값에 접근할 수 있는 이름으로, 식별자 역할을 한다. 50 | 하지만 **반드시 식별자 네이밍 규칙을 따라야 하는 것은 아니다. 하지만 따르지 않을 경우 반드시 따옴표를 사용해야 한다.** 51 | 52 | 53 | 대괄호를 사용하여 프로퍼티 키를 동적으로 생성할 수도 있다. 54 | 55 | ```js 56 | const key = 'name'; 57 | 58 | // es5 59 | const obj = {}; 60 | obj[key] = 'sangjo'; 61 | 62 | //es6 63 | const obj = { [key]: 'sangjo' }; 64 | ``` 65 | 66 | 프로퍼티 키에 문자열, 심벌 값 외의 값을 사용하면 **암묵적 타입 변환을 통해 문자열이 된다.** 67 | 프로퍼티 키로 숫자 리터럴을 사용하면 내부적으로 문자열로 변환된다. 68 | `var` `function`과 같은 예약어를 프로퍼티 키로 사용해도 에러가 발생하지 않는다. 69 | 70 | 이미 존재하는 프로퍼티 키를 **중복 선언**하면 **나중에 선언한 프로퍼티로 덮어씌워진다.** 71 | 이때, **에러가 발생하지 않는다**는 점에 주의하자. 72 | 73 | ## 10.4 메서드 74 | > 💡 프로퍼티 값이 **함수**일 경우, 일반 함수와 구분하기 위해 메서드라 부른다. 75 | 즉, 메서드는 객체에 묶여 있는 함수를 의미한다. 76 | 77 | 자바스크립트에서 사용할 수 있는 모든 값은 프로퍼티 값으로 사용할 수 있다. 78 | 함수는 일급 객체이므로 값으로 취급할 수 있다. 79 | 80 | ## 10.5 프로퍼티 접근 81 | 82 | > 💡 **마침표 표기법**과 **대괄포 표기법**을 통해 프로퍼티에 접근할 수 있다. 83 | 84 | 대괄호 표기법을 사용할 경우, **대괄호 내부의 문자열을 반드시 따옴표로 감싸야 한다.** 85 | 따옴표로 감싸지 않을 경우, 이를 식별자로 해석한다. 86 | 87 | ```js 88 | const obj = { 89 | name: 'Lee'; 90 | } 91 | 92 | console.log(obj.name) // 'Lee' 93 | console.log(obj['name']) // 'Lee' 94 | console.log(obj[name]) // ReferenceError: 식별자로 해석, 선언되지 않은 식별자일 경우 95 | console.log(obj.age) // undefined: 객체에 존재하지 않는 프로퍼티에 접근한 경우 96 | ``` 97 | > 💡 브라우저 환경에서 name은 창(window)의 이름을 가리키는 전역 변수이므로 아래의 경우, 브라우저 환경과 Node.js 환경에서 다르게 평가된다. 98 | ```js 99 | const obj = { 100 | 'firse-name': 'Lee'; 101 | } 102 | console.log(obj.first-name) // Node.js환경: ReferenceError 103 | // 브라우저 환경: NaN 104 | ``` 105 | 106 | 107 | ## 10.6 프로퍼티 값 갱신 108 | > 💡 이미 존재하는 프로퍼티에 값을 할당하면 프로퍼티 값이 갱신된다. 109 | 110 | ## 10.7 프로퍼티 동적 생성 111 | > 💡 존재하지 않는 프로퍼티에 값을 할당하면 프로퍼티가 동적으로 생성되어 추가되고 프로퍼티 값이 할당된다. 112 | 113 | ## 10.8 프로퍼티 삭제 114 | > 💡 `delete` 연산자를 통해 프로퍼티를 삭제할 수 있다. 115 | 116 | `delete`연산자의 피연산자는 프로퍼티 값에 접근할 수 있는 표현식이어야 한다. 117 | 존재하지 않는 프로퍼티를 삭제하면 아무 에러 없이 무시된다. 118 | 119 | 120 | ## 10.9 ES6에서 추가된 객체 리터럴의 확장 기능 121 | 122 | ### 10.9.1 프로퍼티 축약 표현 123 | > 💡 프로퍼티 값을 식별자 표현식으로 사용할 수 있다. 124 | ```js 125 | const x = 1 126 | const obj = { 127 | x: x 128 | } 129 | console.log(obj) //{ x: 1 } 130 | ``` 131 | 132 | > 💡 위와 같이, 프로퍼티 값으로 변수를 사용하는 경우 변수 이름과 프로퍼티 키가 동일한 이름이라면 프로퍼티 키를 생략할 수 있다. 이때 프로퍼티 키는 변수 이름으로 자동 생성된다. 133 | ```js 134 | const x = 1 135 | const obj = {x} 136 | console.log(obj) //{ x: 1 } 137 | ``` 138 | 139 | ### 10.9.2 계산된 프로퍼티 이름 140 | 141 | > 💡 '문자열 또는 문자열로 타입 변환할 수 있는 값으로 평가되는 표현식'을 대괄호로 묶어 프로퍼티 키를 동적으로 생성하는 것. 142 | 143 | ```js 144 | const prefix = 'prop'; 145 | let i = 0; 146 | 147 | const obj = { 148 | [`${prefix}-${++i}`]: i, 149 | [`${prefix}-${++i}`]: i, 150 | [`${prefix}-${++i}`]: i 151 | } 152 | 153 | console.log(obj) //{prop-1: 1, prop-2: 2, prop-3: 3} 154 | ``` 155 | 156 | ### 10.9.3 메서드 축약 표현 157 | > 💡 메서드를 정의할 때 `function` 키워드를 생략할 수 있다. 158 | 159 | ```js 160 | const obj = { 161 | 162 | sayHi: function(){ 163 | console.log('Hi'); 164 | } 165 | 166 | sayHi(){ 167 | console.log('Hi'); 168 | } 169 | 170 | } 171 | ``` 172 | 173 | 174 | 모던 자바스크립트 Deep Dive 스터디 175 | 176 | # 11. 원시 값과 객체의 비교 177 | 178 | 자바스크립트의 데이터 타입은 원시 타입과 객체 타입으로 구분할 수 있다. 179 | - 원시 값은 **변경 불가능한 값**이다. 반면 객체는 **변경 가능한 값**이다. 180 | - 원시 값을 변수에 할당하면 변수에는 **실제 값**이 저장된다. 반면 객체를 변수에 할당하면 **참조 값**이 저장된다. 181 | - 원시 값을 갖는 변수를 다른 변수에 할당하면 **원시 값이 복사되어 전달**된다. 반면 객체를 가리키는 변수를 다른 변수에 할당하면 **참조 값이 복사되어 전달**된다. 182 | 183 | ## 11.1 원시 값 184 | 185 | ### 11.1.1 변경 불가능한 값 186 | 187 | > 💡 한번 생성된 원시 값은 읽기 전용 값으로서 변경할 수 없다. 188 | 189 | **변수**와 **값**은 구분하여 생각해야 한다. 190 | 변수는 **값을 저장하기 위해 확보한 메모리 공간 자체, 또는 그 메모리 공간을 식별하기 위해 붙인 이름**이다. 191 | 따라서 변수는 언제든지 **재할당**을 통해 **메모리 공간의 주소를 변경할 수 있다.** 192 | 변경 불가능하다는 것은 변수가 아니라 값에 대한 진술이다. 193 | 194 | 위의 내용에 따라, **상수와 변경 불가능한 값은 동일하지 않다.** 195 | 상수는 **재할당이 불가능한 변수**일 뿐이기 때문이다. 196 | 197 | 원시 값은 변경 불가능한 값이기에 어떤 일이 있어도 불변한다.(불변성) 이러한 특성은 데이터의 신뢰성을 보장한다. 198 | 199 | 만약 원시 값이 변경 가능한 값이었다면 재할당에서 변수가 가리키던 메모리 주소를 변경하는 것이 아니라 메모리 공간에 있던 원시 값 자체를 변경하면 그만이다. 200 | 하지만 원시 값은 변경 불가능한 값이기에 직접 변경할 수 없고, 결국 새로운 메모리 공간을 확보하고 재할당한 값을 저장한 후, 변수가 참조하던 메모리 공간의 주소를 변경하게 된다. 201 | 불변성을 갖는 원시 값을 할당한 변수는 재할당 이외에 변수 값을 변경할 수 있는 방법이 없다. 202 | 203 | ### 11.1.2 문자열과 불변성 204 | 205 | 자바스크립트는 다른 언어와 다르게, 문자열 타입을 제공한다. 206 | 207 | 원시 값을 저장하려면 타입에 따라 메모리 공간을 다르게 확보해야한다. 208 | 다만, 숫자가 1이나 1000000이나 동일하게 8바이트가 필요한 것과 달리, 문자열은 길이가 길어질 수록 필요한 메모리 공간도 커진다. 209 | 210 | 문자열 역시 불변성을 지닌다. 211 | 따라서 문자열의 일부분을 변경하는 행위는 반영되지 않는다. 212 | 213 | ```js 214 | const str = 'string'; 215 | str[0] = 'S'; 216 | 217 | console.log(str) // 'string' 218 | ``` 219 | 220 | ### 11.1.3 값에 의한 전달 221 | 222 | > 💡 변수에 원시 값을 갖는 변수를 할당하면 할당받는 변수 쪽에는 할당되는 변수의 원시 값이 복사되어 전달된다. 223 | 224 | ```js 225 | let num = 10; 226 | let copy = num; // 새로운 숫자 값 10이 생성되어 copy에 할당. 227 | 228 | num = 100 // copy 변수의 값에는 어떠한 영향도 주지 않는다. 229 | console.log(copy) // 10 230 | ``` 231 | 232 | 변수에 저장되는 것은 값이 아니라 메모리 주소다. 233 | `copy`에 `num`을 할당했으나, 둘은 다른 메모리 주소를 기억하고 있다. 234 | 235 | 236 | ## 11.2 객체 237 | > 💡 객체는 원시 값과 같이 확보해야 할 메모리 공간의 크기를 사전에 정해둘 수 없다. 238 | 239 | 240 | ### 11.2.1 변경 가능한 값 241 | > 💡 객체는 변경 가능한 값이다. 242 | 243 | ```js 244 | const obj = { 245 | firstName: 'Sangjo' 246 | } 247 | ``` 248 | 249 | 위와 같이 변수에 객체를 할당하면, **변수는 객체가 실제로 저장된 메모리 공간의 주소를 저장한다.** 250 | 이를 `obj` 변수는 객체 `{ firstName: 'Sangjo' }`를 가리키고(참조하고) 있다고 표현한다. 251 | 252 | 객체는 **변경 가능한 값**이라서 원시 값과 달리 **재할당 없이 객체를 직접 변경할 수 있다.** 253 | 재할당 없이 프로퍼티 키를 동적으로 생성할 수도 있고 프로퍼티 값을 갱신할 수도 있으며 프로퍼티 자체를 삭제할 수도 있다. 254 | 255 | 이렇게 동작하는 이유는 **메모리를 효율적으로 사용하기 위함**이다. 256 | 객체를 변경할 때마다 원시 값처럼 새롭게 생성한다면 데이터 신뢰성은 확보될지언정 **복사해서 생성하는 비용**이 많이 든다. 257 | 객체를 복사하지 않고 변경 가능한 값으로 설계하여 메모리 사용의 효율성을 확보했다. 258 | 단, **여러 개의 식별자가 하나의 객체를 공유할 수 있다는 단점**이 있다. 259 | 260 | ### 11.2.2 참조에 의한 전달 261 | 262 | 여러 개의 식별자가 하나의 객체를 공유하는 경우를 생각해보자. 263 | 원본 `obj` 변수를 `copy` 변수에 할당할 경우, **원본의 참조 값이 복사되어 전달**된다.(같은 메모리 공간의 주소를 가진다.) 264 | 이 경우, 한쪽에서 객체를 변경할 경우 다른 한쪽도 영향을 받게 된다. 265 | 266 | 이를 **참조에 의한 전달**이라 한다. 267 | 하지만, 결국 **값에 의한 전달**과 동일하게 식별자가 기억하는 **메모리 공간에 저장되어 있는 값을 복사해서 전달**하고 있다. 268 | 그게 원시 값이냐, 참조 값이냐의 차이만 있을 뿐이다. 269 | 따라서 **자바스크립트에는 값에 의한 전달만이 존재한다**고 할 수 있다. 270 | 271 | 272 | 273 | 274 | 모던 자바스크립트 Deep Dive 스터디 275 | 276 | # 12. 함수 277 | 278 | ## 12.1 함수란? 279 | 280 | > 💡 일련의 과정을 문으로 구현하고 코드 블록으로 감싸서 하나의 실행 단위로 정의한 것 281 | 282 | ![](https://velog.velcdn.com/images/sjoleee_/post/b1dbdbff-0816-4af3-b4ba-e7d9eeb3c2d7/image.jpeg) 283 | 284 | 함수는 재료를 투입받아 제품을 생산하는 기계와 같다. 285 | 함수 내부로 입력을 전달받는 변수를 **매개변수**, 입력을 **인수**, 출력을 **반환값**이라 한다. 286 | 287 | 자바스크립트를 구성하는 **거의 모든 것은 객체** 288 | **원시 값은 변경 불가능한 값**이지만 **객체는 변경 가능한 값**이다. 289 | 290 | 함수는 **함수 정의**를 통해 생성한다. 그러나 정의만으로 실행되는 것은 아니다. 291 | 인수를 매개변수를 통해 함수에 전달하면서 함수의 실행을 명시적으로 지시해야 한다. 292 | 이를 **함수 호출**이라 한다. 293 | 294 | 객체는 프로퍼티와 메서드로 구성된 집합체다. 295 | 296 | - **프로퍼티** : 객체의 상태를 나타내는 값 297 | - `key`와 `value`로 구성 298 | - **메서드** : 프로퍼티를 참조하고 조작할 수 있는 동작 299 | - 프로퍼티 값이 **함수**일 경우, 일반 함수와 구분하기 위해 메서드라 부른다. 300 | 301 | 302 | ## 12.2 함수를 사용하는 이유 303 | > 💡 코드의 중복을 억제하고 재사용성을 높여 유지보수에 용이하다. 또한 실수를 줄여 코드의 신뢰성을 높이는 효과가 있다. 304 | 함수는 식별자를 붙일 수 있기에 적절한 함수 이름을 통해 코드의 가독성을 향상시킨다. 305 | 306 | 307 | ## 12.3 함수 리터럴 308 | > 💡 함수는 객체 타입의 값이므로 함수 리터럴로 생성할 수 있다. 309 | 함수 리터럴은 `function`키워드, 함수 이름, 매개변수 목록, 함수 몸체로 구성된다. 310 | _*리터럴 : 사람이 이해할 수 있는 문자 또는 약속된 기호를 통해 값을 생성하는 표기법_ 311 | 312 | ```js 313 | // 변수에 함수 리터럴을 할당 314 | const f = function add(x, y) { 315 | return x + y; 316 | } 317 | ``` 318 | 319 | 함수 리터럴의 구성 요소는 다음과 같다. 320 | 321 | |
구성 요소
|
설명
| 322 | | - | - | 323 | | 함수 이름 | 함수 이름은 식별자다. 따라서 식별자 네이밍 규칙을 준수해야 한다. 324 | | | 함수 이름은 함수 몸체 내에서만 참조할 수 있는 식별자다.| 325 | | | 함수 이름은 생략할 수 있다. 이름이 있는 함수를 기명 함수, 없는 함수를 무명/익명 함수라 한다.| 326 | | 매개변수 목록 | 0개 이상의 매개변수를 소괄호로 감싸고 쉼표로 구분한다.| 327 | | | 각 매개변수에는 지정한 인수가 순서대로 할당된다.| 328 | | | 매개변수는 함수 몸체 내에서 변수와 동일하게 취급된다. 따라서 식별자 네이밍 규칙을 준수해야 한다.| 329 | | 함수 몸체 | 함수가 호출되었을 때 일괄적으로 실행될 문들을 하나의 실행 단위로 정의한 코드 블록이다. | 330 | 331 | 함수는 객체지만 일반 객체와는 다르다. 332 | **일반 객체는 호출할 수 없지만 함수는 호출할 수 있다.** 333 | 그리고 일반 객체에는 없는 함수 객체만의 고유한 프로퍼티를 갖는다. 334 | 335 | ## 12.4 함수 정의 336 | > 💡 함수를 정의하는 방법에는 **함수 선언문, 함수 표현식, Function 생서자 함수, 화살표 함수**가 있다. 337 | 338 | ### 12.4.1 함수 선언문 339 | > 💡 함수 선언문은 함수 이름을 생략할 수 없다. 또한 표현식이 아닌 문이다. 340 | ```js 341 | function add(x, y) { 342 | return x + y; 343 | } 344 | ``` 345 | 346 | 함수 선언문은 함수 리터럴과 형태가 동일하다. 347 | 단, 함수 리터럴은 함수 이름을 생략할 수 있으나 **함수 선언문은 함수 이름을 생략할 수 없다.** 348 | 349 | 함수 선언문은 **표현식이 아닌 문**이다. 표현식이 아닌 문은 변수에 할당할 수 없으므로 **함수 선언문도 변수에 할당할 수 없다.** 350 | 하지만 실제로는 마치 변수에 할당되는 것처럼 보인다. 351 | 352 | ```js 353 | // 변수에 함수 리터럴을 할당 354 | const add = function add(x, y) { 355 | return x + y; 356 | } 357 | 358 | console.log(add(2, 5)) // 7 359 | ``` 360 | 이유는 코드의 문맥에 따라 자바스크립트 엔진은 이를 **함수 리터럴 혹은 함수 선언문으로 중의적 해석**하기 때문이다. 361 | 함수 리터럴은 표현식인 문이고 함수 선언문은 표현식이 아닌 문이다. 362 | 따라서 **값으로 평가되어야 하는 경우라면 함수 리터럴 표현식으로 해석하고, 값으로 평가되지 않는 문맥에서는 함수 선언문으로 해석한다.** 363 | 364 | -------------------------------------------------------------------------------- /3회차/README.md: -------------------------------------------------------------------------------- 1 | # 3회차 2 | 3 | ## 1. 히든 클래스 4 | 5 | - [V8의 히든 클래스 이야기](https://engineering.linecorp.com/ko/blog/v8-hidden-class) 6 | - [자바스크립트에서의 Shape (hidden class)](https://basemenks.tistory.com/m/299) 7 | - [JavaScript engine fundamentals: Shapes and Inline Caches · Mathias Bynens](https://mathiasbynens.be/notes/shapes-ics) 8 | 9 | ## 2. 리액트 컴포넌트 설계 원칙 10 | 11 | - [변경에 유연한 컴포넌트](https://jbee.io/web/components-should-be-flexible/) 12 | - [유용한 리액트 패턴 5가지](https://velog.io/@dnr6054/유용한-리액트-패턴-5가지) 13 | 14 | ## 3. TypeScript Enum 15 | 16 | - [TypeScript enum을 사용하지 않는 게 좋은 이유를 Tree-shaking 관점에서 소개합니다.](https://engineering.linecorp.com/ko/blog/typescript-enum-tree-shaking/) 17 | - [Typescript enum 최적화](https://kimsangyeon-github-io.vercel.app/blog/2022-03-28-typescript-enum) 18 | - [타입스크립트 as const 타입 추론](https://tolluset.gitbook.io/wiki/blogs/as-const) 19 | -------------------------------------------------------------------------------- /3회차/김민지.md: -------------------------------------------------------------------------------- 1 | # 생성자 함수, 함수와 일급 객체 2 | 3 | ## 생성자 함수에 의한 객체 생성 4 | 5 | ### Object 생성자 함수 6 | 7 | `new` 연산자와 함께 `Object` 생성자 함수를 호출하면 빈 객체를 생성하여 반환한다. 빈 객체를 생성한 이후 프로퍼티 또는 메서드를 추가하여 객체를 완성할 수 있다. 8 | 9 | ```js 10 | // 빈 객체의 생성 11 | const person = new Object(); 12 | 13 | // 프로퍼티 추가 14 | person.name = 'Lee'; 15 | person.sayHello = function () { 16 | console.log('Hi! My name is ' + this.name); 17 | }; 18 | 19 | console.log(person); // {name: "Lee", sayHello: ƒ} 20 | person.sayHello(); // Hi! My name is Lee 21 | ``` 22 | 23 | 생성자 함수란 `new` 연산자와 함께 호출하여 객체를 생성하는 함수를 말한다. 생성자 함수에 의해 생성된 객체를 인스턴스라 한다. 24 | 25 | 자바스크립트는 `Object` 생성자 함수 이외에도 `String`, `Number`, `Boolean`, `Function`, `Array`, `Date`, `RegExp`, `Promise` 등의 빌트인 생성자 함수를 제공한다. 26 | 27 | ```js 28 | // String 생성자 함수에 의한 String 객체 생성 29 | const strObj = new String('Lee'); 30 | console.log(typeof strObj); // object 31 | console.log(strObj); // String {"Lee"} 32 | 33 | // Number 생성자 함수에 의한 Number 객체 생성 34 | const numObj = new Number(123); 35 | console.log(typeof numObj); // object 36 | console.log(numObj); // Number {123} 37 | 38 | // Boolean 생성자 함수에 의한 Boolean 객체 생성 39 | const boolObj = new Boolean(true); 40 | console.log(typeof boolObj); // object 41 | console.log(boolObj); // Boolean {true} 42 | 43 | // Function 생성자 함수에 의한 Function 객체(함수) 생성 44 | const func = new Function('x', 'return x * x'); 45 | console.log(typeof func); // function 46 | console.dir(func); // ƒ anonymous(x) 47 | 48 | // Array 생성자 함수에 의한 Array 객체(배열) 생성 49 | const arr = new Array(1, 2, 3); 50 | console.log(typeof arr); // object 51 | console.log(arr); // [1, 2, 3] 52 | 53 | // RegExp 생성자 함수에 의한 RegExp 객체(정규 표현식) 생성 54 | const regExp = new RegExp(/ab+c/i); 55 | console.log(typeof regExp); // object 56 | console.log(regExp); // /ab+c/i 57 | 58 | // Date 생성자 함수에 의한 Date 객체 생성 59 | const date = new Date(); 60 | console.log(typeof date); // object 61 | console.log(date); // Mon May 04 2020 08:36:33 GMT+0900 (대한민국 표준시) 62 | ``` 63 | 64 | ### 생성자 함수 65 | 66 | #### 객체 리터럴에 의한 객체 생성 방식의 문제점 67 | 68 | 객체 리터럴에 의한 객체 생성 방식은 단 하나의 객체만 생성한다. 따라서 동일한 프로퍼티를 갖는 객체를 여러 개 생성해야 하는 경우 매번 같은 프로퍼티를 기술해야 하기 때문에 비효율적이다. 69 | 70 | 객체는 프로퍼티를 통해 객체 고유의 상태를 표현한다. 그리고 메서드를 통해 상태 데이터인 프로퍼티를 참조하고 조작하는 동작을 표현한다. 따라서 프로퍼티는 객체마다 프로퍼티 값이 다를 수 있지만 메서드는 내용이 동일한 경우가 일반적이다. 71 | 72 | 객체 리터럴에 의해 객체를 생성하는 경우 프로퍼티 구조가 동일함에도 불구하고 매번 같은 프로퍼티와 메서드를 기술해야 한다. 73 | 74 | #### 생성자 함수에 의한 객체 생성 방식의 장점 75 | 76 | 생성자 함수에 의한 객체 생성 방식은 마치 객체를 생성하기 위한 템플릿처럼 생성자 함수를 사용하여 프로퍼티 구조가 동일한 객체 여러 개를 간편하게 생성할 수 있다. 77 | 78 | 생성자 함수는 이름 그대로 객체를 생성하는 함수다. 하지만 자바와 같은 클래스 기반 객체지향 언어의 생성자와는 다르게 그 형식이 정해져 있는 것이 아니라 일반 함수와 동일한 방법으로 생성자 함수를 정의하고 **new 연산자와 함께 호출하면 해당 함수는 생성자 함수로 동작한다.** 만약 `new` 연산자와 함께 생성자 함수를 호출하지 않으면 생성자 함수가 아니라 일반 함수로 동작한다. 79 | 80 | ```js 81 | // new 연산자와 함께 호출하지 않으면 생성자 함수로 동작하지 않는다. 82 | // 즉, 일반 함수로서 호출된다. 83 | const circle3 = Circle(15); 84 | 85 | // 일반 함수로서 호출된 Circle은 반환문이 없으므로 암묵적으로 undefined를 반환한다. 86 | console.log(circle3); // undefined 87 | 88 | // 일반 함수로서 호출된 Circle내의 this는 전역 객체를 가리킨다. 89 | console.log(radius); // 15 90 | ``` 91 | 92 | #### 생성자 함수의 인스턴스 생성 과정 93 | 94 | 먼저 생성자 함수의 함수 몸체에서 수행해야 하는 것이 무엇인지 생각해보자. 생성자 함수의 역할은 프로퍼티 구조가 동일한 인스턴스를 생성하기 위한 템플릿으로서 동작하여 **인스턴스를 생성**하는 것과 **생성된 인스턴스를 초기화**하는 것이다. 생성자 함수가 인스턴스를 생성하는 것은 필수이고, 생성된 인스턴스를 초기화하는 것은 옵션이다. 95 | 96 | 자바스크립트 엔진은 암묵적인 처리를 통해 인스턴스를 생성하고 반환한다. `new` 연산자와 함께 생성자 함수를 호출하면 자바스크립트 엔진은 다음과 같은 과정을 거쳐 암묵적으로 인스턴스를 생성하고 인스턴스를 초기화한 후 암묵적으로 인스턴스를 반환한다. 97 | 98 | #### 내부 메서드 `[[Call]]`과 `[[Construct]]` 99 | 100 | 함수 선언문 또는 함수 표현식으로 정의한 함수는 일반적인 함수로서 호출할 수 있는 것은 물론 생성자 함수로서 호출할 수 있다. 생성자 함수로서 호출한다는 것은 `new` 연산자와 함께 호출하여 객체를 생성하는 것을 의미한다. 101 | 102 | 함수는 객체이므로 일반 객체와 동일하게 동작할 수 있다. 함수 객체는 일반 객체가 가지고 있는 내부 슬롯과 내부 메서드를 모두 가지고 있기 때문이다. 103 | 104 | ```js 105 | // 함수는 객체다. 106 | function foo() {} 107 | 108 | // 함수는 객체이므로 프로퍼티를 소유할 수 있다. 109 | foo.prop = 10; 110 | 111 | // 함수는 객체이므로 메서드를 소유할 수 있다. 112 | foo.method = function () { 113 | console.log(this.prop); 114 | }; 115 | 116 | foo.method(); // 10 117 | ``` 118 | 119 | 함수는 객체이지만 일반 객체와는 다르다. **일반 객체는 호출할 수 없지만 함수는 호출할 수 있다.** 따라서 함수 객체는 일반 객체가 가지고 있는 내부 슬롯과 내부 메서드는 물론, 함수로서 동작하기 위해 함수 객체만을 위한 `[[Environment]]`, `[[FormalParameters]]` 등의 내부 슬롯과 `[[Call]]`, `[[Construct]]` 같은 내부 메서드를 추가로 가지고 있다. 120 | 121 | 함수가 일반 함수로서 호출되면 함수 객체의 내부 메서드 `[[Call]]`이 호출되고 `new` 연산자와 함께 생성자 함수로서 호출되면 내부 메서드 `[[Construct]]`가 호출된다. 122 | 123 | ```js 124 | function foo() {} 125 | 126 | // 일반적인 함수로서 호출: [[Call]]이 호출된다. 127 | foo(); 128 | 129 | // 생성자 함수로서 호출: [[Construct]]가 호출된다. 130 | new foo(); 131 | ``` 132 | 133 | 내부 메서드 `[[Call]]`을 갖는 함수 객체를 `callable`이라 하며, 내부 메서드 `[[Construct]]`를 갖는 함수 객체를 `constructor`, `[[Construct]]`를 갖지 않는 함수 객체를 `non-constructor`라고 부른다. `callable`은 호출할 수 있는 객체, 즉 함수를 말하며, `constructor`는 생성자 함수로서 호출할 수 있는 함수, `non-constructor`는 객체를 생성자 함수로서 호출할 수 없는 함수를 의미한다. 134 | 135 | 호출할 수 없는 객체는 함수 객체가 아니므로 함수로서 기능하는 객체, 즉 함수 객체는 반드시 `callable`이어야 한다. 따라서 모든 함수 객체는 내부 메서드를 `[[Call]]`을 갖고 있으므로 호출할 수 있다. 하지만 모든 함수 객체가 `[[Construct]]`를 갖는 것은 아니다. 다시 말해, 함수 객체는 `constructor`일 수도 있고 `non-constructor`일 수도 있다. 136 | 137 | 결론적으로 함수 객체는 `callable`이면서 `constructor`이거나 `callable`이면서 `non-constructor`다. 즉, 모든 함수 객체는 호출할 수 있지만 모든 함수 객체를 생성자 함수로서 호출할 수 있는 것은 아니다. 138 | 139 | #### constructor와 non-constructor 구분 140 | 141 | - `constructor`: 함수 선언문, 함수 표현식, 클래스 142 | - `non-constructor`: 메서드, 화살표 함수 143 | 144 | ```js 145 | // 일반 함수 정의: 함수 선언문, 함수 표현식 146 | function foo() {} 147 | const bar = function () {}; 148 | // 프로퍼티 x의 값으로 할당된 것은 일반 함수로 정의된 함수다. 이는 메서드로 인정하지 않는다. 149 | const baz = { 150 | x: function () {}, 151 | }; 152 | 153 | // 일반 함수로 정의된 함수만이 constructor이다. 154 | new foo(); // -> foo {} 155 | new bar(); // -> bar {} 156 | new baz.x(); // -> x {} 157 | 158 | // 화살표 함수 정의 159 | const arrow = () => {}; 160 | 161 | new arrow(); // TypeError: arrow is not a constructor 162 | 163 | // 메서드 정의: ES6의 메서드 축약 표현만을 메서드로 인정한다. 164 | const obj = { 165 | x() {}, 166 | }; 167 | 168 | new obj.x(); // TypeError: obj.x is not a constructor 169 | ``` 170 | 171 | ## 함수와 일급 객체 172 | 173 | ### 일급 객체 174 | 175 | 다음과 같은 조건을 만족하는 객체를 **일급 객체**라 한다. 176 | 177 | 1. 무명의 리터럴로 생성할 수 있다. 즉, 런타임에 생성이 가능하다. 178 | 2. 변수나 자료구조(객체, 배열 등)에 저장할 수 있다. 179 | 3. 함수의 매개변수에 전달할 수 있다. 180 | 4. 함수의 반환값으로 사용할 수 있다. 181 | 182 | 자바스크립트의 함수는 다음 예제와 같이 위의 조건을 모두 만족하므로 일급 객체다. 183 | 184 | ```js 185 | // 1. 함수는 무명의 리터럴로 생성할 수 있다. 186 | // 2. 함수는 변수에 저장할 수 있다. 187 | // 런타임(할당 단계)에 함수 리터럴이 평가되어 함수 객체가 생성되고 변수에 할당된다. 188 | const increase = function (num) { 189 | return ++num; 190 | }; 191 | 192 | const decrease = function (num) { 193 | return --num; 194 | }; 195 | 196 | // 2. 함수는 객체에 저장할 수 있다. 197 | const auxs = { increase, decrease }; 198 | 199 | // 3. 함수의 매개변수에게 전달할 수 있다. 200 | // 4. 함수의 반환값으로 사용할 수 있다. 201 | function makeCounter(aux) { 202 | let num = 0; 203 | 204 | return function () { 205 | num = aux(num); 206 | return num; 207 | }; 208 | } 209 | 210 | // 3. 함수는 매개변수에게 함수를 전달할 수 있다. 211 | const increaser = makeCounter(auxs.increase); 212 | console.log(increaser()); // 1 213 | console.log(increaser()); // 2 214 | 215 | // 3. 함수는 매개변수에게 함수를 전달할 수 있다. 216 | const decreaser = makeCounter(auxs.decrease); 217 | console.log(decreaser()); // -1 218 | console.log(decreaser()); // -2 219 | ``` 220 | 221 | 함수가 일급 객체라는 것은 함수를 객체와 동일하게 사용할 수 있다는 의미다. 객체는 값이므로 함수는 값과 동일하게 취급할 수 있다. 따라서 함수는 값을 사용할 수 있는 곳(변수 할당문, 객체의 프로퍼티 값, 배열의 요소, 함수 호출의 인수, 함수 반환문)이라면 어디서든지 리터럴로 정의할 수 있으며 런타임에 함수 객체로 평가된다. 222 | 223 | 일급 객체로서 함수가 가지는 가장 큰 특징은 일반 객체와 같이 함수의 매개변수에 전달할 수 있으며, 함수의 반환값으로 사용할 수도 있다는 것이다. 이는 함수형 프로그래밍을 가능케 하는 자바스크립트의 장점 중 하나다. 224 | 225 | 함수는 객체이지만 일반 객체와는 차이가 있다. 일반 객체는 호출할 수 없지만 함수 객체는 호출할 수 있다. 그리고 함수 객체는 일반 객체에는 없는 함수 고유의 프로퍼티를 소유한다. 226 | 227 | ### 함수 객체의 프로퍼티 228 | 229 | 함수는 객체다. 따라서 함수도 프로퍼티를 가질 수 있다. 230 | 231 | `arguments`, `caller`, `length`, `name`, `prototype` 프로퍼티는 모두 함수 객체의 데이터 프로퍼티다. 이들 프로퍼티는 일반 객체에는 없는 함수 객체 고유의 프로퍼티다. 하지만 `__proto__`는 접근자 프로퍼티이며, 함수 객체 고유의 프로퍼티가 아니라 `Object.prototype` 객체의 프로퍼티를 상속받은 것을 알 수 있다. `Object.prototype` 객체의 프로퍼티는 모든 객체가 상속받아 사용할 수 있다. 즉, `Object.prototype` 객체의 `__proto__` 접근자 프로퍼티는 모든 객체가 사용할 수 있다. 232 | 233 | ## 퀴즈 234 | 235 | ### 생성자 함수 236 | 237 | Q.1 자바스크립트에서 객체를 생성하는 방법은? 238 | 239 |
240 | 241 | 정답 242 | 243 | 자바스크립트에서는 리터럴 객체로 객체를 생성할 수도 있고, 생성자 함수로 객체를 생성할 수도 있다. 244 | 245 |
246 | 247 |
248 | 249 | Q.2 생성자 함수 마지막에 `return 1`이라는 문장을 넣고 다시 객체를 생성했을 때 출력값은? `1`이 리턴되는지? 아니라면 그 이유는 무엇인지? 250 | 251 |
252 | 253 | 정답 254 | 255 | 생성자 함수의 목적은 객체를 생성하는 것이다. 리턴문이 존재하지 않는다면 `this`가 리턴되고 만일 `return`문이 객체를 리턴한다면 해당 객체가 반환된다. 하지만 `1`과 같이 객체가 아닌 타입이 리턴된다면 `return`이 없는 것과 마찬가지로 `this`가 리턴된다. 256 | 257 |
258 | -------------------------------------------------------------------------------- /3회차/김효진.md: -------------------------------------------------------------------------------- 1 | # 16. 프로퍼티 어트리뷰트 2 | 3 | ## 16.1 내부 슬롯과 내부 메서드 4 | 모든 객체는 `[[Prototype]]` 이라는 내부 슬롯을 갖는다. `__proto__` 를 통해 간접적으로 접근할 수 있다. 5 | 6 | ## 16.2 프로퍼티 어트리뷰트와 프로퍼티 디스크립터 객체 7 | 자바스크립트 엔진은 프로퍼티를 생성할 때 프로퍼티의 상태를 나타내는 프로퍼티 어트리뷰트를 기본값으로 자동 정의한다. 8 | 프로퍼티 상태 9 | - `[[Value]]` 프로퍼티의 값 10 | - `[[Writable]]` 값의 갱신 가능 여부 11 | - `[[Enumerable]]` 열거 가능 여부 12 | - `[[Configurable]]` 재정의 가능 여부 13 | 14 | # 17. 생성자 함수에 의한 객체 생성 15 | 16 | this는 객체 자신의 프로퍼티나 메서드를 참조하기 위한 자기 참조 변수다. this가 가리키는 값, 즉 this 바인딩은 함수 호출 방식에 따라 동적으로 결정된다. 17 | 18 | - 일반 호출 방식 : 전역 객체 19 | - 메서드로서 호출 : 메서드를 호출한 객체 (마침표 앞의 객체) 20 | - 생성자 함수로서 호출 : 생성자 함수가 (미래에) 생성할 인스턴스 21 | 22 | 바인딩이란, 식별자와 값을 연결하는 과정을 말한다. 예를 들어, 변수 선언은 변수 이름(식별자)과 확보된 메모리 공간의 주소를 바인딩하는 것이다. this 바인딩은 this(키워드로 분류되지만 식별자 역할을 한다)와 this가 가리킬 객체를 바인딩하는 것이다. 23 | 24 | 25 | # 18. 함수와 일급 객체 26 | 27 | 일급 객체 28 | - 무명의 리터럴로 생성할 수 있다. 즉, 런타임에 생성이 가능하다. 29 | - 변수나 자료구조(객체, 배열 등)에 저장할 수 있다. 30 | - 함수의 매개변수에 전달할 수 있다. 31 | - 함수의 반환값으로 사용할 수 있다. 32 | 33 | ## 히든 클래스 34 | 35 | https://engineering.linecorp.com/ko/blog/v8-hidden-class/ 36 | 37 | V8은 히든 클래스를 이용하여 동적 탐색(Dynamic Lookup)을 회피하고 있습니다. 한마디로 말하자면, 프로퍼티가 바뀔 때 각각 그 프로퍼티의 오프셋을 업데이트한 뒤 그 값을 가지고 있는 방식입니다. 38 | 39 | 히든 클래스에는 다음과 같은 특징이 있습니다. 40 | 41 | - 객체는 반드시 하나의 히든 클래스를 참조한다. 42 | - 히든 클래스는 각 프로퍼티에 대해 메모리 오프셋을 가지고 있다. 43 | - 동적으로 새로운 프로퍼티가 만들어질 때, 혹은 기존 프로퍼티가 삭제되거나 기존 프로퍼티의 데이터 타입이 바뀔 때는 신규 히든 클래스가 생성되며, 신규 히든 클래스는 기존 프로퍼티에 대한 정보를 유지하면서 추가적으로 새 프로퍼티의 오프셋을 가지게 된다. 44 | - 히든 클래스는 프로퍼티에 대해 변경이 발생했을 때 참조해야 하는 히든 클래스에 대한 정보를 갖는다. 45 | - 객체에 새로운 프로퍼티가 만들어지면, 현재 참조하고 있는 히든 클래스의 전환 정보를 확인한 후, 현재 프로퍼티에 대한 변경이 전환 정보의 조건과 일치하면, 객체의 참조 히든 클래스를 조건에 명시된 히든 클래스로 변경시킨다. -------------------------------------------------------------------------------- /3회차/이병현.md: -------------------------------------------------------------------------------- 1 | ## 내부 슬롯과 내부 메서드 2 | 3 | ECMAScript 사양에서 사용하는 의사 프로퍼티, 의사 메서드를 내부 슬롯, 내부 메서드라 부른다. 4 | 개발자가 직접 접근할 수 있듀륙 외부로 공개된 객체의 프로퍼티는 아니다. 하지만, 일부는 접근할 수 있는 수단을 제공하는데, `[[Prototype]]` 이라는 내부 슬롯의 경우 `__proto__` 를 통해 접근할 수 있다. (던더 프로토라 부른다) 5 | 6 | ```js 7 | const o = {}; 8 | 9 | // 내부 슬롯은 자바스크립트 엔진의 내부 로직이므로 직접 접근할 수 없다. 10 | o.[[Prototype]] // -> Uncaught SyntaxError: Unexpected token '[' 11 | // 단, 일부 내부 슬롯과 내부 메서드에 한하여 간접적으로 접근할 수 있는 수단을 제공하기는 한다. 12 | o.__proto__ // -> Object.prototype 13 | ``` 14 | 15 | 16 | --- 17 | 18 | ## 데이터 프로퍼티와 접근자 프로퍼티 19 | 20 | 자바스크립트 엔진은 프로퍼티를 생성할 때 프로퍼티의 상태를 나타내는 프로퍼티 어트리뷰트를 추가한다. 21 | 22 | ### 데이터 프로퍼티 23 | 24 | 프로퍼티 어트리뷰트는 자바스크립트 엔진이 관리하는 내부 상태 값인 내부 슬롯이다. 25 | 26 | ```js 27 | const person = { 28 | name: 'Lee' 29 | }; 30 | 31 | // 프로퍼티 동적 생성 32 | person.age = 20; 33 | 34 | // 모든 프로퍼티의 프로퍼티 어트리뷰트 정보를 제공하는 프로퍼티 디스크립터 객체들을 반환한다. 35 | console.log(Object.getOwnPropertyDescriptors(person)); 36 | /* 37 | { 38 | name: {value: "Lee", writable: true, enumerable: true, configurable: true}, 39 | age: {value: 20, writable: true, enumerable: true, configurable: true} 40 | } 41 | */ 42 | ``` 43 | 44 | `[[Value]]` 45 | 46 | 프로퍼티 키를 통해 프로퍼티 값에 전근하면 반환되는 값 47 | 48 | `[[Writable]]` 49 | 50 | 프로퍼티 값의 변경 가능 여부를 나타내며 불리언 값을 가짐 51 | 52 | `[[Enumerable]]` 53 | 54 | 프로퍼티의 열거 가능 여부를 나타내며 불리언 값을 가짐 55 | 56 | `[[Configurable]]` 57 | 58 | 프로퍼티의 재정의 기능 여부를 나타내며 불리언 값을 가짐 59 | 60 | ### 접근자 프로퍼티 61 | 62 | `[[Get]]` 63 | 64 | 접근자 프로퍼티를 통해 데이터 프로퍼티의 값을 읽을 때 호출되는 접근자 함수 getter라 생각하면 된다. 65 | 66 | `[[Set]]` 67 | 68 | 접근자 프로퍼티를 통해 데이터 프로퍼티의 값을 저장할 때 호출되는 접근자 함수 setter라 생각하면 된다. 69 | 70 | `[[Enumberable]], [[Configurable]]` 는 데이터 프로퍼티와 같다 71 | 72 | ```js 73 | const person = { 74 | // 데이터 프로퍼티 75 | firstName: 'Ungmo', 76 | lastName: 'Lee', 77 | 78 | // fullName은 접근자 함수로 구성된 접근자 프로퍼티다. 79 | // getter 함수 80 | get fullName() { 81 | return `${this.firstName} ${this.lastName}`; 82 | }, 83 | // setter 함수 84 | set fullName(name) { 85 | // 배열 디스트럭처링 할당: "31.1 배열 디스트럭처링 할당" 참고 86 | [this.firstName, this.lastName] = name.split(' '); 87 | } 88 | }; 89 | 90 | // 데이터 프로퍼티를 통한 프로퍼티 값의 참조. 91 | console.log(person.firstName + ' ' + person.lastName); // Ungmo Lee 92 | 93 | // 접근자 프로퍼티를 통한 프로퍼티 값의 저장 94 | // 접근자 프로퍼티 fullName에 값을 저장하면 setter 함수가 호출된다. 95 | person.fullName = 'Heegun Lee'; 96 | console.log(person); // {firstName: "Heegun", lastName: "Lee"} 97 | 98 | // 접근자 프로퍼티를 통한 프로퍼티 값의 참조 99 | // 접근자 프로퍼티 fullName에 접근하면 getter 함수가 호출된다. 100 | console.log(person.fullName); // Heegun Lee 101 | 102 | // firstName은 데이터 프로퍼티다. 103 | // 데이터 프로퍼티는 [[Value]], [[Writable]], [[Enumerable]], [[Configurable]] 프로퍼티 어트리뷰트를 갖는다. 104 | let descriptor = Object.getOwnPropertyDescriptor(person, 'firstName'); 105 | console.log(descriptor); 106 | // {value: "Heegun", writable: true, enumerable: true, configurable: true} 107 | 108 | // fullName은 접근자 프로퍼티다. 109 | // 접근자 프로퍼티는 [[Get]], [[Set]], [[Enumerable]], [[Configurable]] 프로퍼티 어트리뷰트를 갖는다. 110 | descriptor = Object.getOwnPropertyDescriptor(person, 'fullName'); 111 | console.log(descriptor); 112 | // {get: ƒ, set: ƒ, enumerable: true, configurable: true} 113 | ``` 114 | 115 | ## 객체 변경 방지 116 | 117 | - Object.preventExtensions 118 | 119 | 객체의 프로퍼티 추가가 금지됨 120 | 121 | ```js 122 | const person = { name: 'Lee' }; 123 | 124 | // person 객체는 확장이 금지된 객체가 아니다. 125 | console.log(Object.isExtensible(person)); // true 126 | 127 | // person 객체의 확장을 금지하여 프로퍼티 추가를 금지한다. 128 | Object.preventExtensions(person); 129 | 130 | // person 객체는 확장이 금지된 객체다. 131 | console.log(Object.isExtensible(person)); // false 132 | 133 | // 프로퍼티 추가가 금지된다. 134 | person.age = 20; // 무시. strict mode에서는 에러 135 | console.log(person); // {name: "Lee"} 136 | 137 | // 프로퍼티 추가는 금지되지만 삭제는 가능하다. 138 | delete person.name; 139 | console.log(person); // {} 140 | 141 | // 프로퍼티 정의에 의한 프로퍼티 추가도 금지된다. 142 | Object.defineProperty(person, 'age', { value: 20 }); 143 | // TypeError: Cannot define property age, object is not extensible 144 | ``` 145 | 146 | - Object.seal 147 | 148 | 객체를 밀봉함, 밀봉됨 객체는 읽기와 쓰기만 가능 149 | 150 | ```js 151 | const person = { name: 'Lee' }; 152 | 153 | // person 객체는 밀봉(seal)된 객체가 아니다. 154 | console.log(Object.isSealed(person)); // false 155 | 156 | // person 객체를 밀봉(seal)하여 프로퍼티 추가, 삭제, 재정의를 금지한다. 157 | Object.seal(person); 158 | 159 | // person 객체는 밀봉(seal)된 객체다. 160 | console.log(Object.isSealed(person)); // true 161 | 162 | // 밀봉(seal)된 객체는 configurable이 false다. 163 | console.log(Object.getOwnPropertyDescriptors(person)); 164 | /* 165 | { 166 | name: {value: "Lee", writable: true, enumerable: true, configurable: false}, 167 | } 168 | */ 169 | 170 | // 프로퍼티 추가가 금지된다. 171 | person.age = 20; // 무시. strict mode에서는 에러 172 | console.log(person); // {name: "Lee"} 173 | 174 | // 프로퍼티 삭제가 금지된다. 175 | delete person.name; // 무시. strict mode에서는 에러 176 | console.log(person); // {name: "Lee"} 177 | 178 | // 프로퍼티 값 갱신은 가능하다. 179 | person.name = 'Kim'; 180 | console.log(person); // {name: "Kim"} 181 | 182 | // 프로퍼티 어트리뷰트 재정의가 금지된다. 183 | Object.defineProperty(person, 'name', { configurable: true }); 184 | // TypeError: Cannot redefine property: name 185 | ``` 186 | 187 | 188 | - Object.freeze 189 | 190 | 객체를 동결함, 읽기만 가능 191 | 192 | ```js 193 | const person = { name: 'Lee' }; 194 | 195 | // person 객체는 동결(freeze)된 객체가 아니다. 196 | console.log(Object.isFrozen(person)); // false 197 | 198 | // person 객체를 동결(freeze)하여 프로퍼티 추가, 삭제, 재정의, 쓰기를 금지한다. 199 | Object.freeze(person); 200 | 201 | // person 객체는 동결(freeze)된 객체다. 202 | console.log(Object.isFrozen(person)); // true 203 | 204 | // 동결(freeze)된 객체는 writable과 configurable이 false다. 205 | console.log(Object.getOwnPropertyDescriptors(person)); 206 | /* 207 | { 208 | name: {value: "Lee", writable: false, enumerable: true, configurable: false}, 209 | } 210 | */ 211 | 212 | // 프로퍼티 추가가 금지된다. 213 | person.age = 20; // 무시. strict mode에서는 에러 214 | console.log(person); // {name: "Lee"} 215 | 216 | // 프로퍼티 삭제가 금지된다. 217 | delete person.name; // 무시. strict mode에서는 에러 218 | console.log(person); // {name: "Lee"} 219 | 220 | // 프로퍼티 값 갱신이 금지된다. 221 | person.name = 'Kim'; // 무시. strict mode에서는 에러 222 | console.log(person); // {name: "Lee"} 223 | 224 | // 프로퍼티 어트리뷰트 재정의가 금지된다. 225 | Object.defineProperty(person, 'name', { configurable: true }); 226 | // TypeError: Cannot redefine property: name 227 | ``` 228 | 229 | 230 | ## 생성자 함수 231 | 232 | 생성자 함수의 역할은 프로퍼티 구조가 동일한 인스턴스를 생성하기 위한 템플릿으로서 동작하여 인스턴스를 생성하는 것과 생성된 인스턴스를 초기화 하는 것이다. (클래스와 매우 유사) 233 | 234 | ```js 235 | function Circle(radius) { 236 | // 1. 암묵적으로 인스턴스가 생성되고 this에 바인딩된다. 237 | 238 | // 2. this에 바인딩되어 있는 인스턴스를 초기화한다. 239 | this.radius = radius; 240 | this.getDiameter = function () { 241 | return 2 * this.radius; 242 | }; 243 | 244 | // 3. 완성된 인스턴스가 바인딩된 this가 암묵적으로 반환된다 245 | } 246 | 247 | // 인스턴스 생성. Circle 생성자 함수는 암묵적으로 this를 반환한다. 248 | const circle = new Circle(1); 249 | console.log(circle); // Circle {radius: 1, getDiameter: ƒ} 250 | ``` 251 | 252 | ## `[[Call]]`과 `[[Constructor]]` 253 | 254 | 내부 메서드 `[[Call]]` 을 갖는 함수 객체를 `callable`이라 하며, 내부 메서드 `[[Constructor]]`를 갖는 함수 객체를 `constructor`라 부른다. 255 | 256 | ```js 257 | function foo() {} 258 | 259 | // 일반적인 함수로서 호출: [[Call]]이 호출된다. 260 | foo(); 261 | 262 | // 생성자 함수로서 호출: [[Construct]]가 호출된다. 263 | new foo(); 264 | ``` 265 | 266 | ## constructor와 non-constructor의 구분 267 | 268 | - constructor: 함수 선언문, 함수 표현식, 클래스(클래스도 함수다) 269 | - non-constructor: 메서드(ES6 메서드 축약 표현), 화살표 함수 270 | 271 | ```js 272 | // 일반 함수 정의: 함수 선언문, 함수 표현식 273 | function foo() {} 274 | const bar = function () {}; 275 | // 프로퍼티 x의 값으로 할당된 것은 일반 함수로 정의된 함수다. 이는 메서드로 인정하지 않는다. 276 | const baz = { 277 | x: function () {} 278 | }; 279 | 280 | // 일반 함수로 정의된 함수만이 constructor이다. 281 | new foo(); // -> foo {} 282 | new bar(); // -> bar {} 283 | new baz.x(); // -> x {} 284 | 285 | // 화살표 함수 정의 286 | const arrow = () => {}; 287 | 288 | new arrow(); // TypeError: arrow is not a constructor 289 | 290 | // 메서드 정의: ES6의 메서드 축약 표현만을 메서드로 인정한다. 291 | const obj = { 292 | x() {} 293 | }; 294 | 295 | new obj.x(); // TypeError: obj.x is not a constructor 296 | ``` 297 | 298 | ## new.target 299 | 300 | `new`연산자와 함께 생성자 함수로서 호출되면 함수 내부의 `new.target` 은 함수 자신을 가리킨다. `new`연산자 없이 일반 함수로서 호출된 함수 내부의 `new.target`은 `undefined`이다. 301 | 302 | ```js 303 | // 생성자 함수 304 | function Circle(radius) { 305 | // 이 함수가 new 연산자와 함께 호출되지 않았다면 new.target은 undefined다. 306 | if (!new.target) { 307 | // new 연산자와 함께 생성자 함수를 재귀 호출하여 생성된 인스턴스를 반환한다. 308 | return new Circle(radius); 309 | } 310 | 311 | this.radius = radius; 312 | this.getDiameter = function () { 313 | return 2 * this.radius; 314 | }; 315 | } 316 | 317 | // new 연산자 없이 생성자 함수를 호출하여도 new.target을 통해 생성자 함수로서 호출된다. 318 | const circle = Circle(5); 319 | console.log(circle.getDiameter()); 320 | ``` 321 | 322 | `Object, Function` 생성자 함수는 `new` 연산자 없이 호출해도 `new` 연산자와 함께 호출했을 때와 동일하게 동작한다. 323 | 324 | ```js 325 | let obj = new Object(); 326 | console.log(obj); // {} 327 | 328 | obj = Object(); 329 | console.log(obj); // {} 330 | 331 | let f = new Function('x', 'return x ** x'); 332 | console.log(f); // ƒ anonymous(x) { return x ** x } 333 | 334 | f = Function('x', 'return x ** x'); 335 | console.log(f); // ƒ anonymous(x) { return x ** x } 336 | ``` 337 | 338 | 하지만 `String, Number, Boolean` 생성자 함수는 데이터 타입을 변환한다. 339 | 340 | ```js 341 | const str = String(123); 342 | console.log(str, typeof str); // 123 string 343 | 344 | const num = Number('123'); 345 | console.log(num, typeof num); // 123 number 346 | 347 | const bool = Boolean('true'); 348 | console.log(bool, typeof bool); // true boolean 349 | ``` 350 | 351 | ## 일급 객체 352 | 353 | 아래의 조건들을 만족하는 객체를 *일급 객체*라 한다. 함수가 일급 객체라는 것은 함수를 객체와 동일하게 사용할 수 있다는 의미이다. 354 | 355 | ```js 356 | // 1. 함수는 무명의 리터럴로 생성할 수 있다. 357 | // 2. 함수는 변수에 저장할 수 있다. 358 | // 런타임(할당 단계)에 함수 리터럴이 평가되어 함수 객체가 생성되고 변수에 할당된다. 359 | const increase = function (num) { 360 | return ++num; 361 | }; 362 | 363 | const decrease = function (num) { 364 | return --num; 365 | }; 366 | 367 | // 2. 함수는 객체에 저장할 수 있다. 368 | const auxs = { increase, decrease }; 369 | 370 | // 3. 함수의 매개변수에게 전달할 수 있다. 371 | // 4. 함수의 반환값으로 사용할 수 있다. 372 | function makeCounter(aux) { 373 | let num = 0; 374 | 375 | return function () { 376 | num = aux(num); 377 | return num; 378 | }; 379 | } 380 | 381 | // 3. 함수는 매개변수에게 함수를 전달할 수 있다. 382 | const increaser = makeCounter(auxs.increase); 383 | console.log(increaser()); // 1 384 | console.log(increaser()); // 2 385 | 386 | // 3. 함수는 매개변수에게 함수를 전달할 수 있다. 387 | const decreaser = makeCounter(auxs.decrease); 388 | console.log(decreaser()); // -1 389 | console.log(decreaser()); // -2 390 | ``` 391 | -------------------------------------------------------------------------------- /3회차/이상조.md: -------------------------------------------------------------------------------- 1 | 모던 자바스크립트 Deep Dive 스터디 2 | 3 | # 16. 프로퍼티 어트리뷰트 4 | 5 | ## 16.1 내부 슬롯과 내부 메서드 6 | 7 | > 💡 자바스크립트 엔진의 구현 알고리즘을 설명하기 위해 ECMAScript 사양에서 사용하는 의사 프로퍼티와 의사 메서드. 8 | 9 | 실제로 동작하지만, 개발자가 직접 접근할 수 있도록 외부로 공개된 객체의 프로퍼티는 아니다. 10 | 원칙적으로 자바스크립트는 내부 슬롯과 내부 메서드에 직접적으로 접근하거나 호출할 수 있는 방법을 제공하지 않는다. 11 | 단, 일부 내부 슬롯과 내부 메서드에 한하여 간접적으로 접근할 수 있는 수단을 제공하기는 한다. 12 | 13 | 모든 객체는 `[[Prototype]]`이라는 내부 슬롯을 갖는다. 14 | 내부 슬롯은 원칙적으로 직접 접근할 수 없으나, `[[Prototype]]`의 경우, `__proto__`를 통해 간접적으로 접근할 수 있다. 15 | 16 | ```js 17 | const obj = {}; 18 | 19 | obj.[[Prototype]] // Error 20 | obj.__proto__ //Object.prototype 21 | ``` 22 | 23 | ## 16.2 프로퍼티 어트리뷰트와 프로퍼티 디스크립터 객체 24 | 25 | > 💡 프로퍼티 어트리뷰트란, 자바스크립트 엔진이 관리하는 내부 상태 값인 내부 슬롯 `[[Value]]` `[[Writable]]` `[[Enumerable]]` `[[Configurable]]`를 말한다. 26 | 프로퍼티를 생성할 때 이러한 프로퍼티 어트리뷰트를 기본값으로 자동 정의한다. 27 | 28 | 위에서 말했듯, `내부 슬롯`은 직접 접근할 수 없으나 간접적으로 `Object.getOwnPropertyDescriptor`를 사용하여 확인할 수 있다. 29 | 30 | `Object.getOwnPropertyDescriptor`는 두개의 매개변수를 받는다. 31 | `Object.getOwnPropertyDescriptor(객체의 참조, 확인할 프로퍼티 키)` 32 | 33 | 만약 하나의 프로퍼티 키가 아닌 모든 프로퍼티 키에 대해 프로퍼티 어트리뷰트 정보를 받으려면 `Object.getOwnPropertyDescriptors(객체의 참조)`를 사용하면 된다. 34 | 35 | ## 16.3 데이터 프로퍼티와 접근자 프로퍼티 36 | 37 | > 💡 **데이터 프로퍼티** : 키와 값으로 구성된 일반적인 프로퍼티 38 | **접근자 프로퍼티** : 자체적으로는 값을 갖지 않고 다른 데이터 프로퍼티의 값을 읽거나 저장할 때 호출되는 접근자 함수로 구성된 프로퍼티.(`get`, `set`) 39 | 40 | ### 16.3.1 데이터 프로퍼티 41 | 42 | 데이터 프로퍼티는 다음과 같은 프로퍼티 어트리뷰트를 갖는다. 43 | - `[[Value]]` 44 | - 프로퍼티 키를 통해 프로퍼티 값에 접근하면 반환되는 값이다. 45 | - 프로퍼티 키를 통해 프로퍼티 값을 변경하면 `[[Value]]`에 값을 재할당한다. 46 | - 이때 프로퍼티가 없으면 프로퍼티를 동적 생성하고 생성된 프로퍼티의 `[[Value]]`에 값을 할당한다. 47 | - `[[Writable]]` 48 | - 프로퍼티 값의 변경 가능 여부를 나타내며 `boolean`값을 갖는다. 49 | - `[[Enumerable]]` 50 | - 프로퍼티의 열거 가능 여부를 나타내며 `boolean`값을 갖는다. 51 | - `[[Enumerable]]`이 `false`인 경우, 해당 프로퍼티는 `for...in` `Object.keys` 등으로 열거할 수 없다. 52 | - `[[Configurable]]` 53 | - 프로퍼티 재정의 가능 여부를 나타내며 `boolean`값을 갖는다. 54 | - `[[Configurable]]`이 `false`인 경우, 해당 프로퍼티의 삭제, 프로퍼티 어트리뷰트 값의 변경이 금지된다. 55 | 56 | 57 | ### 16.3.2 접근자 프로퍼티 58 | 59 | 접근자 프로퍼티는 다음과 같은 프로퍼티 어트리뷰트를 갖는다. 60 | - `[[Get]]` 61 | - 접근자 프로퍼티를 통해 데이터 프로퍼티의 값을 읽을 때 호출되는 접근자 함수다 62 | - 접근자 프로퍼티 키로 프로퍼티 값에 접근하면 프로퍼티 어트리뷰트 `[[Get]]`의 값, 즉 `getter`함수가 호출되고 그 결과가 프로퍼티 값으로 반환된다. 63 | - `[[Set]]` 64 | - 접근자 프로퍼티를 통해 데이터 프로퍼티의 값을 저장할 때 호출되는 접근자 함수다 65 | - 접근자 프로퍼티 키로 프로퍼티 값을 저장하면 프로퍼티 어트리뷰트 `[[Set]]`의 값, 즉 `Setter`함수가 호출되고 그 결과가 프로퍼티 값으로 저장된다. 66 | - `[[Enumerable]]` 67 | - 프로퍼티의 열거 가능 여부를 나타내며 `boolean`값을 갖는다. 68 | - `[[Enumerable]]`이 `false`인 경우, 해당 프로퍼티는 `for...in` `Object.keys` 등으로 열거할 수 없다. 69 | - `[[Configurable]]` 70 | - 프로퍼티 재정의 가능 여부를 나타내며 `boolean`값을 갖는다. 71 | - `[[Configurable]]`이 `false`인 경우, 해당 프로퍼티의 삭제, 프로퍼티 어트리뷰트 값의 변경이 금지된다. 72 | 73 | ```js 74 | const person = { 75 | firstName: "Sangjo", 76 | lastName: "Lee", 77 | 78 | get fullName(){ 79 | return `${this.firstName} ${this.lastName}`; 80 | } 81 | 82 | set fullName(name){ 83 | [this.firstName, this.lastName] = name.split(" "); 84 | } 85 | } 86 | ``` 87 | 88 | 위 예시에서`get` `set`이 붙은 `fullName`이 접근자 프로퍼티이다. 89 | 접근자 프로퍼티는 `[[Value]]`를 갖지 않는다. 90 | 91 | ## 16.4 프로퍼티 정의 92 | > 💡 새로운 프로퍼티를 추가하면서 프로퍼티 어트리뷰트를 명시적으로 정의하거나, 기존 프로퍼티의 프로퍼티 어트리뷰트를 재정의하는 것 93 | 94 | `Object.defineProperty` 메서드를 사용하여 프로퍼티의 어트리뷰트를 정의할 수 있다. 95 | 인수로 `객체의 참조`, `데이터 프로퍼티의 키인 문자열`, `프로퍼티 디스크립터 객체`를 전달한다. 96 | 97 | ```js 98 | const person = {}; 99 | 100 | // 데이터 프로퍼티 정의 101 | Object.defineProperty(person, 'name', { 102 | value: "sangjo", 103 | writable: true, 104 | enumerable: true, 105 | configurable: true 106 | }) 107 | 108 | // 접근자 프로퍼티 정의 109 | Object.defineProperty(person, 'yourName', { 110 | 111 | get(){ 112 | return `${this.name}` 113 | } 114 | 115 | set(newName){ 116 | this.name = newName; 117 | } 118 | 119 | enumerable: true, 120 | configurable: true 121 | }) 122 | ``` 123 | 124 | `Object.defineProperty` 메서드는 하나의 프로퍼티만 정의할 수 있는데, 여러 프로퍼티를 정의하려면 `Object.definePropertys` 메서드를 사용하면 된다. 125 | 126 | ## 16.5 객체 변경 방지 127 | 128 | 객체는 변경 가능한 값이므로 재할당 없이 직접 변경할 수 있다. 129 | 130 | ### 16.5.1 객체 확장 금지 131 | 132 | > 💡 `Object.perventExtensions` 메서드는 객체의 확장을 금지한다. 133 | 확장이 금지된 객체는 프로퍼티 추가가 금지된다. 134 | `Object.isExtensible` 메서드로 확인할 수 있다. 135 | 136 | 137 | ### 16.5.2 객체 밀봉 138 | 139 | > 💡 `Object.seal` 메서드는 객체를 밀봉한다. 140 | 밀봉된 객체는 프로퍼티 추가 및 삭제, 프로퍼티 어트리뷰트 재정의가 금지된다. 141 | 즉, **읽기와 쓰기만 가능**하다. 142 | `Object.isSealed` 메서드로 확인할 수 있다. 143 | 144 | ### 16.5.3 객체 동결 145 | 146 | > 💡 `Object.freeze` 메서드는 객체를 동결한다. 147 | 동결된 객체는 **읽기만 가능**하다. 148 | `Object.isFrozen` 메서드로 확인할 수 있다. 149 | 150 | ### 16.5.4 불변 객체 151 | > 💡 위 변경 방지 메서드들은 얕은 병경 방지로, 직속 프로퍼티만 변경이 방지되고 중첩 객체까지 영향을 주지는 못한다. 152 | `Object.freeze` 메서드를 모든 프로퍼티에 대해 재귀적으로 호출하여 읽기 전용의 불변 객체를 구현할 수 있다. 153 | 154 | -------------------------------------------------------------------------------- /3회차/천승아.md: -------------------------------------------------------------------------------- 1 | # ✍️ 공부 내용 정리 2 | 3 | ## 17장 생성자 함수에 의한 객체 생성 4 | 5 | ### 17-1. Object 생성자 함수 6 | 7 | - 생성자 함수: new 연산자와 함께 호출하여 객체(인스턴스)를 생성하는 함수 8 | - Object, String, Number, Date 등 빌트인 생성자 함수 제공 9 | - `typeof` object 10 | 11 | ### 17-2. 객체 리터럴, 생성자 함수 비교 12 | 13 | #### 1. 객체 리터럴에 의한 객체 생성 방식 문제점 14 | 15 | - 직관적이고 간편함 16 | - 단 하나의 객체만 생성하므로 동일한 프로퍼티 갖는 여러 객체 생성 시 번거로움 17 | 18 | #### 2. 생성자 함수에 의한 객체 생성 방식 장점 19 | 20 | - 클래스처럼 생성자 함수 하나로 프로퍼티 구조 동일한 객체 여러 개 간편하게 생성함 21 | 22 | ```js 23 | // 생성자 함수 24 | function Circle(radius) { 25 | // 생성자 함수 내부의 this는 생성자 함수가 생성할 인스턴스를 가리킨다. 26 | this.radius = radius; 27 | this.getDiameter = function () { 28 | return 2 * this.radius; 29 | }; 30 | } 31 | 32 | // 인스턴스의 생성 33 | const circle1 = new Circle(5); // 반지름이 5인 Circle 객체를 생성 34 | const circle2 = new Circle(10); // 반지름이 10인 Circle 객체를 생성 35 | 36 | console.log(circle1.getDiameter()); // 10 37 | console.log(circle2.getDiameter()); // 20 38 | ``` 39 | 40 | - `this`: 자기 참조 변수 -> 바인딩은 함수 호출 방식에 따라 동적으로 결정 됨 41 | 42 | ```js 43 | // 함수는 다양한 방식으로 호출될 수 있다. 44 | function foo() { 45 | console.log(this); 46 | } 47 | 48 | // 1. 일반적인 함수로서 호출: this는 전역객체 49 | // 전역 객체는 브라우저 환경에서는 window, Node.js 환경에서는 global을 가리킨다. 50 | foo(); // window 51 | 52 | // 2. 메서드로서 호출: this는 메서드를 호출한 객체 53 | const obj = { foo }; // ES6 프로퍼티 축약 표현 54 | obj.foo(); // obj 55 | 56 | // 3. 생성자 함수로서 호출: this는 생성자 함수가 생성할 인스턴스 57 | const inst = new foo(); // inst 58 | ``` 59 | 60 | - 생성자 함수는 new 연산자와 함께 호출해야 생성자 함수로 동작한다. 61 | - 함께 호출하지 않으면 일반 함수로 동작한다. (this 바인딩도 마찬가지) 62 | 63 | #### 3. 생성자 함수의 인스턴스 생성 과정 64 | 65 | - 생성자 함수의 역할 66 | 1. 인스턴스 생성(필수): 프로퍼티 구조가 동일한 인스턴스를 생성하기 위한 템플릿(클래스)로서 동작 67 | 2. 생성된 인스턴스 초기화(옵션) 68 | 69 | > JS 엔진은 암묵적으로 아래와 같은 과정을 거쳐 위 내용을 처리한다. 70 | 71 | 1. 인스턴스 생성과 this 바인딩 72 | 73 | - 암묵적으로 빈 객체(인스턴스) 생성 후 this에 바인딩 74 | - 바인딩: 식별자와 값을 연결하는 과정 -> this가 가리킬 객체를 바인딩 75 | 76 | 2. 인스턴스 초기화 77 | 78 | - 생성자 함수 코드가 실행되어 this에 바인딩 된 인스턴스를 초기화 79 | 80 | 3. 인스턴스 반환 81 | 82 | - 생성자 함수 내부에서 모든 처리가 끝나면 인스턴스가 바인딩된 this를 암묵적 반환함 83 | 84 | ```js 85 | function Circle(radius) { 86 | // 1. 암묵적으로 인스턴스가 생성되고 this에 바인딩된다. 87 | console.log(this); // Circle {} 88 | 89 | // 2. this에 바인딩되어 있는 인스턴스를 초기화한다. 90 | this.radius = radius; 91 | this.getDiameter = function () { 92 | return 2 * this.radius; 93 | }; 94 | 95 | // 3. 완성된 인스턴스가 바인딩된 this가 암묵적으로 반환된다 96 | } 97 | 98 | // 인스턴스 생성. Circle 생성자 함수는 암묵적으로 this를 반환한다. 99 | const circle = new Circle(1); 100 | console.log(circle); // Circle {radius: 1, getDiameter: ƒ} 101 | ``` 102 | 103 | 생성자 함수에서 다른 내용을 return하는 건 생성자 함수의 기본 동작을 훼손하므로 지양해야 함. 104 | 105 | #### 4.내부 메서드 [[Call]]과 [[Constructor]] 106 | 107 | - 함수는 객체 -> 일반 객체와 동일하게 동작 가능 108 | - but 일반 객체와 달리 호출 가능. 함수 객체만을 위한 슬롯과 내부 메서드를 추가적으로 가짐 109 | 110 | - [[Environment]], [[FormalParameters]], [[Call]], [[Constructor]] .. 111 | 112 | ```js 113 | function foo() {} 114 | 115 | // 일반적인 함수로서 호출: [[Call]]이 호출된다. 116 | foo(); 117 | 118 | // 생성자 함수로서 호출: [[Construct]]가 호출된다. 119 | new foo(); 120 | ``` 121 | 122 | 1. callable 함수 객체 123 | 124 | - 일반 함수로서 호출 -> 내부 메서드 [[Call]] 가짐 125 | - 호출할 수 있는 객체 == 함수 126 | - 함수는 무조건 callable! 하지만 constructor or non-constructor 127 | - -> **모든 함수 객체는 호출할 수 있지만 모든 함수 객체를 생성자 함수로서 호출할 수 있는 것은 아니다.** 128 | 129 | 2. constructor 함수 객체 130 | 131 | - new + 생성자 함수로서 호출 -> [[Constructor]] 호출 132 | 133 | 3. non-constructor 함수 객체 134 | 135 | - [[Constructor]]를 갖지 않음 136 | - 객체를 생성자 함수로서 호출할 수 없는 함수 137 | 138 | #### 5. constructor, non-constructor 구분 139 | 140 | 메서드 범위에 주의! 141 | 142 | - constructor: 함수 선언문, 함수 표현식, 클래스 143 | - non-constructor: 메서드, 화살표 함수 144 | - new + 생성자로 호출 시 에러 발생 145 | 146 | ```js 147 | // 일반 함수 정의: 함수 선언문, 함수 표현식 148 | function foo() {} 149 | const bar = function () {}; 150 | // 프로퍼티 x의 값으로 할당된 것은 일반 함수로 정의된 함수다. 이는 메서드로 인정하지 않는다. 151 | const baz = { 152 | x: function () {}, 153 | }; 154 | 155 | // 일반 함수로 정의된 함수만이 constructor이다. 156 | new foo(); // -> foo {} 157 | new bar(); // -> bar {} 158 | new baz.x(); // -> x {} 159 | 160 | // 화살표 함수 정의 161 | const arrow = () => {}; 162 | 163 | new arrow(); // TypeError: arrow is not a constructor 164 | 165 | // 메서드 정의: ES6의 메서드 축약 표현만을 메서드로 인정한다. 166 | const obj = { 167 | x() {}, 168 | }; 169 | 170 | new obj.x(); // TypeError: obj.x is not a constructor 171 | ``` 172 | 173 | - 함수를 프로퍼티 값으로 사용하면 일반적으로 메서드로 통칭하나 -> ECMA에서는 메서드 축약 표현만을 의미한다. 174 | 175 | - 함수 정의 방식에 따라 constructor, non-constructor를 구분함! 176 | 177 | #### 6. new 연산자 178 | 179 | - new 연산자와 함께 호출: [[Constructor]] 실행 180 | - new 연산자 없이 호출: [[Call]] 실행 181 | 182 | ```js 183 | // 생성자 함수로서 정의하지 않은 일반 함수 184 | function add(x, y) { 185 | return x + y; 186 | } 187 | 188 | // 생성자 함수로서 정의하지 않은 일반 함수를 new 연산자와 함께 호출 189 | let inst = new add(); 190 | // 함수가 객체를 반환하지 않았으므로 반환문이 무시된다. 따라서 빈 객체가 생성되어 반환된다. 191 | console.log(inst); // {} 192 | 193 | // 객체를 반환하는 일반 함수 194 | function createUser(name, role) { 195 | return { name, role }; 196 | } 197 | 198 | // 생성자 함수로서 정의하지 않은 일반 함수를 new 연산자와 함께 호출 199 | inst = new createUser("Lee", "admin"); 200 | // 함수가 생성한 객체를 반환한다. 201 | console.log(inst); // {name: "Lee", role: "admin"} 202 | ``` 203 | 204 | - 생성자 함수: 파스칼케이스로 명명하여 일반 함수와 구분함. 205 | 206 | #### 7. new.target 207 | 208 | - new 연산자와 함께 생성자 함수로 호출 되었는 지 확인 -> 생성자 함수가 new 없이 호출 되는 것을 방지 209 | - 메타 프로퍼티. new 연산자로 호출되었을 경우 자기 자신 가리키고, 없이 호출되면 undefined 210 | - IE는 지원 X 211 | 212 | ```js 213 | // 생성자 함수 214 | function Circle(radius) { 215 | // 이 함수가 new 연산자와 함께 호출되지 않았다면 new.target은 undefined다. 216 | if (!new.target) { 217 | // new 연산자와 함께 생성자 함수를 재귀 호출하여 생성된 인스턴스를 반환한다. 218 | return new Circle(radius); 219 | } 220 | 221 | this.radius = radius; 222 | this.getDiameter = function () { 223 | return 2 * this.radius; 224 | }; 225 | } 226 | 227 | // new 연산자 없이 생성자 함수를 호출하여도 new.target을 통해 생성자 함수로서 호출된다. 228 | const circle = Circle(5); 229 | console.log(circle.getDiameter()); 230 | ``` 231 | 232 | > 스코프 세이프 생성자 패턴 233 | 234 | - new.target 지원하지 않을 때 사용: this 바인딩 이용 235 | 236 | ```js 237 | // Scope-Safe Constructor Pattern 238 | function Circle(radius) { 239 | // 생성자 함수가 new 연산자와 함께 호출되면 함수의 선두에서 빈 객체를 생성하고 240 | // this에 바인딩한다. 이때 this와 Circle은 프로토타입에 의해 연결된다. 241 | 242 | // 이 함수가 new 연산자와 함께 호출되지 않았다면 이 시점의 this는 전역 객체 window를 가리킨다. 243 | // 즉, this와 Circle은 프로토타입에 의해 연결되지 않는다. 244 | if (!(this instanceof Circle)) { 245 | // new 연산자와 함께 호출하여 생성된 인스턴스를 반환한다. 246 | return new Circle(radius); 247 | } 248 | 249 | this.radius = radius; 250 | this.getDiameter = function () { 251 | return 2 * this.radius; 252 | }; 253 | } 254 | 255 | // new 연산자 없이 생성자 함수를 호출하여도 생성자 함수로서 호출된다. 256 | const circle = Circle(5); 257 | console.log(circle.getDiameter()); // 10 258 | ``` 259 | 260 | - 대부분 빌트인 함수는 new 연산자 관계 없이 동일 동작함 261 | - but String, Number, Boolean 등은 new 연산자로 호출하면 객체를 반환하지만 없이 호출할 경우 데이터 타입 변환하여 원시값을 리턴함. 262 | 263 | ```js 264 | let obj = new Object(); 265 | console.log(obj); // {} 266 | 267 | let f = new Function("x", "return x ** x"); 268 | console.log(f); // ƒ anonymous(x) { return x ** x } 269 | 270 | f = Function("x", "return x ** x"); 271 | console.log(f); // ƒ anonymous(x) { return x ** x } 272 | 273 | const str = String(123); 274 | console.log(str, typeof str); // 123 string 275 | 276 | const bool = Boolean("true"); 277 | console.log(bool, typeof bool); // true boolean 278 | ``` 279 | 280 | --- 281 | 282 | ## 18장 함수와 일급 객체 283 | 284 | ### 18-1. 일급 객체 285 | 286 | 일급 객체란? 287 | 288 | - 무명의 리터럴로 생성 가능 == 런타임에 생성 가능 289 | - 변수, 자료구조에 저장할 수 있음 290 | - 함수 매개변수에 전달 가능 291 | - 함수 반환값으로 사용 가능 292 | 293 | -> JS에서 함수는 일급 객체다. 294 | 295 | ## 18-2. 함수 객체의 프로퍼티 296 | 297 | 함수는 객체이므로 프로퍼티를 가진다. 298 | 299 | - 일반 객체에는 없는 함수 객체 고유 프로퍼티: arguments, caller, length, name, prototype 300 | 301 | #### 1. arguments 302 | 303 | - 인수 정보를 갖고 있는 유사 배열 객체 -> 함수 내부에서 지역변수로 사용. 304 | - 초과된 인수는 arguments에 저장됨 -> 가변 인자 함수 구현할 때 유용함 305 | 306 | - 프로퍼티 키: 인수의 순서 307 | - callee 프로퍼티: 함수 자신 308 | - length 프로퍼티: 인수의 개수 309 | - Symbol 프로퍼티: arguments 객체를 순회 가능한 이터러블로 만들기 위한 프로퍼티 310 | 311 | 유사 배열 객체이므로 직접 배열 메서드 사용 불가능 -> Function.prototype.call, Function.prototype.apply로 간접 호출 해야했으나 ES6부터 Rest 파라미터 도입으로 해결됨. 312 | 313 | #### 2. caller 프로퍼티 314 | 315 | - 함수 자신을 호출한 함수를 가리킴 -> 비표준 프로퍼티 316 | 317 | #### 3. length 프로퍼티 318 | 319 | - 함수 정의할 때 선언한 매개변수의 개수 320 | - arguments.length와 차이: 인자의 개수 321 | 322 | #### 4. name 프로퍼티 323 | 324 | - 함수 이름 325 | - 함수 이름 !== 함수 객체 가리키는 식별자 326 | - 함수 호출 시 식별자로 호출함. 327 | - 익명 함수 표현식에서 ES5, ES6 차이가 있음 328 | 329 | ```js 330 | // 기명 함수 표현식 331 | var namedFunc = function foo() {}; 332 | console.log(namedFunc.name); // foo 333 | 334 | // 익명 함수 표현식 335 | var anonymousFunc = function () {}; 336 | // ES5: name 프로퍼티는 빈 문자열을 값으로 갖는다. 337 | // ES6: name 프로퍼티는 함수 객체를 가리키는 변수 이름을 값으로 갖는다. 338 | console.log(anonymousFunc.name); // anonymousFunc 339 | 340 | // 함수 선언문(Function declaration) 341 | function bar() {} 342 | console.log(bar.name); // bar 343 | ``` 344 | 345 | #### 5. `__proto__` 접근자 프로퍼티 346 | 347 | - 모든 객체는 [[Prototype]] 이라는 내부 슬롯을 가짐 348 | - 객체 지향 프로그래밍에서 상속을 구현하는 프로토타입 객체 349 | - `__proto__` 프로퍼티: 프로토타입 객체에 접근하기 위해 사용하는 접근자 프로퍼티 350 | - 간접적으로만 프로토타입 객체에 접근 가능 351 | 352 | ```js 353 | const obj = { a: 1 }; 354 | 355 | // 객체 리터럴 방식으로 생성한 객체의 프로토타입 객체는 Object.prototype이다. 356 | console.log(obj.__proto__ === Object.prototype); // true 357 | 358 | // 객체 리터럴 방식으로 생성한 객체는 프로토타입 객체인 Object.prototype의 프로퍼티를 상속받는다. 359 | // hasOwnProperty 메서드는 Object.prototype의 메서드다. 360 | console.log(obj.hasOwnProperty("a")); // true 361 | console.log(obj.hasOwnProperty("__proto__")); // false 362 | ``` 363 | 364 | - hasOwnProperty: 객체 고유의 프로퍼티 키 일때만 true 365 | 366 | #### 6. prototype 프로퍼티 367 | 368 | - constructor 만이 소유하는 프로퍼티 369 | - 함수가 생성자 함수로 호출될 때 인스턴스의 프로토타입 객체를 가리킴 370 | 371 | --- 372 | -------------------------------------------------------------------------------- /4회차/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hy57in/study-javascript-deep-dive/aed951621cd80b2830fc238d625027502e88d41c/4회차/README.md -------------------------------------------------------------------------------- /4회차/img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hy57in/study-javascript-deep-dive/aed951621cd80b2830fc238d625027502e88d41c/4회차/img.png -------------------------------------------------------------------------------- /4회차/img_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hy57in/study-javascript-deep-dive/aed951621cd80b2830fc238d625027502e88d41c/4회차/img_1.png -------------------------------------------------------------------------------- /4회차/img_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hy57in/study-javascript-deep-dive/aed951621cd80b2830fc238d625027502e88d41c/4회차/img_2.png -------------------------------------------------------------------------------- /4회차/img_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hy57in/study-javascript-deep-dive/aed951621cd80b2830fc238d625027502e88d41c/4회차/img_3.png -------------------------------------------------------------------------------- /4회차/img_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hy57in/study-javascript-deep-dive/aed951621cd80b2830fc238d625027502e88d41c/4회차/img_4.png -------------------------------------------------------------------------------- /4회차/img_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hy57in/study-javascript-deep-dive/aed951621cd80b2830fc238d625027502e88d41c/4회차/img_5.png -------------------------------------------------------------------------------- /4회차/김록원.md: -------------------------------------------------------------------------------- 1 | # 프로토타입 2 | js는 `명령형`, `함수형`, `프로토타입 기반 객체지향` 을 지원하는 멀티 패러다임 프로그래밍언어. 3 | 클래스 기반 객체지향의 특징인 상속과 캡슐화를 위한 키워드인 public, private, protected 등이 없지만 더 강력한 객체지향 능력을 지니는 포로토타입 기반의 객체지향 언어이다. 4 | 5 | > **💡 ES6의 클래스** 6 | 생성자 함수와 마찬가지로 프로토타입 기반의 인스턴스를 생성. 7 | 하지만 정확히 동일하게 동작하지 않으며 클래스가 더 엄격하며 생성자 함수에서는 제공하지 않는 기능을 제공한다. 8 | 9 |
10 | 11 | ### 상속과 프로토타입 12 | js는 프로토타입을 기반으로 상속을 구현. 13 | 14 | **생성자 함수의 문제점** 15 | - 인스턴스 생성시 각 인스턴스마다 프로퍼티, 메서드들을 따로 메모리를 가진다.(메서드의 경우 같은 메모리를 공유해도 문제 없지 않는가!) 16 | - 동일한 메서드를 중복 소유하는 것은 메모리를 불필요하게 낭비함. 17 | 18 | **프로토 타입을 이용해 불필요한 중복제거** 19 | - 프로토 타입을 이용하여 상속을 구현한다.(생성자 함수의 prototype에 상속될 것 정의) 20 | ```js 21 | function Circle(radius) { this.radius = radius } 22 | 23 | Circle.prototype.getArea = function() { 24 | return Math.PI * this.radius ** 2; 25 | } 26 | 27 | const circle1 = new Circle(1); 28 | const circle2 = new Circle(2); 29 | console.log(circle1.getArea === circle2.getArea) // true 30 | ``` 31 |
32 | 33 | ### 프로토타입 객체 34 | - 프로토타입이란 어떤 객체의 상위 객체의 역할을 하는 객체. 35 | - 모든 객체는 `[[Prototype]]`이라는 내부 슬롯을 가짐(객체생성방식에 따라 프로토타입이 결정) 36 | - 객체 리터럴로 생성된 객체의 프로토타입 -> Object.prototype 37 | - 생성자 함수로 생성된 객체의 프로토타입 -> 생성자 함수의 prototype 프로퍼티에 바인딩되어 있는 객체 38 | - 모든 객체는 `__proto__` 프로퍼티를 이용해 자신의 프로토타입에 **간접 접근할 수 있다**. 39 | 40 |
41 | 42 | **`__proto__` 접근 프로퍼티** 43 | - getter, setter라 부르는 접근자 함수를 통해 프로토타입을 호출, 할당한다. 44 | - 객체가 직접 소유하는 프로퍼티가 아니라 `Object.prototype`의 프로퍼티다. 45 | - 모든 객체가 상속으로 `Object.prototype.__proto__` 접근자 프로퍼티를 사용할 수 있다. 46 | 47 |
48 | 49 | **함수 객체의 prototype 프로퍼티** 50 | - 함수 객체만이 소유하는 `prototype` 프로퍼티 -> 생성자 함수가 생성할 인스턴스의 프로토타입을 가리킨다. 51 | - 생성자 함수로서 생성할 수 없는 함수(non-cnostructor)인 화살표 함수와 ES6의 메서드 축약표현은 해당 프로퍼티를 가지지 않는다. 52 | - 객체 생성 후 `__proto__` 와 생성자 함수의 prototype 은 동일한 프로토타입을 가리킨다. 53 | ```js 54 | // 화살표 함수는 non-constructor다. 55 | const Person = name => { 56 | this.name = name; 57 | }; 58 | 59 | // non-constructor는 prototype 프로퍼티를 소유하지 않는다. 60 | console.log(Person.hasOwnProperty('prototype')); // false 61 | 62 | // non-constructor는 프로토타입을 생성하지 않는다. 63 | console.log(Person.prototype); // undefined 64 | 65 | // ES6의 메서드 축약 표현으로 정의한 메서드는 non-constructor다. 66 | const obj = { 67 | foo() {} 68 | }; 69 | 70 | // non-constructor는 prototype 프로퍼티를 소유하지 않는다. 71 | console.log(obj.foo.hasOwnProperty('prototype')); // false 72 | 73 | // non-constructor는 프로토타입을 생성하지 않는다. 74 | console.log(obj.foo.prototype); // undefined 75 | ``` 76 | 77 | 78 | 79 |
80 | 81 | ### 리터럴 표기법에 의해 생성된 객체의 생성자 함수와 프로토타입 82 | Object 생성자 함수에 인수를 전달하지 않거나 undefined, null 인수를 전달하면서 83 | - 내부적으로 추상 연산 OrdinaryObjectCreate를 호출 -> Object.prototype을 프로토타입으로 갖는 빈객체를 생성함. 84 | 85 | **객체 리터럴이 평가될 때는 위 과정으로 빈객체가 생성된 후 프로퍼티가 추가됨** 86 | ```js 87 | // obj 객체는 Object 생성자 함수로 생성한 객체가 아니라 객체 리터럴로 생성했다. 88 | const obj = {}; 89 | 90 | // 하지만 obj 객체의 생성자 함수는 Object 생성자 함수다. 91 | console.log(obj.constructor === Object); // true 92 | ``` 93 | 94 |
95 | 96 | ### 프로토타입의 생성 시점 97 | 프로토타입과 생성자 함수는 단독으로 존재할 수 없고 언제나 쌍으로 존재하다. 98 | 고로 생성자 함수가 생성되는 시점에 프로토타입도 생성된다. 99 | 100 | **사용자 정의 생성자 함수** 101 | - 일반 함수(`[[Construct]]`를 내부 메서드로 갖는)로 정의한 함수 객체(함수 선언문, 함수 표현식) 102 | - 런타임 이전 선언문을 먼저 처리하기에 그 시점에 프로토타입이 생성(`사용자정의.prototype`)되고 바인딩 된다. 103 | - 프로토타입도 객체이므로 프로토타입의 프로토타입은 `Object.prototype`을 갖는다. 104 | 105 |
106 | 107 | **빌트인 생성자 함수** 108 | - Object, String ,Member, Fucntion ... 109 | - 전역 객체가 생성되는 시점에 생성된다 110 | 111 |
112 | 113 | 114 | ### 객체 생성방식과 프로토타입의 결정 115 | `객체리터럴`, `Object 생성자 함수`, `생성자 함수`, `Object.create 메서드`, `클래스` 116 | 추상 연산에 전달되는 인수에 의해 프로토타입이 결정된다. 117 | 118 | **객체리터럴, Object 생성자 함수** 119 | 추상연산에 따라 `Object.property`를 프로토타입으로 갖는다. 120 | 121 | **생성자 함수** 122 | 추상 연산 OrdinaryObjectCreate에 전달되는 프로토타입은 생성자 함수의 constructor에 바인딩 되어있는 객체이다.(즉, 생성자함수.prototype을 프로토타입으로 가진다) 123 | Object.prototype은 다양한 빌트인 메서드를 가지고 있지만 사용자 정의 생성자 함수와 함께 생성된 prototype은 constructor 프로퍼티만을 가진다. 124 | 125 |
126 | 127 | ### 프로토타입 체인 128 | 프로토타입 체인은 **js가 객체지향 프로그래밍의 상속을 구현하는 메커니즘**이다. 129 | 객체의 프로퍼티에 접근하려고 할때 해당 객체에 접근하려는 프로퍼티가 없다면 `[[Prototype]]`의 참조를 따라 순차적으로 검색힌다. 130 | 131 | `프로토타입 체인` -> 상속과 프로퍼티 검색을 위한 메커니즘 132 | `스코프 체인` -> 식별자 검색을 위한 매커니즘 133 | 134 |
135 | 136 | ### 프로토타입의 교체 137 | 프로토타입은 생성자 함수 또는 인스턴스에 의해 교체할 수 있다. 138 | 139 | **생성자 함수를 이용** 140 | ```js 141 | const Person(function() { 142 | function Person(name) { 143 | this.name = name; 144 | } 145 | 146 | // 프로토 타입 교체! 147 | // 객체리터럴은 constructor가 없으므로 사라짐 but, 상위인 Object.prototype이 가지고 있음 148 | Person.prototype = { 149 | sayHello() { 150 | console.log(`good ${this.name}}`) 151 | } 152 | } 153 | 154 | return Person; 155 | }()); 156 | 157 | const me = new Person('Lee'); 158 | ``` 159 | 160 |
161 | 162 | **인스턴스에 의한 프로토타입 교체** 163 | ```js 164 | function Person(name) { 165 | this.name = name; 166 | } 167 | 168 | const me = new Person('Lee'); 169 | 170 | const parent = { 171 | sayHello() { 172 | console.log(`Hi! My name is ${this.name}`); 173 | } 174 | }; 175 | 176 | Object.setPrototypeOf(me, parent); 177 | ``` 178 | 179 |
180 | 181 | ### 직접 상속 182 | 183 | **Object.create에 의한 직접상속** 184 | `Object.create`메서드는 명시적으로 프로토타입을 지정해 새로운 객체를 생성한다. 185 | - 첫번째인자로 생성할 객체의 프로토타입으로 지정할 객체 186 | - 두번째인자(생략가능)는 생성할 객체의 프로퍼티 키와 프로퍼티 디스크립터 객체로 이뤄진 객체 187 | 188 | **객체 리터럴 내부에서 직접상속** 189 | ```js 190 | const myProto = { x: 10}; 191 | 192 | const obj = { 193 | y: 20, 194 | // obj -> myProto -> Object.prototype 195 | __proto__: myProto 196 | }; 197 | ``` 198 | 199 |
200 | 201 | ### 프로퍼티 존재확인과 열거 202 | `{프로퍼티 키를 나타내는 문자열} in {객체로 평가되는 표현식}` 연산자를 통해 객체 내에 프로퍼티가 있는지 확인가능하다. 프로토타입 체인상에 존재하는 프로퍼티까지도 검색하므로 유의하자! 203 | ```js 204 | const person = { 205 | name: 'Lee', 206 | address: 'Seoul' 207 | }; 208 | 209 | // person 객체에 name 프로퍼티가 존재한다. 210 | console.log('name' in person); // true 211 | // person 객체에 address 프로퍼티가 존재한다. 212 | console.log('address' in person); // true 213 | // person 객체에 age 프로퍼티가 존재하지 않는다. 214 | console.log('age' in person); // false 215 | console.log('toString' in person); // true 216 | ``` 217 | 218 |
219 | 220 | Object.prototype에 존재하는 `hasOwnProperty` 메서드를 통해 객체 고유의 프로퍼티 키인 경우에만 true를 반환하게 할 수 있다. 221 | ```js 222 | console.log(person.hasOwnProperty('name')); // true 223 | console.log(person.hasOwnProperty('age')); // false 224 | console.log(person.hasOwnProperty('toString')); // false 225 | ``` 226 | 227 |
228 | 229 | `for (변수선언문 in 객체)`을 이용해 객체 프로퍼티를 순회할 수 있다. 프로퍼티 어트리뷰트 `[[Enumerable]]`이 false이면 순회되지 않는다. key가 symbol일 경우에도 순회하지 않는다. 이 또한 프로토타입 체인상의 모든 프로퍼티를 순회한다. 230 | - 대부분의 모던 브라우저가 순서를 보장하고 숫자인 프로퍼티 키에 대해서는 정렬을 한다. 231 | - 하지만 보장하지 않는 브라우저가 있을 수 있으니 사용시 주의하자! 232 | ```js 233 | const person = { 234 | name: 'Lee', 235 | address: 'Seoul' 236 | }; 237 | 238 | // in 연산자는 객체가 상속받은 모든 프로토타입의 프로퍼티를 확인한다. 239 | console.log('toString' in person); // true 240 | 241 | // for...in 문도 객체가 상속받은 모든 프로토타입의 프로퍼티를 열거한다. 242 | // 하지만 toString과 같은 Object.prototype의 프로퍼티가 열거되지 않는다. 243 | for (const key in person) { 244 | console.log(key + ': ' + person[key]); 245 | } 246 | // name: Lee 247 | // address: Seoul 248 | ``` 249 | 250 |
251 | 252 | **추가** 253 | `Object.keys(객체)` -> key들만을 배열로 반환함 254 | `Object.values(객체)`(ES8) -> value들만을 배열로 반환함 255 | `Object.entries(객체)`(ES8) -> [key, value]로 배열을 반환 256 | ```js 257 | const person = { 258 | name: 'Lee', 259 | address: 'Seoul', 260 | __proto__: { age: 20 } 261 | }; 262 | 263 | console.log(Object.keys(person)); // ["name", "address"] 264 | console.log(Object.values(person)); // ["Lee", "Seoul"] 265 | console.log(Object.entries(person)); // [["name", "Lee"], ["address", "Seoul"]] 266 | ``` 267 | 268 |
269 | 270 | # 빌트인 객체 271 | 272 | ### 원시값과 래퍼 객체 273 | 원시값은 객체가 아니므로 프로퍼티나 메서드를 가질 수 없지만 원시값인 문자열이 마치 객체처럼 동작한다. 274 | js 엔진은 일시적으로 원시값을 연관된 객체로 변환해준다.(이를 `래퍼 객체`라고 부름) 275 | ```js 276 | const str = 'hi'; 277 | 278 | // 원시 타입인 문자열이 래퍼 객체인 String 인스턴스로 변환된다. 279 | console.log(str.length); // 2 280 | console.log(str.toUpperCase()); // HI 281 | 282 | // 래퍼 객체로 프로퍼티에 접근하거나 메서드를 호출한 후, 다시 원시값으로 되돌린다. 283 | console.log(typeof str); // string 284 | ``` 285 | 286 |
287 | 288 | ### 전역객체 289 | 브라우저 환경에서는 `window(또는 self, this, frames)`가 전역객체를 가리킴 290 | Node.js 환경에서는 `global`이 전역객체를 가리킴 291 | ES11부터 `globalThis`를 이용해서 어느 환경에서든 전역객체에 접근할 수 있다. 292 | 293 | - 전역 객체의 프로퍼티를 참조할 때 위 명칭을 생략할 수 있다. 294 | - var 키워드로 선언하면 전역변수 295 | 296 |
297 | 298 | ### encodeURI, decodeURI 299 | 300 | **encodeURI** 301 | URI를 문자열로 전달받아 이스케이프 처리를 위해 인코딩함. 302 | 이스케이프 처리란 어떤 시스템에서도 읽을 수 있는 아스키 문자 셋으로 변환하는 것이다. 303 | `알파벳`, `숫자`, `-_.!~*'()`는 이스케이프 처리에서 제외된다. 304 | 305 | **decodeURI** 306 | 인코딩된 URI를 인수로 전달받아 이스케이프 처리 이전으로 디코딩한다. 307 | 308 |
309 | 310 | ### encodeURIComponent, decodeURIComponent 311 | URI의 구성요소를 encode, decode 해준다. 312 | querystring의 경우 `? = &` 도 인코딩되므로 주의하자! 313 | 314 |
315 | 316 | # 참고자료 317 | 318 | - [js class의 extends](https://2ssue.github.io/common_questions_for_Web_Developer/docs/Javascript/8_es6_class_extends.html#%E1%84%8E%E1%85%A1%E1%86%B7%E1%84%80%E1%85%A9) 319 | - [Object.create](https://velog.io/@thms200/Object.create-) 320 | - [객체 지향의 본질](https://velog.io/@eddy_song/alan-kay-OOP) 321 | - [js 퀴즈 1](https://inflearn-quiz.vercel.app/javascript/21-prototype-1) 322 | - [js 퀴즈 2](https://inflearn-quiz.vercel.app/javascript/22-prototype-2) 323 | - [js가 프로토타입을 선택한 이유](https://medium.com/@limsungmook/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EB%8A%94-%EC%99%9C-%ED%94%84%EB%A1%9C%ED%86%A0%ED%83%80%EC%9E%85%EC%9D%84-%EC%84%A0%ED%83%9D%ED%96%88%EC%9D%84%EA%B9%8C-997f985adb42) 324 | - [최신브라우저 내부 살피기?](https://d2.naver.com/helloworld/5237120) 325 | -------------------------------------------------------------------------------- /4회차/김민지.md: -------------------------------------------------------------------------------- 1 | # 프로토타입 2 | 3 | ## 1. 프로토타입 객체 4 | 5 | 자바스크립트는 프로토타입 기반 객체지향 프로그래밍 언어이다. 프로토타입 기반 객체지향 프로그래밍 언어는 클래스 없이 객체를 생성할 수 있다. 6 | 7 | 자바스크립트의 모든 객체는 자신의 부모 역할을 담당하는 객체와 연결되어 있다. 객체 지향의 상속 개념처럼 부모 객체의 프로퍼티 또는 메소드를 상속받아 사용할 수 있게 한다. 이러한 부모 객체를 프로토타입이라 한다. 8 | 9 | 프로토타입 객체는 생성자 함수에 의해 생성된 각각의 객체에 공유 프로퍼티를 제공하기 위해 사용한다. 10 | 11 | ```js 12 | var student = { 13 | name: 'Lee', 14 | score: 90, 15 | }; 16 | 17 | // student에는 hasOwnProperty 메소드가 없지만 아래 구문은 동작한다. 18 | student.hasOwnProperty('name'); // true 19 | ``` 20 | 21 | 자바스크립트의 모든 객체는 `[[Prototype]]`이라는 인터널 슬롯을 가진다. `[[Prototype]]`의 값은 `null` 또는 객체이며 상속을 구현하는데 사용된다. `[[Prototype]]` 객체의 데이터 프로퍼티는 `get` 액세스를 위해 상속되어 자식 객체의 프로퍼티처럼 사용할 수 있다. 하지만 `set` 액세스는 허용되지 않는다. 22 | 23 | `[[Prototype]]`의 값은 프로토타입 객체이며 `__proto__` 프로퍼티로 접근할 수 있다. `__proto__` 프로퍼티에 접근하면 내부적으로 `Object.getPrototypeOf`가 호출되어 프로토타입 객체를 반환한다. 24 | 25 | `student` 객체는 `__proto__` 프로퍼티로 자신의 부모 객체(프로토타입 객체)인 `Object.prototype`을 가리키고 있다. 26 | 27 | ```js 28 | var student = { 29 | name: 'Lee', 30 | score: 90, 31 | }; 32 | 33 | console.log(student.__proto__ === Object.prototype); // true 34 | ``` 35 | 36 | 프로토타입은 객체를 생성할 때 결정된다. 결정된 프로토타입 객체는 다른 임의의 객체로 변경할 수 있다. 이것은 부모 객체인 프로토타입을 동적으로 변경할 수 있다는 것을 의미한다. 이러한 특징을 활용하여 객체의 상속을 구현할 수 있다. 37 | 38 | ## 2. `[[Prototype]]` vs `prototype` 프로퍼티 39 | 40 | 모든 객체는 자신의 프로토타입 객체를 가리키는 `[[Prototype]]` 인터널 슬롯을 갖으며 상속을 위해 사용한다. 41 | 42 | 함수도 객체이므로 `[[Prototype]]` 인터널 슬롯을 갖는다. 그런데 함수 객체는 일반 객체와는 달리 `prototype` 프로퍼티도 소유하고 있다. 43 | 44 | ```js 45 | function Person(name) { 46 | this.name = name; 47 | } 48 | 49 | var foo = new Person('Lee'); 50 | 51 | console.log(Person); // prototype 프로퍼티가 있다. 52 | console.log(foo); // prototype 프로퍼티가 없다. 53 | ``` 54 | 55 | 함수를 포함한 모든 객체는 `[[Prototype]]` 인터널 슬롯을 가지고 있다. 객체의 입장에서 자신의 부모 역할을 하는 프로토타입 객체를 가리키며 함수 객체의 경우 `Function.prototype`을 가리킨다. 56 | 57 | ```js 58 | console.log(Person.__proto__ === Function.prototype); 59 | ``` 60 | 61 | `prototype` 프로퍼티는 함수 객체만 가지고 있는 프로퍼티이다. 함수 객체가 생성자로 사용될 때 이 함수를 통해 생성될 객체의 부모 역할을 하는 객체(프로토타입 객체)를 가리킨다. 62 | 63 | ```js 64 | console.log(Person.prototype === foo.__proto__); 65 | ``` 66 | 67 | ## 3. `constructor` 프로퍼티 68 | 69 | 프로토타입 객체는 `constructor` 프로퍼티를 갖는다. 이 `constructor` 프로퍼티는 객체의 입장에서 자신을 생성한 객체를 가리킨다. 70 | 71 | ```js 72 | function Person(name) { 73 | this.name = name; 74 | } 75 | 76 | var foo = new Person('Lee'); 77 | 78 | // Person() 생성자 함수에 의해 생성된 객체를 생성한 객체는 Person() 생성자 함수이다. 79 | console.log(Person.prototype.constructor === Person); 80 | 81 | // foo 객체를 생성한 객체는 Person() 생성자 함수이다. 82 | console.log(foo.constructor === Person); 83 | 84 | // Person() 생성자 함수를 생성한 객체는 Function() 생성자 함수이다. 85 | console.log(Person.constructor === Function); 86 | ``` 87 | 88 | `Person()` 생성자 함수에 의해 생성된 객체를 `foo`라 하자. 이 `foo` 객체를 생성한 객체는 `Person()` 생성자 함수이다. 이때 `foo` 객체 입장에서 자신을 생성한 객체는 `Person()` 생성자 함수이며, `foo` 객체의 프로토타입 객체는 `Person.prototype`이다. 따라서 프로토타입 객체 `Person.prototype`의 `constructor` 프로퍼티는 `Person()` 생성자 함수를 가리킨다. 89 | 90 | ## 4. 프로토타입 체인 91 | 92 | 자바스크립트는 특정 객체의 프로퍼티나 메소드에 접근하려고 할 때 해당 객체에 접근하려는 프로퍼티 또는 메소드가 없다면 `[[Prototype]]`이 가리키는 링크를 따라 자신의 부모 역할을 하는 프로토타입 객체의 프로퍼티나 메소드를 차례대로 검색한다. 이것을 프로토타입 체인이라 한다. 93 | 94 | ```js 95 | var student = { 96 | name: 'Lee', 97 | score: 90, 98 | }; 99 | 100 | // Object.prototype.hasOwnProperty() 101 | console.log(student.hasOwnProperty('name')); // true 102 | ``` 103 | 104 | `student` 객체의 `[[Prototype]]`이 가리키는 링크를 따라가서 `student` 객체의 부모 역할을 하는 프로토타입 객체(`Object.prototype`)의 메소드 `hasOwnProperty`를 호출해서 `student` 객체가 `hasOwnProperty` 메소드를 가지고 있지 않아도 에러가 발생하지 않는다. 105 | 106 | ```js 107 | var student = { 108 | name: 'Lee', 109 | score: 90, 110 | }; 111 | 112 | console.log(student.hasOwnProperty('name')); // true 113 | console.log(student.__proto__ === Object.prototype); // true 114 | console.log(Object.prototype.hasOwnProperty('hasOwnProperty')); // true 115 | ``` 116 | 117 | ### 4-1. 객체 리터럴 방식으로 생성된 객체의 프로토타입 체인 118 | 119 | 자바스크립트 엔진은 객체 리터럴로 객체를 생성하는 코드를 만나면 내부적으로 `Object()` 생성자 함수를 사용하여 객체를 생성한다. 120 | 121 | 함수 객체인 `Object()` 생성자 함수는 일반 객체와 달리 `prototype` 프로퍼티가 있다. 122 | 123 | ```js 124 | var person = { 125 | name: 'Lee', 126 | gender: 'male', 127 | sayHello: function () { 128 | console.log('Hi! my name is ' + this.name); 129 | }, 130 | }; 131 | 132 | console.log(person.__proto__ === Object.prototype); // true 133 | console.log(Object.prototype.constructor === Object); // true 134 | console.log(Object.__proto__ === Function.prototype); // true 135 | console.log(Function.prototype.__proto__ === Object.prototype); // true 136 | ``` 137 | 138 | ### 4-2. 생성자 함수로 생성된 객체의 프로토타입 체인 139 | 140 | 함수 선언식, 함수 표현식 모두 함수 리터럴 방식을 사용한다. 함수 리터럴 방식은 `Function()` 생성자 함수로 함수를 생성하는 것을 단순화한 것이다. 141 | 142 | 함수를 정의하는 함수 선언식, 함수 표현식, `Function()` 생성자 함수 모두 `Function()` 생성자 함수를 통해 함수 객체를 생성한다. 어떠한 방식으로 함수 객체를 생성해도 모든 함수 객체의 `prototype` 객체는 `Function.prototype`이다. 생성자 함수도 함수 객체이므로 생성자 함수의 `prototype` 객체는 `Function.prototype`이다. 143 | 144 | 3가지 객체 생성 방식에 의해 생성된 객체의 `prototype` 객체는 다음과 같다. 145 | 146 | | 객체 생성 방식 | 엔진의 객체 생성 | 인스턴스의 prototype 객체 | 147 | | :------------------: | :------------------: | :------------------------: | 148 | | 객체 리터럴 | Object() 생성자 함수 | Object.prototype | 149 | | Object() 생성자 함수 | Object() 생성자 함수 | Object.prototype | 150 | | 생성자 함수 | 생성자 함수 | 생성자 함수 이름.prototype | 151 | 152 | ```js 153 | function Person(name, gender) { 154 | this.name = name; 155 | this.gender = gender; 156 | this.sayHello = function () { 157 | console.log('Hi! my name is ' + this.name); 158 | }; 159 | } 160 | 161 | var foo = new Person('Lee', 'male'); 162 | 163 | console.dir(Person); 164 | console.dir(foo); 165 | 166 | console.log(foo.__proto__ === Person.prototype); // true 167 | console.log(Person.prototype.__proto__ === Object.prototype); // true 168 | console.log(Person.prototype.constructor === Person); // true 169 | console.log(Person.__proto__ === Function.prototype); // true 170 | console.log(Function.prototype.__proto__ === Object.prototype); // true 171 | ``` 172 | 173 | `foo` 객체의 프로토타입 객체 `Person.prototype` 객체와 `Person()` 생성자 함수의 프로토타입 객체인 `Function.prototype`의 프로토타입 객체는 `Object.prototype` 객체이다. 174 | 175 | 객체 리터럴 방식과 생성자 함수 방식 모두 부모 객체인 `Object.prototype` 객체에서 프로토타입 체인이 끝나기 때문이다. 이때 `Object.prototype` 객체를 프로토타입 체인의 종점이라 한다. 176 | 177 | ## 5. 프로토타입 객체의 확장 178 | 179 | 프로토타입 객체도 객체이므로 일반 객체처럼 프로퍼티를 추가하거나 삭제할 수 있다. 추가하거나 삭제한 프로퍼티는 즉시 프로토타입 체인에 반영된다. 180 | 181 | ```js 182 | function Person(name) { 183 | this.name = name; 184 | } 185 | 186 | var foo = new Person('Lee'); 187 | 188 | Person.prototype.sayHello = function () { 189 | console.log('Hi! my name is ' + this.name); 190 | }; 191 | 192 | foo.sayHello(); 193 | ``` 194 | 195 | 생성자 함수 `Person`은 프로토타입 객체 `Person.prototype`과 `prototype` 프로퍼티에 의해 바인딩되어있다. `Person.prototype` 객체는 일반 객체와 같이 프로퍼티를 추가하거나 삭제할 수 있다. 196 | 197 | `Person.prototype` 객체에 메소드 `sayHello`를 추가하면 `sayHello` 메소드는 프로토타입 체인에 반영된다. 따라서 생성자 함수 `Person`에 의해 생성된 모든 객체는 프로토타입 체인에 의해 부모객체인 `Person.prototype`의 메소드를 사용할 수 있게 된다. 198 | 199 | ## 6. 원시 타입의 확장 200 | 201 | 원시 타입은 객체가 아니므로 프로퍼티나 메소드를 가질수 없다. 하지만 원시 타입으로 프로퍼티나 메소드를 호출할 때 원시 타입과 연관된 객체로 일시적으로 변환되어 프로토타입 객체를 공유하게 된다. 202 | 203 | 원시 타입인 문자열이 객체와 유사하게 동작한다. 204 | 205 | ```js 206 | var str = 'test'; 207 | console.log(typeof str); // string 208 | console.log(str.constructor === String); // true 209 | console.dir(str); // test 210 | 211 | var strObj = new String('test'); 212 | console.log(typeof strObj); // object 213 | console.log(strObj.constructor === String); // true 214 | console.dir(strObj); 215 | // {0: "t", 1: "e", 2: "s", 3: "t", length: 4, __proto__: String, [[PrimitiveValue]]: "test" } 216 | 217 | console.log(str.toUpperCase()); // TEST 218 | console.log(strObj.toUpperCase()); // TEST 219 | ``` 220 | 221 | 원시 타입은 객체가 아니므로 프로퍼티나 메소드를 직접 추가할 수 없다. 222 | 223 | ```js 224 | var str = 'test'; 225 | 226 | // 에러가 발생하지 않는다. 227 | str.myMethod = function () { 228 | console.log('str.myMethod'); 229 | }; 230 | 231 | str.myMethod(); // Uncaught TypeError: str.myMethod is not a function 232 | ``` 233 | 234 | `String` 객체의 프로토타입 객체 `String.prototype`에 메소드를 추가하면 원시 타입, 객체 모두 메소드를 사용할 수 있다. 235 | 236 | ```js 237 | var str = 'test'; 238 | 239 | String.prototype.myMethod = function () { 240 | return 'myMethod'; 241 | }; 242 | 243 | console.log(str.myMethod()); // myMethod 244 | console.log('string'.myMethod()); // myMethod 245 | ``` 246 | 247 | ## 7. 프로토타입 객체의 변경 248 | 249 | 프로토타입은 객체를 생성할 때 결정된다. 결정된 프로토타입 객체는 다른 임의의 객체로 변경할 수 있다. 이것은 부모 객체인 프로토타입을 동적으로 변경할 수 있다는 것을 의미한다. 이러한 특징을 활용하여 객체의 상속을 구현할 수 있다. 250 | 251 | 프로토타입 객체를 변경하면 변경 시점 이전에 생성된 객체는 기존 프로토타입 객체를 `[[Prototype]]`에 바인딩하고 변경 시점 이후에 생성된 객체는 변경된 프로토타입 객체를 `[[Prototype]]`에 바인딩한다. 252 | 253 | ```js 254 | function Person(name) { 255 | this.name = name; 256 | } 257 | 258 | var foo = new Person('Lee'); 259 | 260 | // 프로토타입 객체의 변경 261 | Person.prototype = { gender: 'male' }; 262 | 263 | var bar = new Person('Kim'); 264 | 265 | console.log(foo.gender); // undefined 266 | console.log(bar.gender); // 'male' 267 | 268 | console.log(foo.constructor); // Person() 생성자 함수 269 | console.log(bar.constructor); // Object() 생성자 함수(Object.prototype.constructor) 270 | ``` 271 | 272 | ## 8. 프로토타입 체인 동작 조건 273 | 274 | 객체의 프로퍼티를 참조하는 경우와 해당 객체에 프로퍼티가 없는 경우에 프로토타입 체인이 동작한다. 275 | 276 | 객체의 프로퍼티에 값을 할당하는 경우는 프로토타입 체인이 동작하지 않는다. 객체에 해당 프로퍼티가 있는 경우 값을 재할당하고 해당 프로퍼티가 없는 경우는 해당 객체에 프로퍼티를 동적으로 추가하기 때문이다. 277 | 278 | ## 9. 퀴즈 279 | 280 | ### 9-1. 프로토타입 상속 281 | 282 | ```js 283 | function Dog(name) { 284 | this.name = name; 285 | this.speak = function () { 286 | return 'woof'; 287 | }; 288 | } 289 | 290 | const dog = new Dog('Pogo'); 291 | 292 | Dog.prototype.speak = function () { 293 | return 'arf'; 294 | }; 295 | 296 | console.log(dog.speak()); 297 | ``` 298 | 299 |
300 | 301 | 정답 302 | 303 | > **woof** 304 | 305 | - 해설: 새로운 Dog 인스턴스를 생성할 때마다 speak 프로퍼티에는 "woof"를 반환하는 함수가 할당됩니다. 생성할 때마다 할당되므로 인터프리터는 speak 프로퍼티를 찾기 위해 prototype 체인을 살펴보지 않습니다. 따라서, prototype으로 할당한 speak 메서드는 사용되지 않고 "woof"를 반환합니다. 306 | 307 |
308 | 309 |
310 | 311 | 퀴즈 출처 312 | 313 | - [TypeOfNaN](https://typeofnan.dev/10-javascript-quiz-questions-and-answers) 314 | -------------------------------------------------------------------------------- /4회차/김효진.md: -------------------------------------------------------------------------------- 1 | # 프로토타입 퀴즈 2 | 3 | https://inflearn-quiz.vercel.app/javascript/22-prototype-1 4 | 5 | https://inflearn-quiz.vercel.app/javascript/22-prototype-2 -------------------------------------------------------------------------------- /4회차/병현.md: -------------------------------------------------------------------------------- 1 | ## 객체지향 2 | 3 | > 객체지향 프로그래밍은 객체의 상태(state)를 나타내는 데이터와 상태 데이터를 조작할 수 있는 동작을 하나의 논리적인 단위로 묶어 생각한다. 따라서 객체는 상태 데이터와 동작을 하나의 논리적인 단위로 묶은 복합적인 자료구조이다. 4 | 5 | ```js 6 | const circle = { 7 | radius: 5, // 반지름 8 | 9 | // 원의 지름: 2r 10 | getDiameter() { 11 | return 2 * this.radius; 12 | }, 13 | 14 | // 원의 둘레: 2πr 15 | getPerimeter() { 16 | return 2 * Math.PI * this.radius; 17 | }, 18 | 19 | // 원의 넓이: πrr 20 | getArea() { 21 | return Math.PI * this.radius ** 2; 22 | } 23 | }; 24 | 25 | console.log(circle); 26 | // {radius: 5, getDiameter: ƒ, getPerimeter: ƒ, getArea: ƒ} 27 | 28 | console.log(circle.getDiameter()); // 10 29 | console.log(circle.getPerimeter()); // 31.41592653589793 30 | console.log(circle.getArea()); // 78.53981633974483 31 | ``` 32 | 33 | 34 | 🧐🧐🧐 35 | _과연 이게 객체지향의 모든것일까? 다른면은 없을까?_ [[19 ~ 21#객체지향을 어떻게 이해해야할까]] 36 | 37 | 38 | ## 프로토타입의 상속 39 | 40 | 같은 생성자 함수로 생성된 인스턴스들에게 동일한 메서드를 상속하기 위해서 프로토타입에 메서드를 추가한다. 41 | 42 | ```js 43 | // 생성자 함수 44 | function Circle(radius) { 45 | this.radius = radius; 46 | } 47 | 48 | // Circle 생성자 함수가 생성한 모든 인스턴스가 getArea 메서드를 49 | // 공유해서 사용할 수 있도록 프로토타입에 추가한다. 50 | // 프로토타입은 Circle 생성자 함수의 prototype 프로퍼티에 바인딩되어 있다. 51 | Circle.prototype.getArea = function () { 52 | return Math.PI * this.radius ** 2; 53 | }; 54 | 55 | // 인스턴스 생성 56 | const circle1 = new Circle(1); 57 | const circle2 = new Circle(2); 58 | 59 | // Circle 생성자 함수가 생성한 모든 인스턴스는 부모 객체의 역할을 하는 60 | // 프로토타입 Circle.prototype으로부터 getArea 메서드를 상속받는다. 61 | // 즉, Circle 생성자 함수가 생성하는 모든 인스턴스는 하나의 getArea 메서드를 공유한다. 62 | console.log(circle1.getArea === circle2.getArea); // true 63 | 64 | console.log(circle1.getArea()); // 3.141592653589793 65 | console.log(circle2.getArea()); // 12.566370614359172 66 | ``` 67 | 68 | 69 | ## 프로토타입 체인 70 | 71 | 아래의 코드와 같이 `Person`으로 생성된 `me`객체가 `Person.prototype`뿐만 아니라 `Object.prototype`을 상속받았다는 것을 알 수 있다. 72 | 73 | ```js 74 | function Person(name) { 75 | this.name = name; 76 | } 77 | 78 | // 프로토타입 메서드 79 | Person.prototype.sayHello = function () { 80 | console.log(`Hi! My name is ${this.name}`); 81 | }; 82 | 83 | const me = new Person('Lee'); 84 | 85 | // hasOwnProperty는 Object.prototype의 메서드다. 86 | console.log(me.hasOwnProperty('name')); // true 87 | 88 | Object.getPrototypeOf(me) === Person.prototype; // -> true 89 | 90 | Object.getPrototypeOf(Person.prototype) === Object.prototype; // -> true 91 | ``` 92 | 93 | > 자바스크립트는 객체의 프로퍼티(메서드 포함)에 접근하려고 할 때 해당 객체에 접근하려는 프로퍼티가 없다면 `[[Prototype]]`내부 슬롯의 참조를 따라 자신의 부모 역할을 하는 프로토타입의 프로퍼티를 순차적으로 검색한다. 이를 프로토타입 체인이라 한다. 프로토타입 체인은 자바스크립트가 객체지향 프로그래밍의 상속을 구현하는 메커니즘이다. 94 | 95 | ## 오버라이딩 96 | 97 | 프로토타입 프로퍼티와 같은 이름의 프로퍼티를 인스턴스에 추가하면 프로토타입 체인을 따라 프로토타입 프로퍼티를 검색하여 프로토타입 프로퍼티를 덮어쓰는 것이 아니라 인스턴스 프로퍼티로 추가한다. 아래 코드와 같이 `sayHello` 메서드가 덮어씌워지게 되는데 이걸 **오버라이딩**이라 부르며 프로퍼티가 가려지는 현상을 **프로퍼티 섀도잉**이라한다. 98 | 99 | ```js 100 | const Person = (function () { 101 | // 생성자 함수 102 | function Person(name) { 103 | this.name = name; 104 | } 105 | 106 | // 프로토타입 메서드 107 | Person.prototype.sayHello = function () { 108 | console.log(`Hi! My name is ${this.name}`); 109 | }; 110 | 111 | // 생성자 함수를 반환 112 | return Person; 113 | }()); 114 | 115 | const me = new Person('Lee'); 116 | 117 | // 인스턴스 메서드 118 | me.sayHello = function () { 119 | console.log(`Hey! My name is ${this.name}`); 120 | }; 121 | 122 | // 인스턴스 메서드가 호출된다. 프로토타입 메서드는 인스턴스 메서드에 의해 가려진다. 123 | me.sayHello(); // Hey! My name is Lee 124 | ``` 125 | 126 | ## instanceOf 127 | 128 | 좌변의 인스턴스의 프로토타입 체인에 우변 생성자 함수의 `prototype`이 존재하는지 검사한다. 129 | 130 | ```js 131 | // 생성자 함수 132 | function Person(name) { 133 | this.name = name; 134 | } 135 | 136 | const me = new Person('Lee'); 137 | 138 | // Person.prototype이 me 객체의 프로토타입 체인 상에 존재하므로 true로 평가된다. 139 | console.log(me instanceof Person); // true 140 | 141 | // Object.prototype이 me 객체의 프로토타입 체인 상에 존재하므로 true로 평가된다. 142 | console.log(me instanceof Object); // true 143 | ``` 144 | 145 | 프로토타입 체인에서 검사하기 때문에 아래와 같이 프로토타입을 갈아끼워도 상위의 프로토타입 검사 결과는 `true` 로 나올 수 있다. 146 | 147 | ```js 148 | // 생성자 함수 149 | function Person(name) { 150 | this.name = name; 151 | } 152 | 153 | const me = new Person('Lee'); 154 | 155 | // 프로토타입으로 교체할 객체 156 | const parent = {}; 157 | 158 | // 프로토타입의 교체 159 | Object.setPrototypeOf(me, parent); 160 | 161 | // Person 생성자 함수와 parent 객체는 연결되어 있지 않다. 162 | console.log(Person.prototype === parent); // false 163 | console.log(parent.constructor === Person); // false 164 | 165 | // Person.prototype이 me 객체의 프로토타입 체인 상에 존재하지 않기 때문에 false로 평가된다. 166 | console.log(me instanceof Person); // false 167 | 168 | // Object.prototype이 me 객체의 프로토타입 체인 상에 존재하므로 true로 평가된다. 169 | console.log(me instanceof Object); // true 170 | ``` 171 | 172 | ## 정적 프로퍼티/메서드 173 | 174 | 생성자 함수 객체가 소유한 프로퍼티/메서드를 정적 프로퍼티/메서드라고 한다. 정적 프로퍼티/메서드는 생성된 인스턴스로는 참조/호출 할 수 없다. 175 | 176 | ```js 177 | // 생성자 함수 178 | function Person(name) { 179 | this.name = name; 180 | } 181 | 182 | // 프로토타입 메서드 183 | Person.prototype.sayHello = function () { 184 | console.log(`Hi! My name is ${this.name}`); 185 | }; 186 | 187 | // 정적 프로퍼티 188 | Person.staticProp = 'static prop'; 189 | 190 | // 정적 메서드 191 | Person.staticMethod = function () { 192 | console.log('staticMethod'); 193 | }; 194 | 195 | const me = new Person('Lee'); 196 | 197 | // 생성자 함수에 추가한 정적 프로퍼티/메서드는 생성자 함수로 참조/호출한다. 198 | Person.staticMethod(); // staticMethod 199 | 200 | // 정적 프로퍼티/메서드는 생성자 함수가 생성한 인스턴스로 참조/호출할 수 없다. 201 | // 인스턴스로 참조/호출할 수 있는 프로퍼티/메서드는 프로토타입 체인 상에 존재해야 한다. 202 | me.staticMethod(); // TypeError: me.staticMethod is not a function 203 | ``` 204 | 205 | ## 프로퍼티 열거 206 | 207 | `for ... in` 문은 객체의 키값을 열거하지만 `[[Enumberable]]` 이 `false`인 값은 열거하지 않는다. 208 | ```js 209 | const person = { 210 | name: 'Lee', 211 | address: 'Seoul' 212 | }; 213 | 214 | // in 연산자는 객체가 상속받은 모든 프로토타입의 프로퍼티를 확인한다. 215 | console.log('toString' in person); // true 216 | 217 | // for...in 문도 객체가 상속받은 모든 프로토타입의 프로퍼티를 열거한다. 218 | // 하지만 toString과 같은 Object.prototype의 프로퍼티가 열거되지 않는다. 219 | for (const key in person) { 220 | console.log(key + ': ' + person[key]); 221 | } 222 | 223 | // name: Lee 224 | // address: Seoul 225 | ``` 226 | 227 | 228 | 또한, 심벌 프로퍼티는 열거하지 않는다. 229 | 230 | ```js 231 | const sym = Symbol(); 232 | const obj = { 233 | a: 1, 234 | [sym]: 10 235 | }; 236 | 237 | for (const key in obj) { 238 | console.log(key + ': ' + obj[key]); 239 | } 240 | // a: 1 241 | ``` 242 | 243 | 244 | ## strict mode 245 | 246 | 좀 더 엄격한 문법 체크를 위한 기능으로 `'use strict'`라는 키워드를 사용한다. 247 | 248 | ```js 249 | 'use strict'; 250 | 251 | function foo() { 252 | x = 10; // ReferenceError: x is not defined 253 | } 254 | foo(); 255 | ``` 256 | 257 | _ES6의 모듈은 기본적으로 strict mode이다._ 258 | 259 | https://262.ecma-international.org/6.0/#sec-strict-mode-code 260 | > Module code is always strict mode code. 261 | 262 | ## 원시값과 래퍼객체 263 | 264 | 문자열, 숫자, 불리언 값의 경우 이들 원시값에 대해 마치 객체처럼 마침표 표기법(혹은 대괄호 표기법)으로 접근하면 자바스크립트 엔진이 원시값을 연관된 객체로 변환해 준다. 이 객체를 **래퍼객체**라 부른다. 265 | 266 | ```js 267 | const str = 'hi'; 268 | 269 | // 원시 타입인 문자열이 래퍼 객체인 String 인스턴스로 변환된다. 270 | console.log(str.length); // 2 271 | console.log(str.toUpperCase()); // HI 272 | 273 | // 래퍼 객체로 프로퍼티에 접근하거나 메서드를 호출한 후, 다시 원시값으로 되돌린다. 274 | console.log(typeof str); // string 275 | ``` 276 | 277 | 이후에 사용되지 않는 래퍼객체를 가비지 컬렉터가 수거해간다. 278 | 279 | ```js 280 | // ① 식별자 str은 문자열을 값으로 가지고 있다. 281 | const str = 'hello'; 282 | 283 | // ② 식별자 str은 암묵적으로 생성된 래퍼 객체를 가리킨다. 284 | // 식별자 str의 값 'hello'는 래퍼 객체의 [[StringData]] 내부 슬롯에 할당된다. 285 | // 래퍼 객체에 name 프로퍼티가 동적 추가된다. 286 | str.name = 'Lee'; 287 | 288 | // ③ 식별자 str은 다시 원래의 문자열, 즉 래퍼 객체의 [[StringData]] 내부 슬롯에 할당된 원시값을 갖는다. 289 | // 이때 ②에서 생성된 래퍼 객체는 아무도 참조하지 않는 상태이므로 가비지 컬렉션의 대상이 된다. 290 | 291 | // ④ 식별자 str은 새롭게 암묵적으로 생성된(②에서 생성된 래퍼 객체와는 다른) 래퍼 객체를 가리킨다. 292 | // 새롭게 생성된 래퍼 객체에는 name 프로퍼티가 존재하지 않는다. 293 | console.log(str.name); // undefined 294 | 295 | // ⑤ 식별자 str은 다시 원래의 문자열, 즉 래퍼 객체의 [[StringData]] 내부 슬롯에 할당된 원시값을 갖는다. 296 | // 이때 ④에서 생성된 래퍼 객체는 아무도 참조하지 않는 상태이므로 가비지 컬렉션의 대상이 된다. 297 | console.log(typeof str, str); 298 | ``` 299 | 300 | 301 | ## 이야기거리 302 | 303 | ### 객체지향을 어떻게 이해해야할까 304 | 305 | 객체지향의 선구자 앨런 케이는 메시징이라는 관점을 중요시 했다 306 | https://velog.io/@eddy_song/alan-kay-OOP 307 | -------------------------------------------------------------------------------- /5회차/README.md: -------------------------------------------------------------------------------- 1 | # 5회차 2 | 3 | **실행 컨텍스트 참고 링크** 4 | https://www.zerocho.com/category/JavaScript/post/609778ad9f879900043a8728 5 | 6 | **스코프 관련 퀴즈 (this 공부하면서 다시 언급된 퀴즈!)** 7 | https://github.com/hy57in/study-javascript-deep-dive/blob/main/2회차/김민지.md 8 | 9 | **[10분 테코톡] 하루의 실행 컨텍스트** 10 | https://www.youtube.com/watch?v=EWfujNzSUmw -------------------------------------------------------------------------------- /5회차/김록원.md: -------------------------------------------------------------------------------- 1 | # this 2 | 3 | 메서드는 프로퍼티를 참조하고 변경할 수 있어야 한다. 이때 자신이 속한 객체의 프로퍼티를 참조하려면 먼저 **자신이 속한 객체를 가리키는 식별자를 참조할 수 있어야한다.** 4 | 5 | > **💡 this란?** 6 | 자신이 속한 객체/자신이 생성할 인스턴스를 가리키는 자기 참조 변수다. 7 | this를 통해 프로퍼티나 메서드를 참조할 수 있다. 8 | 9 | ### this의 생성 10 | `this`는 js엔진에 의해 암묵적으로 생성되며, 코드 어디서든 참조할 수 있다. 11 | 단, **this가 가리키는 값(this 바인딩)은 함수 호출 방식에 의해 동적으로 결정된다.** 12 | (자바나 C++ 같은 클래스 기반 언어에서 this는 언제나 클래스가 생성하는 인스턴스를 가리킨다.) 13 | 14 | this는 객체의 프로퍼티, 메서드를 참조하기 위한 자기 참조이므로 객체의 메서드 내부, 생성자 함수 내부에서만 의미가 있다. 일반 함수에서의 this는 의미가없다(중첩 함수에서도!) 15 | 16 |
17 | 18 | ## 함수 호출방식에 따른 this 바인딩 19 | 20 | 위에서 얘기했든 this 바인딩은 함수가 어떻게 호출되었는지에 따라 동적으로 결정된다. 21 | > **💡 렉시컬 스코프와 this 바인딩 결정 시기** 22 | 렉시컬 스코프 -> 함수의 상위 스코프를 결정 방식 23 | `렉시컬 스코프`는 함수 정의가 평가되어 함수 객체가 생성되는 시점에 상위 스코프를 결정 24 | `this 바인딩`은 함수 호출 시점에 결정 25 | 26 |
27 | 28 | ### 일반 함수 호출 29 | 1. 일반 함수로 호출시 내부에 사용되는 this는 전역객체가 바인딩된다. 30 | 전역함수 뿐만아니라 중첩함수를 일반함수로 호출해도 중첩함수 내에 this는 전역객체가 바인딩된다. 31 | ```js 32 | function foo() { 33 | console.log("foo's this: ", this); // window 34 | function bar() { 35 | console.log("bar's this: ", this); // window 36 | } 37 | bar(); 38 | } 39 | foo(); 40 | ``` 41 | 2. strict mode가 적용된 일반 함수 내부의 this에는 undefined가 바인됭된다. 42 | ```js 43 | function foo() { 44 | 'use strict'; 45 | 46 | console.log("foo's this: ", this); // undefined 47 | function bar() { 48 | console.log("bar's this: ", this); // undefined 49 | } 50 | bar(); 51 | } 52 | foo(); 53 | ``` 54 | 3. 메서드 내에 정의함 중첩함수도 일반함수로 호출한다면 this에는 전역객체가 바인딩된다. 55 | ```js 56 | // var 키워드로 선언한 전역 변수 value는 전역 객체의 프로퍼티다. 57 | var value = 1; 58 | // const 키워드로 선언한 전역 변수 value는 전역 객체의 프로퍼티가 아니다. 59 | // const value = 1; 60 | 61 | const obj = { 62 | value: 100, 63 | foo() { 64 | console.log("foo's this: ", this); // {value: 100, foo: ƒ} 65 | console.log("foo's this.value: ", this.value); // 100 66 | 67 | // 메서드 내에서 정의한 중첩 함수 68 | function bar() { 69 | console.log("bar's this: ", this); // window 70 | console.log("bar's this.value: ", this.value); // 1 71 | } 72 | 73 | // 메서드 내에서 정의한 중첩 함수도 일반 함수로 호출되면 중첩 함수 내부의 this에는 전역 객체가 바인딩된다. 74 | bar(); 75 | } 76 | }; 77 | 78 | obj.foo(); 79 | ``` 80 | 81 |
82 | 83 | 함수 내 지역변수에 this 바인딩을 할당해줌으로써 중첩함수나 콜백 함수의 this 바인딩을 메서드의 this 바인딩과 일치시켜준다. 84 | ```js 85 | var value = 1; 86 | 87 | const obj = { 88 | value: 100, 89 | foo() { 90 | // this 바인딩(obj)을 변수 that에 할당한다. 91 | const that = this; 92 | 93 | // 콜백 함수 내부에서 this 대신 that을 참조한다. 94 | setTimeout(function () { 95 | console.log(that.value); // 100 96 | }, 100); 97 | } 98 | }; 99 | 100 | obj.foo(); 101 | ``` 102 | 103 | 뿐만 아니라 화살표 함수에서의 this는 상위 스코프의 this를 가리킨다. 104 | ```js 105 | var value = 1; 106 | 107 | const obj = { 108 | value: 100, 109 | foo() { 110 | // 화살표 함수 내부의 this는 상위 스코프의 this를 가리킨다. 111 | setTimeout(() => console.log(this.value), 100); // 100 112 | } 113 | }; 114 | 115 | obj.foo(); 116 | ``` 117 | 118 |
119 | 120 | > 🧐 클래스 인스턴스의 메서드 내부에 중첩된 내부함수를 일반 함수 호출을 한다면? 121 | this는 전역객체인가? class 인스턴스인가? 122 | 123 |
124 | 125 | ### 메서드 호출 126 | 메서드 내부의 this에는 메서드를 호출한 객체, 즉 메서드를 호출할 때 지정한 객체가 바인딩 된다. 127 | (메서드 내부의 this는 메서드를 호출한 객체에 바인딩된다는 것) 128 | ```js 129 | const person = { 130 | name: 'Lee', 131 | getName() { 132 | // 메서드 내부의 this는 메서드를 호출한 객체에 바인딩된다. 133 | return this.name; 134 | } 135 | }; 136 | 137 | // 메서드 getName을 호출한 객체는 person이다. 138 | console.log(person.getName()); // Lee 139 | ``` 140 | 141 | person 객체의 getName 메서드는 getName 프로퍼티에 바인됭된 함수다. 142 | 즉 getName 프로퍼티가 가리키는 함수는 객체는 person 객체에 포함된 것이 아닌 독립적으로 존재하는 별도의 객체다. 143 | 144 |
145 | 146 | ### 생성자 함수 호출 147 | 생성자 함수 내부의 this에는 생성자 함수가 (미래에) 생성할 인스턴스가 바인딩된다. 148 | 149 | ```js 150 | // 생성자 함수 151 | function Circle(radius) { 152 | // 생성자 함수 내부의 this는 생성자 함수가 생성할 인스턴스를 가리킨다. 153 | this.radius = radius; 154 | this.getDiameter = function () { 155 | return 2 * this.radius; 156 | }; 157 | } 158 | 159 | // 반지름이 5인 Circle 객체를 생성 160 | const circle1 = new Circle(5); 161 | // 반지름이 10인 Circle 객체를 생성 162 | const circle2 = new Circle(10); 163 | 164 | console.log(circle1.getDiameter()); // 10 165 | console.log(circle2.getDiameter()); // 20 166 | ``` 167 | 168 |
169 | 170 | ### Function.prototype.apply/call/bind 메서드에 의한 간접 호출 171 | apply, call, bind 메서드는 Function.prototyp의 메서드이다. 172 | 173 | apply와 call 메서드는 this로 사용할 객체와 인수 리스트를 인수로 전달받아서 함수를 호출한다. 174 | 이 둘의 본질적인 기능은 함수를 호출하는 것이다.(즉, this로 사용할 것을 인자로 받은 후 함수를 호출) 175 | 176 | ```js 177 | function getThisBinding() { 178 | return this; 179 | } 180 | 181 | // this로 사용할 객체 182 | const thisArg = { a: 1 }; 183 | 184 | console.log(getThisBinding()); // window 185 | 186 | // getThisBinding 함수를 호출하면서 인수로 전달한 객체를 getThisBinding 함수의 this에 바인딩한다. 187 | console.log(getThisBinding.apply(thisArg)); // {a: 1} 188 | console.log(getThisBinding.call(thisArg)); // {a: 1} 189 | ``` 190 | 191 |
192 | 193 | bind 메서드는 apply, call 메서드와 달리 함수를 호출하지 않는다. 다만 첫번째 인수로 전달한 값으로 this 바인딩이 교체된 함수를 새롭게 생성해 반환한다. 194 | ```js 195 | function getThisBinding() { 196 | return this; 197 | } 198 | 199 | // this로 사용할 객체 200 | const thisArg = { a: 1 }; 201 | 202 | // bind 메서드는 첫 번째 인수로 전달한 thisArg로 this 바인딩이 교체된 203 | // getThisBinding 함수를 새롭게 생성해 반환한다. 204 | console.log(getThisBinding.bind(thisArg)); // getThisBinding 205 | // bind 메서드는 함수를 호출하지는 않으므로 명시적으로 호출해야 한다. 206 | console.log(getThisBinding.bind(thisArg)()); // {a: 1} 207 | ``` 208 | 209 |
210 | 211 | # 실행 컨텍스트 212 | 컨텍스트는 js의 동작 원리를 담고 있는 핵심 개념이다. 213 | js는 소스코드를 4가지 타입을 구분하고 4가지 타입의 소스코드는 실행 컨텍스를 생성한다. 214 | (소스코드의 타입에 따라 실행 컨텍스트를 생성하는 과정과 관리 내용이 다르다.) 215 | 216 | `전역코드` (전역에 존재하는 소스코드) 전역에 정의된 함수, 클래스 등의 내부코드는 포함되지 않는다. 217 | `함수코드` (함수 내부에 존재하는 소스코드) 함수 내부에 중첩된 함수, 클래스 등의 내부 코드는 포함되지 않는다. 218 | `eval 코드` 빌트인 전역 함수인 eval 함수에 인수로 전달되어 실행되는 소스코드를 말한다. 219 | `모듈 코드` (모듈 내부에 존재하는 소스코드를 말한다) 모듈 내부의 함수, 클래스 등의 내부코드는 포함되지 않는다. 220 | 221 | **전역코드** 222 | var 키워드로 선언된 전역 변수, 함수 선언문으로 정의된 전역 함수를 전역 객체의 프로퍼티와 메서드로 바인딩하고 참조하기 위해 전역 객체와 연결되어야 한다. 이를 위해 전역 코드가 평가되면 전역 실행 컨텍스트가 생성된다. 223 | 224 | **함수코드** 225 | 지역 스코프를 생성하고 지역변수, 매개변수, arguments 객체를 관리해야 한다. 이렇게 생성된 지역 스코프를 전역 스코프에서 시작하는 스코프 체인의 일원으로 연결해야한다. 이를 위해 함수 코드가 평가되면 함수 실행 컨텍스트가 생성된다. 226 | 227 | **모듈코드** 228 | 모듈별로 독립적인 모듈 스코프를 생성한다. 이를 위해 모듈 코드가 평가되면 모듈 실행 컨텍스트가 생서된다. 229 | 230 |
231 | 232 | ### 소스코드의 평가와 실행 233 | 모든 소스코드는 실행에 앞서 평가 과정을 커쳐 코드 실행을 위한 준비를 한다. 234 | 즉, js 엔진은 `소스코드 평가`와 `소스코드 실행` 과정으로 나누어 처리한다. 235 | 236 | `소스코드 평가`에서는 **실행 컨텍스트를 생성**하고 변수, 함수 등의 선언문만 먼저 실행하여 생성된 변수나 함수 **식별자를 키로 실행 컨텍스트가 관리하는 스코프에 등록**한다. 237 | 238 | `소스코드 실행` 선언문을 제외한 소스코드가 순차적으로 실행된다.(런타임) 이때 **필요한 정보(변수나 함수의 참조)를 실행 컨텍스트가 관리하는 스코프에서 검색해서 취득**한다. 변수 값의 변경 등 **소스코드 실행결과는 다시 실행 컨텍스트가 관리하는 스코프에 등록**된다. 239 | 240 |
241 | 242 | ### 실행 컨텍스트의 역할 243 | 실행 컨텍스트는 **소스코드를 실행하는 데 필요한 환경을 제공해주고 코드의 실행 결과를 실제로 관리하는 영역이다.** 244 | 코드가 실행되려면 스코프, 식별자, 코드 실행 순서 등의 관리가 필요하다. 245 | 즉, 아래와 같은 역할을 한다. 246 | - 식별자를 스코프를 구분하여 등록하고 상태 변화를 지속적으로 관리 247 | - 스코프는 중첩 관계에 의해 스코프 체인을 형성(상위 스코프로 이동하여 식별자 검색 가능) 248 | - 실행 순서를 변경(함수 호출에 의한 실행 순서 변경)할 수 있어야하며 돌아가기도 가능 249 | 250 | **식별자와 스코프**는 `실행 컨텍스트의 렉시컬 환경`으로 관리하고 251 | **코드 실행 순서**는 `실행 컨텍스트 스택으로 관리`한다. 252 | 253 |
254 | 255 | ### 렉시컬 환경 256 | - 실행 컨텍스트를 구성하는 컴포넌트이다. 257 | - 식별자와 식별자에 바인딩된 값, 상위 스코프에 대한 참조를 기록하는 자료구조.(키와 값을 갖는 객체 형태) 258 | - 스코프를 구분하여 식별자를 등록하고 관리하는 저장소 역할을 하는 렉시컬 스코프의 실체다. 259 | 260 |
261 | 262 | ### 실행 컨텍스트의 생성과 식별자 검색 과정 263 | 아래 코드로 실행 컨텍스트의 생성, 코드 실행 결과 관리, 그리고 실행 컨텍스트를 통해 식별자를 검색하는지 보자. 264 | ```js 265 | var x = 1; 266 | const y = 2; 267 | 268 | function foo (a) { 269 | var x = 3; 270 | const y = 4; 271 | 272 | function bar (b) { 273 | const z = 5; 274 | console.log(a + b + x + y + z); 275 | } 276 | bar(10); 277 | } 278 | 279 | foo(20); // 42 280 | ``` 281 | 282 | 1. 전역 객체 생성 283 | 2. 전역 코드 평가 284 | - 전역 실행 컨텍스트 생성 285 | - 전역 섹시컬 환경 생성 286 | - 전역 환경 레코드 생성(객체 환경, 선언적 환경 레코드 생성) 287 | - this 바인딩 288 | - 외부 렉시컬 환경에 대한 참조 결정 289 | 3. 전역 코드 실행 290 | 4. foo 함수 코드 평가 291 | 5. foo 함수 코드 실행 292 | 6. bar 함수 코드 평가 293 | 7. bar 함수 코드 실행 294 | 8. bar 함수 코드 실행 종료 295 | 8. foo 함수 코드 실행 종료 296 | 9. 전역 코드 실행 종료 297 | -------------------------------------------------------------------------------- /5회차/박상범.md: -------------------------------------------------------------------------------- 1 | # 22장 this 2 | 3 | 객체 리터럴 방식으로 생성한 객체의 경우 메서드 내부에서 메서드 자신이 속한 객체를 가리키는 식별자를 재귀적으로 참조할 수 있다, 4 | 5 | ```tsx 6 | const circle = { 7 | // 프로퍼티: 객체 고유의 상태 데이터 8 | radius: 5, 9 | // 메서드: 상태 데이터를 참조하고 조작하는 동작 10 | getDiameter() { 11 | // 이 메서드가 자신이 속한 객체의 프로퍼티나 다른 메서드를 참조하려면 12 | // 자신이 속한 객체인 circle을 참조할 수 있어야 한다. 13 | return 2 * circle.radius; 14 | } 15 | }; 16 | 17 | console.log(circle.getDiameter()); // 10 18 | ``` 19 | 20 | getDiameter 메서드 내에서 메서드 자신이 속한 객체를 가리키는 식별자 circle을 참조하고 있다. 이 참조표현식이 평가되는 시점은 getDiameter 메서드가 호출되어 함수 몸체가 실행되는 시점이다. 21 | 22 | 위 예제의 객체 리터럴은 circle 변수에 할당되기 직전에 평가된다. 23 | 따라서 getDiameter 메서드가 호출되는 시점에는 이미 객체 리터럴의 평가가 완료되어 참조할 수 있는거다. 24 | 25 | 하지만 자기 자신이 속한 객체를 재귀적으로 참조하는 방식은 일반적이지 않고 바람직하지도 않다. 26 | 27 | ```tsx 28 | function Circle(radius) { 29 | // 이 시점에는 생성자 함수 자신이 생성할 인스턴스를 가리키는 식별자를 알 수 없다. 30 | ????.radius = radius; 31 | // Circle { radius: radius } 32 | } 33 | 34 | Circle.prototype.getDiameter = function () { 35 | // 이 시점에는 생성자 함수 자신이 생성할 인스턴스를 가리키는 식별자를 알 수 없다. 36 | return 2 * ????.radius; 37 | }; 38 | 39 | // 생성자 함수로 인스턴스를 생성하려면 먼저 생성자 함수를 정의해야 한다. 40 | const circle = new Circle(5); 41 | ``` 42 | 43 | 생성자 함수 내부에서는 프로퍼티 또는 메서드를 추가하기 위해 자신이 생성할 인스턴스를 참조할 수 있어야한다. 하지만 생성자 함수에 의한 객체 생성 방식은 먼저 생성자 함수를 정의한 이후 new 연산자와 함께 생성자 함수를 호출하는 단계가 추가로 필요하다. 다시 말해, 생성자 함수로 인스턴스를 생성하려면 먼저 생성자 함수가 존재해야 한다. 44 | 45 | 생성자 함수를 정의하는 시점에는 아직 인스턴스를 생성하기 이전이므로 생성자 함수가 생성할 인스턴스를 가리키는 식별자를 알 수 없다. 따라서 자신이 속한 객체 또는 자신이 생성할 인스턴스를 가리키는 특수한 식별자가 필요하다. 이를 위해 자바스크립트는 this라는 특수한 식별자를 제공한다. 46 | 47 | **this는 자신이 속한 객체 또는 자신이 생성할 인스턴스를 가리키는 자기 참조 변수다.** 48 | 49 | ### this 바인딩 50 | 51 | 바인딩이란 식별자와 값을 연결하는 과정을 의미한다. 52 | 53 | ```tsx 54 | // 객체 리터럴 55 | const circle = { 56 | radius: 5, 57 | getDiameter() { 58 | // this는 메서드를 호출한 객체를 가리킨다. 59 | return 2 * this.radius; 60 | } 61 | }; 62 | 63 | console.log(circle.getDiameter()); // 10 64 | ``` 65 | 66 | ```tsx 67 | // 생성자 함수 68 | function Circle(radius) { 69 | // this는 생성자 함수가 생성할 인스턴스를 가리킨다. 70 | this.radius = radius; 71 | } 72 | 73 | Circle.prototype.getDiameter = function () { 74 | // this는 생성자 함수가 생성할 인스턴스를 가리킨다. 75 | return 2 * this.radius; 76 | }; 77 | 78 | // 인스턴스 생성 79 | const circle = new Circle(5); 80 | console.log(circle.getDiameter()); // 10 81 | ``` 82 | 83 | 여기서 생성자 함수 내부의 this는 생성자 함수가 생성할 인스턴스를 가리킨다. 84 | 85 | 자바스크립트 this는 함수가 호출되는 방식에 따라 this가 바인딩될 값 즉, this 바인딩이 동적으로 결정된다. 86 | 87 | strict mode가 적용된 일반 함수 내부의 this에는 undefined가 바인딩된다. 일반 함수 내부에서 this를 사용할 필요가 없기 때문이다. 88 | 89 | ## 22.2 함수 호출 방식과 this 바인딩 90 | 91 | this 바인딩은 함수 호출 방식, 즉 함수가 어떻게 호출되었는지에 따라 동적으로 결정됩니다. 92 | 93 | 함수 호출 방식 94 | 95 | 1. 일반 함수 호출 96 | 2. 메서드 호출 97 | 3. 생성자 함수 호출 98 | 4. Function.prototype.apply/call/bind 메서드에 의한 간접 호출 99 | 100 | ```tsx 101 | // this 바인딩은 함수 호출 방식에 따라 동적으로 결정된다. 102 | const foo = function () { 103 | console.dir(this); 104 | }; 105 | 106 | // 동일한 함수도 다양한 방식으로 호출할 수 있다. 107 | 108 | // 1. 일반 함수 호출 109 | // foo 함수를 일반적인 방식으로 호출 110 | // foo 함수 내부의 this는 전역 객체 window를 가리킨다. 111 | foo(); // window 112 | 113 | // 2. 메서드 호출 114 | // foo 함수를 프로퍼티 값으로 할당하여 호출 115 | // foo 함수 내부의 this는 메서드를 호출한 객체 obj를 가리킨다. 116 | const obj = { foo }; 117 | obj.foo(); // obj 118 | 119 | // 3. 생성자 함수 호출 120 | // foo 함수를 new 연산자와 함께 생성자 함수로 호출 121 | // foo 함수 내부의 this는 생성자 함수가 생성한 인스턴스를 가리킨다. 122 | new foo(); // foo {} 123 | 124 | // 4. Function.prototype.apply/call/bind 메서드에 의한 간접 호출 125 | // foo 함수 내부의 this는 인수에 의해 결정된다. 126 | const bar = { name: 'bar' }; 127 | 128 | foo.call(bar); // bar 129 | foo.apply(bar); // bar 130 | foo.bind(bar)(); // bar 131 | ``` 132 | 133 | ### 22.2.1 일반 함수 호출 134 | 135 | ```tsx 136 | function foo() { 137 | console.log("foo's this: ", this); // window 138 | function bar() { 139 | console.log("bar's this: ", this); // window 140 | } 141 | bar(); 142 | } 143 | foo(); 144 | ``` 145 | 146 | 중첩함수를 일반 함수로 호출하면 함수 내부의 this에는 전역객체가 바인딩 된다. 147 | 148 | ```tsx 149 | // var 키워드로 선언한 전역 변수 value는 전역 객체의 프로퍼티다. 150 | var value = 1; 151 | // const 키워드로 선언한 전역 변수 value는 전역 객체의 프로퍼티가 아니다. 152 | // const value = 1; 153 | 154 | const obj = { 155 | value: 100, 156 | foo() { 157 | console.log("foo's this: ", this); // {value: 100, foo: ƒ} 158 | console.log("foo's this.value: ", this.value); // 100 159 | 160 | // 메서드 내에서 정의한 중첩 함수 161 | function bar() { 162 | console.log("bar's this: ", this); // window 163 | console.log("bar's this.value: ", this.value); // 1 164 | } 165 | 166 | // 메서드 내에서 정의한 중첩 함수도 일반 함수로 호출되면 중첩 함수 내부의 this에는 전역 객체가 바인딩된다. 167 | bar(); 168 | } 169 | }; 170 | 171 | obj.foo(); 172 | ``` 173 | 174 | 콜백함수가 일반 함수로 호출된다면 콜백 함수 내부의 this에도 전역 객체가 바인딩 된다. 175 | 176 | ```tsx 177 | var value = 1; 178 | 179 | const obj = { 180 | value: 100, 181 | foo() { 182 | console.log("foo's this: ", this); // {value: 100, foo: ƒ} 183 | // 콜백 함수 내부의 this에는 전역 객체가 바인딩된다. 184 | setTimeout(function () { 185 | console.log("callback's this: ", this); // window 186 | console.log("callback's this.value: ", this.value); // 1 187 | }, 100); 188 | } 189 | }; 190 | 191 | obj.foo(); 192 | ``` 193 | 194 | **이처럼 일반 함수로 호출된 모든 함수 (중첩함수, 콜백 함수 포함) 내부의 this 에는 전역 객체가 바인딩 된다.** 195 | 196 | 외부함수인 메서드와 중첩함수 또는 콜백함수의 this가 일치하지 않는다는 것은 중첩 함수 또는 콜백함수를 헬퍼 함수로 동작하기 어렵게 만든다. 197 | 198 | ### 메서드 내부의 중첩 함수나 콜백 함수의 this 바인딩을 메서드의 this 바인딩과 일치시키기 위한 방법 199 | 200 | ```tsx 201 | var value = 1; 202 | 203 | const obj = { 204 | value: 100, 205 | foo() { 206 | 207 | // 방법 1. this 바인딩(obj)을 변수 that에 할당한다. 208 | const that = this; 209 | // 콜백 함수 내부에서 this 대신 that을 참조한다. 210 | setTimeout(function () { 211 | console.log(that.value); // 100 212 | }, 100); 213 | 214 | // 방법 2. 콜백 함수에 명시적으로 this를 바인딩한다. 215 | setTimeout(function () { 216 | console.log(that.value); // 100 217 | }.bind(this), 100); 218 | 219 | // 방법 3. 화살표 함수 내부의 this는 상위 스코프의 this를 가리킨다. 220 | const that = this; 221 | // 콜백 함수 내부에서 this 대신 that을 참조한다. 222 | setTimeout(() => console.log(this.value), 100); // 100 223 | } 224 | }; 225 | 226 | obj.foo(); 227 | ``` 228 | 229 | ## 22.2.2 메서드 호출 230 | 231 | ```tsx 232 | const person = { 233 | name: 'Lee', 234 | getName() { 235 | // 메서드 내부의 this는 메서드를 호출한 객체에 바인딩된다. 236 | return this.name; 237 | } 238 | }; 239 | 240 | // 메서드 getName을 호출한 객체는 person이다. 241 | console.log(person.getName()); // Lee 242 | ``` 243 | 244 | ```tsx 245 | const anotherPerson = { 246 | name: 'Kim' 247 | }; 248 | // getName 메서드를 anotherPerson 객체의 메서드로 할당 249 | anotherPerson.getName = person.getName; 250 | 251 | // getName 메서드를 호출한 객체는 anotherPerson이다. 252 | console.log(anotherPerson.getName()); // Kim 253 | 254 | // getName 메서드를 변수에 할당 255 | const getName = person.getName; 256 | 257 | // getName 메서드를 일반 함수로 호출 258 | console.log(getName()); // '' 259 | // 일반 함수로 호출된 getName 함수 내부의 this.name은 브라우저 환경에서 window.name과 같다. 260 | // 브라우저 환경에서 window.name은 브라우저 창의 이름을 나타내는 빌트인 프로퍼티이며 기본값은 ''이다. 261 | // Node.js 환경에서 this.name은 undefined다. 262 | ``` 263 | 264 | ## 22.2.3 생성자 함수 호출 265 | 266 | 생성자 함수 내부의 this에는 생성자 함수가 미래에 생성할 인스턴스가 바인딩 된다. 267 | 268 | ```tsx 269 | // 생성자 함수 270 | function Circle(radius) { 271 | // 생성자 함수 내부의 this는 생성자 함수가 생성할 인스턴스를 가리킨다. 272 | this.radius = radius; 273 | this.getDiameter = function () { 274 | return 2 * this.radius; 275 | }; 276 | } 277 | 278 | // 반지름이 5인 Circle 객체를 생성 279 | const circle1 = new Circle(5); 280 | // 반지름이 10인 Circle 객체를 생성 281 | const circle2 = new Circle(10); 282 | 283 | console.log(circle1.getDiameter()); // 10 284 | console.log(circle2.getDiameter()); // 20 285 | ``` 286 | 287 | 만약 new 연산자와 함께 생성자 함수를 호출하지 않으면 생성자 함수가 아니라 일반 함수로 동작한다. 288 | 289 | ```tsx 290 | // new 연산자와 함께 호출하지 않으면 생성자 함수로 동작하지 않는다. 즉, 일반적인 함수의 호출이다. 291 | const circle3 = Circle(15); 292 | 293 | // 일반 함수로 호출된 Circle에는 반환문이 없으므로 암묵적으로 undefined를 반환한다. 294 | console.log(circle3); // undefined 295 | 296 | // 일반 함수로 호출된 Circle 내부의 this는 전역 객체를 가리킨다. 297 | console.log(radius); // 15 298 | ``` 299 | 300 | ## 22.2.4 Function.prototype.apply/call/bind 메서드에 의한 간접 호출 301 | 302 | apply와 call 메서드의 본질적인 기능은 함수를 호출하는 것이다. 303 | 304 | ```tsx 305 | function getThisBinding() { 306 | console.log(arguments); 307 | return this; 308 | } 309 | 310 | // this로 사용할 객체 311 | const thisArg = { a: 1 }; 312 | 313 | // getThisBinding 함수를 호출하면서 인수로 전달한 객체를 getThisBinding 함수의 this에 바인딩한다. 314 | 315 | // apply 메서드는 호출할 함수의 인수를 배열로 묶어 전달한다. 316 | console.log(getThisBinding.apply(thisArg, [1, 2, 3])); 317 | // Arguments(3) [1, 2, 3, callee: ƒ, Symbol(Symbol.iterator): ƒ] 318 | // {a: 1} 319 | 320 | // call 메서드는 호출할 함수의 인수를 쉼표로 구분한 리스트 형식으로 전달한다. 321 | console.log(getThisBinding.call(thisArg, 1, 2, 3)); 322 | // Arguments(3) [1, 2, 3, callee: ƒ, Symbol(Symbol.iterator): ƒ] 323 | // {a: 1} 324 | ``` 325 | 326 | apply와 call 메서드의 대표적인 용도는 arguments 개체와 같은 유사배열객체에 배열 메서드를 사용하는 경우다. 327 | 328 | arguments 객체는 배열이 아니기 떄문에 Array.prototype.slice 같은 배열의 메서드를 사용할 수 없으나 apply와 call 메서드를 이용하면 가능하다. 329 | 330 | ```tsx 331 | function convertArgsToArray() { 332 | console.log(arguments); 333 | 334 | // arguments 객체를 배열로 변환 335 | // Array.prototype.slice를 인수없이 호출하면 배열의 복사본을 생성한다. 336 | const arr = Array.prototype.slice.call(arguments); 337 | // const arr = Array.prototype.slice.apply(arguments); 338 | console.log(arr); 339 | 340 | return arr; 341 | } 342 | 343 | convertArgsToArray(1, 2, 3); // [1, 2, 3] 344 | ``` 345 | 346 | # 23. 실행 컨텍스트 347 | 348 | 따로 설명 349 | -------------------------------------------------------------------------------- /5회차/이병현.md: -------------------------------------------------------------------------------- 1 | ## this 2 | 3 | 자신이 속한 객체를 가리키는 식별자 4 | 5 | ```js 6 | // 생성자 함수 7 | function Circle(radius) { 8 | // this는 생성자 함수가 생성할 인스턴스를 가리킨다. 9 | this.radius = radius; 10 | } 11 | 12 | Circle.prototype.getDiameter = function () { 13 | // this는 생성자 함수가 생성할 인스턴스를 가리킨다. 14 | return 2 * this.radius; 15 | }; 16 | 17 | // 인스턴스 생성 18 | const circle = new Circle(5); 19 | console.log(circle.getDiameter()); // 10 20 | ``` 21 | 22 | `this` 바인딩은 함수 호출 방식, 함수가 어떻게 호출되었는지에 따라 동적으로 결정된다. 23 | 24 | 함수를 호출하는 방식은 다음과 같다. 25 | 26 | - 일반 함수 호출 27 | - 메서드 호출 28 | - 생성자 함수 호출 29 | - `Function.prototype.apply/call/bind`에 의한 간접 호출 30 | 31 | 32 | ### 일반 함수 호출/메서드 호출 33 | 34 | 일반 함수로 호출하면 함수 내부의 `this`에는 전역 객체가 바인딩된다. 35 | 36 | 메서드 내에서 정의한 중첩 함수도 일반 함수로 호출되면 중첩 함수 내부의 `this`에는 전역 객체가 바인딩된다. 37 | 38 | 39 | ```js 40 | // var 키워드로 선언한 전역 변수 value는 전역 객체의 프로퍼티다. 41 | var value = 1; 42 | // const 키워드로 선언한 전역 변수 value는 전역 객체의 프로퍼티가 아니다. 43 | // const value = 1; 44 | 45 | const obj = { 46 | value: 100, 47 | foo() { 48 | console.log("foo's this: ", this); // {value: 100, foo: ƒ} 49 | console.log("foo's this.value: ", this.value); // 100 50 | 51 | // 메서드 내에서 정의한 중첩 함수 52 | function bar() { 53 | console.log("bar's this: ", this); // window 54 | console.log("bar's this.value: ", this.value); // 1 55 | } 56 | 57 | // 메서드 내에서 정의한 중첩 함수도 일반 함수로 호출되면 중첩 함수 내부의 this에는 전역 객체가 바인딩된다. 58 | bar(); 59 | } 60 | }; 61 | 62 | obj.foo(); 63 | ``` 64 | 65 | 66 | ### 생성자 함수 호출 67 | 68 | 생성자 함수 내부의 `this`에는 생성자 함수가 (미래에) 생성할 인스턴스가 바인딩된다. 69 | 70 | ```js 71 | // 생성자 함수 72 | function Circle(radius) { 73 | // 생성자 함수 내부의 this는 생성자 함수가 생성할 인스턴스를 가리킨다. 74 | this.radius = radius; 75 | this.getDiameter = function () { 76 | return 2 * this.radius; 77 | }; 78 | } 79 | 80 | // 반지름이 5인 Circle 객체를 생성 81 | const circle1 = new Circle(5); 82 | // 반지름이 10인 Circle 객체를 생성 83 | const circle2 = new Circle(10); 84 | 85 | console.log(circle1.getDiameter()); // 10 86 | console.log(circle2.getDiameter()); // 20 87 | ``` 88 | 89 | ### `Function.prototype.apply/call/bind`에 의한 간접 호출 90 | 91 | `apply, call`메서드는 `this`로 사용할 객체와 인수 리스트를 인수로 받아 함수를 호출한다. 92 | 93 | `bind`의 경우 함수를 호출하지 않고`this` 로 사용할 객체만 전달한다. 94 | 95 | ```js 96 | function getThisBinding() { 97 | console.log(arguments); 98 | return this; 99 | } 100 | 101 | // this로 사용할 객체 102 | const thisArg = { a: 1 }; 103 | 104 | // getThisBinding 함수를 호출하면서 인수로 전달한 객체를 getThisBinding 함수의 this에 바인딩한다. 105 | // apply 메서드는 호출할 함수의 인수를 배열로 묶어 전달한다. 106 | console.log(getThisBinding.apply(thisArg, [1, 2, 3])); 107 | // Arguments(3) [1, 2, 3, callee: ƒ, Symbol(Symbol.iterator): ƒ] 108 | // {a: 1} 109 | 110 | // call 메서드는 호출할 함수의 인수를 쉼표로 구분한 리스트 형식으로 전달한다. 111 | console.log(getThisBinding.call(thisArg, 1, 2, 3)); 112 | // Arguments(3) [1, 2, 3, callee: ƒ, Symbol(Symbol.iterator): ƒ] 113 | // {a: 1} 114 | 115 | 116 | // this로 사용할 객체 117 | const thisArg = { a: 1 }; 118 | 119 | // bind 메서드는 첫 번째 인수로 전달한 thisArg로 this 바인딩이 교체된 120 | // getThisBinding 함수를 새롭게 생성해 반환한다. 121 | console.log(getThisBinding.bind(thisArg)); // getThisBinding 122 | // bind 메서드는 함수를 호출하지는 않으므로 명시적으로 호출해야 한다. 123 | console.log(getThisBinding.bind(thisArg)()); // {a: 1} 124 | ``` 125 | 126 | ## 실행 컨텍스트 127 | 128 | 소스코드는 4가지 타입으로 구분한다. 129 | 130 | - 전역 코드 131 | - 함수 코드 132 | - `eval` 코드 133 | - 모듈 코드 134 | 135 | 각각의 코드 스코프에 따라 실행 컨텍스트도 따라 생성되어지게 된다. 136 | 137 | 138 | 자바스크립트 엔진은 소스코드를 2개의 과정 "소스코드의 평가"와 "소스코드의 실행"으로 나누어 처리한다. 139 | 140 | 소스코드 평가 과정에서는 실행 컨텍스트를 생성하고 변수, 함수등의 선언문만 먼저 실행하여 생성된 변수나 함수 식별자를 키로 실행 컨텍스트가 관리하는 스코프에 등록한다. 141 | 142 | 소스코드 평가 과정이 끝나면 비로소 선언문을 제외한 소스코드가 순차적으로 실행되기 시작한다(이를 런타임이라 한다). 소스코드 실행에 필요한 정보들을 실행 컨텍스트가 관리하는 스코프에서 검색해서 취득하며, 변경 및 실행 결과도 다시 실행 컨텍스트가 관리하는 스코프에 등록된다. 143 | 144 | ## 실행 컨텍스트 스택 145 | 146 | 실행 컨텍스트는 스택 자료구조로 관리된다. 이를 실행 컨텍스트 스택이라고 부른다. 147 | 148 | 실행 컨텍스트 스택은 코드의 실행 순서를 관리한다. 소스코드가 평가되면 실행 컨텍스트가 생성되고 실행 컨텍스트 스택의 최상위에 쌓인다. 실행 컨텍스트 스택의 최상위에 존재하는 실행 컨텍스트는 언제나 현재 실행 중인 코드의 실행 컨텍스트이다. 스택 최상위에 존재하는 실행 컨텍스트를 "실행 중인 실행 컨텍스트"라 부른다. 149 | 150 | ## 렉시컬 환경 151 | 152 | 식별자와 식별자에 바인딩된 값, 그리고 상위 스코프에 대한 참조를 기록하는 자료구조로 실행 컨텍스트를 구성하는 컴포넌트이다. 153 | 154 | -------------------------------------------------------------------------------- /5회차/천승아.md: -------------------------------------------------------------------------------- 1 | # ✍️ 공부 내용 정리 2 | 3 | ## 22장 this 4 | 5 | ### 22-1. this 키워드 6 | 7 | - this: 자신이 속한 객체 or 자신이 생성할 인스턴스를 가리키는 자기 참조 변수 -> 지역 변수처럼 사용 8 | - 자신의 프로퍼티나 메서드 참조 가능 9 | - this 바인딩은 함수 호출 방식에 의해 동적으로 결정 됨 10 | - 클래스 기반 언어에서의 this는 클래스가 생성하는 인스턴스를 가리킴. (생성자 함수 처럼) 11 | 12 | > 일반 함수 13 | 14 | - strict mode에서 일반 함수 내의 this는 상대적으로 무의미하므로 undefined가 바인딩된다. 15 | 16 | ```js 17 | function square(number) { 18 | // 일반 함수 내부에서 this는 전역 객체 window를 가리킨다. 19 | console.log(this); // window, strict mode: undefined 20 | return number * number; 21 | } 22 | square(2); 23 | ``` 24 | 25 | > 객체 리터럴 26 | 27 | - 객체 리터럴 내부 메서드의 this: this 메서드를 호출한 객체 28 | 29 | ```js 30 | // 객체 리터럴 31 | const circle = { 32 | radius: 5, 33 | getDiameter() { 34 | // this는 메서드를 호출한 객체를 가리킨다. 35 | return 2 * this.radius; 36 | }, 37 | }; 38 | 39 | console.log(circle.getDiameter()); // 10 40 | ``` 41 | 42 | > 생성자 함수 43 | 44 | ```js 45 | // 생성자 함수 46 | function Circle(radius) { 47 | // this는 생성자 함수가 생성할 인스턴스를 가리킨다. 48 | this.radius = radius; 49 | } 50 | 51 | Circle.prototype.getDiameter = function () { 52 | // this는 생성자 함수가 생성할 인스턴스를 가리킨다. 53 | return 2 * this.radius; 54 | }; 55 | 56 | // 인스턴스 생성 57 | const circle = new Circle(5); 58 | console.log(circle.getDiameter()); // 10 59 | ``` 60 | 61 | ### 22-2. 함수 호출 방식과 this 바인딩 62 | 63 | - this 바인딩은 함수 호출 방식에 따라 동적으로 결정된다. 64 | - 함수도 다양한 방식으로 호출 방식에 주의 65 | 66 | #### 22-2-1. 일반 함수 호출 67 | 68 | - 전역 함수를 포함하여 중첩 함수를 일반 함수로 호출하면 this에 전역 객체가 바인딩 69 | - strict mode에서는 중첩 함수도 undefined로 바인딩 70 | 71 | > 메서드, 콜백 함수 등 어떠한 함수라도 일반 함수로 호출되면 this에 전역 객체가 바인딩 된다. 72 | 73 | ```js 74 | // var 키워드로 선언한 전역 변수 value는 전역 객체의 프로퍼티다. 75 | var value = 1; 76 | // const 키워드로 선언한 전역 변수 value는 전역 객체의 프로퍼티가 아니다. 77 | // const value = 1; 78 | 79 | const obj = { 80 | value: 100, 81 | foo() { 82 | console.log("foo's this: ", this); // {value: 100, foo: ƒ} 83 | console.log("foo's this.value: ", this.value); // 100 84 | 85 | // 메서드 내에서 정의한 중첩 함수 86 | function bar() { 87 | console.log("bar's this: ", this); // window 88 | console.log("bar's this.value: ", this.value); // 1 89 | } 90 | 91 | // 메서드 내에서 정의한 중첩 함수도 일반 함수로 호출되면 중첩 함수 내부의 this에는 전역 객체가 바인딩된다. 92 | bar(); 93 | }, 94 | }; 95 | 96 | obj.foo(); 97 | ``` 98 | 99 | ```js 100 | var value = 1; 101 | 102 | const obj = { 103 | value: 100, 104 | foo() { 105 | console.log("foo's this: ", this); // {value: 100, foo: ƒ} 106 | // 콜백 함수 내부의 this에는 전역 객체가 바인딩된다. 107 | setTimeout(function () { 108 | console.log("callback's this: ", this); // window 109 | console.log("callback's this.value: ", this.value); // 1 110 | }, 100); 111 | }, 112 | }; 113 | 114 | obj.foo(); 115 | ``` 116 | 117 | > 일반 함수에서 메서드 내부의 중첩 함수 or 콜백 함수의 this 바인딩을 메서드의 this 바인딩을 일치 시키기 위한 방법은 다음과 같다. 118 | 119 | 1. 변수에 this 할당 120 | 121 | ```js 122 | const obj = { 123 | value: 100, 124 | foo() { 125 | // this 바인딩(obj)을 변수 that에 할당한다. 126 | const that = this; 127 | 128 | // 콜백 함수 내부에서 this 대신 that을 참조한다. 129 | setTimeout(function () { 130 | console.log(that.value); // 100 131 | }, 100); 132 | }, 133 | }; 134 | ``` 135 | 136 | 2. this 명시적 바인딩 137 | 138 | ```js 139 | // Function.prototype.bind 140 | const obj = { 141 | value: 100, 142 | foo() { 143 | // 콜백 함수에 명시적으로 this를 바인딩한다. 144 | setTimeout( 145 | function () { 146 | console.log(this.value); // 100 147 | }.bind(this), 148 | 100 149 | ); 150 | }, 151 | }; 152 | 153 | // 화살표 함수 154 | const obj = { 155 | value: 100, 156 | foo() { 157 | // 화살표 함수 내부의 this는 상위 스코프의 this를 가리킨다. 158 | setTimeout(() => console.log(this.value), 100); // 100 159 | }, 160 | }; 161 | ``` 162 | 163 | #### 22-2-2. 메서드 호출 164 | 165 | - **메서드를 호출한 객체**에 this 바인딩 (메서드를 소유한 객체가 아님) 166 | - 메서드가 가리키는 함수 객체는 객체에 포함된 것이 아니라, 독립적으로 존재하는 별도의 객체 167 | - 즉 메서드를 다른 객체의 프로퍼티에 할당하는 것으로 다른 메서드가 되거나 일반 함수로 호출될 수 있음. 168 | - 프로토타입 내부 메서드도 마찬가지 169 | 170 | ```js 171 | const anotherPerson = { 172 | name: "Kim", 173 | }; 174 | // getName 메서드를 anotherPerson 객체의 메서드로 할당 175 | anotherPerson.getName = person.getName; 176 | 177 | // getName 메서드를 호출한 객체는 anotherPerson이다. 178 | console.log(anotherPerson.getName()); // Kim 179 | 180 | // getName 메서드를 변수에 할당 181 | const getName = person.getName; 182 | 183 | // getName 메서드를 일반 함수로 호출 184 | console.log(getName()); // '' 185 | // 일반 함수로 호출된 getName 함수 내부의 this.name은 브라우저 환경에서 window.name과 같다. 186 | // 브라우저 환경에서 window.name은 브라우저 창의 이름을 나타내는 빌트인 프로퍼티이며 기본값은 ''이다. 187 | // Node.js 환경에서 this.name은 undefined다. 188 | ``` 189 | 190 | #### 22-2-3. 생성자 함수 호출 191 | 192 | - this에는 생성자 함수가 생성할 인스턴스가 바인딩 193 | - 생성자 함수가 일반 함수로 호출되면 생성자 함수로 동작하지 않아 undefined를 반환하고 내부 this는 전역 객체를 가진다. 194 | 195 | ```js 196 | // new 연산자와 함께 호출하지 않으면 생성자 함수로 동작하지 않는다. 즉, 일반적인 함수의 호출이다. 197 | const circle3 = Circle(15); 198 | 199 | // 일반 함수로 호출된 Circle에는 반환문이 없으므로 암묵적으로 undefined를 반환한다. 200 | console.log(circle3); // undefined 201 | 202 | // 일반 함수로 호출된 Circle 내부의 this는 전역 객체를 가리킨다. 203 | console.log(radius); // 15 204 | ``` 205 | 206 | #### 22-2-4. Function.prototype.apply/call/bind 메서드에 의한 간접 호출 207 | 208 | - this에 메서드에 첫 번째 인수로 전달한 객체가 바인딩 209 | - apply, call, bind 메서드는 Function.prototype 메서드이므로 모든 함수가 상속받아 사용할 수 있다. 210 | 211 | > apply, call 212 | 213 | - this로 사용할 객체와 인수 리스트를 전달받아 함수 호출함 214 | - 호출할 함수에 인수를 전달하는 방식만 다르며 동일하게 동작 215 | 1. apply: 호출할 함수의 인수를 배열로 묶어 전달 216 | 2. call: 호출할 함수의 인수를 쉼표 리스트로 전달 217 | 218 | ```js 219 | function getThisBinding() { 220 | console.log(arguments); 221 | return this; 222 | } 223 | 224 | // this로 사용할 객체 225 | const thisArg = { a: 1 }; 226 | 227 | // getThisBinding 함수를 호출하면서 인수로 전달한 객체를 getThisBinding 함수의 this에 바인딩한다. 228 | // apply 메서드는 호출할 함수의 인수를 배열로 묶어 전달한다. 229 | console.log(getThisBinding.apply(thisArg, [1, 2, 3])); 230 | // Arguments(3) [1, 2, 3, callee: ƒ, Symbol(Symbol.iterator): ƒ] 231 | // {a: 1} 232 | 233 | // call 메서드는 호출할 함수의 인수를 쉼표로 구분한 리스트 형식으로 전달한다. 234 | console.log(getThisBinding.call(thisArg, 1, 2, 3)); 235 | // Arguments(3) [1, 2, 3, callee: ƒ, Symbol(Symbol.iterator): ƒ] 236 | // {a: 1} 237 | ``` 238 | 239 | > bind 240 | 241 | - 함수를 호출하지 않고, 첫 번째 인수로 전달한 값으로 this 바인딩이 교체된 함수를 새롭게 생성해 반환한다. 242 | - -> 메서드의 this, 메서드 내부의 중첩 함수 or 콜백 함수 this가 불일치하는 문제를 해결하는데 사용 243 | 244 | ```js 245 | const person = { 246 | name: "Lee", 247 | foo(callback) { 248 | // bind 메서드로 callback 함수 내부의 this 바인딩을 전달 249 | setTimeout(callback.bind(this), 100); 250 | }, 251 | }; 252 | 253 | person.foo(function () { 254 | console.log(`Hi! my name is ${this.name}.`); // Hi! my name is Lee. 255 | }); 256 | ``` 257 | -------------------------------------------------------------------------------- /6회차/README.md: -------------------------------------------------------------------------------- 1 | 2 | # 공유 자료 3 | 4 | ## 클로저 5 | 6 | 7 | 8 | [자바스크립트 클로저로 Hooks구현하기](https://medium.com/humanscape-tech/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%ED%81%B4%EB%A1%9C%EC%A0%80%EB%A1%9C-hooks%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0-3ba74e11fda7) 9 | 10 | [[React] 클로저와 useState Hooks](https://yeoulcoding.tistory.com/149#recentEntries) 11 | 12 | 13 | ## 클래스 14 | 15 | [리액트에서 데이터로 클래스 사용하기 (feat. enum)](https://www.youtube.com/watch?v=J3TrdIoEu9I) 16 | 17 | [[es2015+] class문은 특별할까?](https://www.bsidesoft.com/5370) 18 | 19 | [ES6의 Class extends 내부 동작원리에 대해서 설명해보세요](https://2ssue.github.io/common_questions_for_Web_Developer/docs/Javascript/8_es6_class_extends.html#%E1%84%8E%E1%85%A1%E1%86%B7%E1%84%80%E1%85%A9) 20 | -------------------------------------------------------------------------------- /6회차/이병현.md: -------------------------------------------------------------------------------- 1 | ## 클로저 2 | 3 | _클로저는 함수와 그 함수가 선언된 렉시컬 환경과의 조합이다._ 4 | 5 | 아래 두개의 코드에서 차이점을 알 수 있는데 위의 정의와 같이 그 함수가 "선언"된 곳을 기준으로 하기 때문이다. 6 | 7 | ```js 8 | const x = 1; 9 | 10 | function outerFunc() { 11 | const x = 10; 12 | 13 | function innerFunc() { 14 | console.log(x); // 10 15 | } 16 | 17 | innerFunc(); 18 | } 19 | 20 | outerFunc(); 21 | ``` 22 | 23 | ```js 24 | const x = 1; 25 | 26 | function outerFunc() { 27 | const x = 10; 28 | innerFunc(); 29 | } 30 | 31 | function innerFunc() { 32 | console.log(x); // 1 33 | } 34 | 35 | outerFunc(); 36 | ``` 37 | 38 | ## 렉시컬 스코프 39 | 40 | _렉시컬 환경의 "외부 렉시컬 환경에 대한 참조"에 저장할 참조값, 즉 상위 스코프에 대한 참조는 함수 정의가 평가되는 시점에 함수가 정의된 환경(위치)에 의해 결정된다. 이것이 바로 렉시컬 스코프이다._ 41 | 42 | ## `[[Environment]]` 43 | 44 | _외부 렉시컬 환경에 대한 참조에는 함수 객체의 내부 슬롯 `[[Environment]]`에 저장된 렉시컬 환경의 참조가 할당된다._ 45 | 46 | 즉, 함수 객체의 내부 슬롯 `[[Environment]]`에 저장된 렉시컬 환경의 참조는 바로 함수의 상위 스코프를 의미한다. 이것이 바로 함수 정의 위치에 따라 상위 스코프를 결정하는 렉시컬 스코프의 실체다. 47 | 48 | 49 | ## 클로저와 렉시컬 스코프 50 | 51 | ```js 52 | const x = 1; 53 | 54 | // ① 55 | function outer() { 56 | const x = 10; 57 | const inner = function () { console.log(x); }; // ② 58 | return inner; 59 | } 60 | 61 | // outer 함수를 호출하면 중첩 함수 inner를 반환한다. 62 | // 그리고 outer 함수의 실행 컨텍스트는 실행 컨텍스트 스택에서 팝되어 제거된다. 63 | const innerFunc = outer(); // ③ 64 | innerFunc(); // ④ 10 65 | ``` 66 | 67 | _외부 함수보다 중첩 함수가 더 오래 유지되는 경우 중첩 함수는 이미 생명 주기가 종료한 외부 함수의 변수를 참조할 수 있다. 이러한 중첩 함수를 "클로저"라고 부른다._ 68 | 69 | 위와 같은 이유로 실행 컨텍스트에서는 제거되지만, 참조되고 있는 경우 가비지 컬렉션의 대상이 되지 않는다. 70 | 71 | 🧐🧐🧐 72 | _모던 자바스크립트 엔진은 최적화가 잘 되어 있어서 참조하고 있지 않는 식별자는 기억하지 않는다._ 73 | -> 실제로 테스트 해보자 74 | 75 | 76 | ## 클로저의 활용 77 | 78 | 79 | ```js 80 | const counter = (function () { 81 | // 카운트 상태 변수 82 | let num = 0; 83 | 84 | // 클로저인 메서드를 갖는 객체를 반환한다. 85 | // 객체 리터럴은 스코프를 만들지 않는다. 86 | // 따라서 아래 메서드들의 상위 스코프는 즉시 실행 함수의 렉시컬 환경이다. 87 | return { 88 | // num: 0, // 프로퍼티는 public하므로 은닉되지 않는다. 89 | increase() { 90 | return ++num; 91 | }, 92 | decrease() { 93 | return num > 0 ? --num : 0; 94 | } 95 | }; 96 | }()); 97 | 98 | console.log(counter.increase()); // 1 99 | console.log(counter.increase()); // 2 100 | 101 | console.log(counter.decrease()); // 1 102 | console.log(counter.decrease()); // 0 103 | ``` 104 | 105 | ```js 106 | const Counter = (function () { 107 | // ① 카운트 상태 변수 108 | let num = 0; 109 | 110 | function Counter() { 111 | // this.num = 0; // ② 프로퍼티는 public하므로 은닉되지 않는다. 112 | } 113 | 114 | Counter.prototype.increase = function () { 115 | return ++num; 116 | }; 117 | 118 | Counter.prototype.decrease = function () { 119 | return num > 0 ? --num : 0; 120 | }; 121 | 122 | return Counter; 123 | }()); 124 | 125 | const counter = new Counter(); 126 | 127 | console.log(counter.increase()); // 1 128 | console.log(counter.increase()); // 2 129 | 130 | console.log(counter.decrease()); // 1 131 | console.log(counter.decrease()); // 0 132 | ``` 133 | 134 | 🧐🧐🧐 135 | 클래스와 비슷해보인다, 클래스와는 어떻게 다를까 136 | -> 클로저는 위 같은 상황 뿐만 아니라 아래와 같이 사용할 수도 있다. 137 | 138 | ```js 139 | 140 | // 함수를 반환하는 고차 함수 141 | // 이 함수는 카운트 상태를 유지하기 위한 자유 변수 counter를 기억하는 클로저를 반환한다. 142 | const counter = (function () { 143 | // 카운트 상태를 유지하기 위한 자유 변수 144 | let counter = 0; 145 | 146 | // 함수를 인수로 전달받는 클로저를 반환 147 | return function (aux) { 148 | // 인수로 전달 받은 보조 함수에 상태 변경을 위임한다. 149 | counter = aux(counter); 150 | return counter; 151 | }; 152 | }()); 153 | 154 | // 보조 함수 155 | function increase(n) { 156 | return ++n; 157 | } 158 | 159 | // 보조 함수 160 | function decrease(n) { 161 | return --n; 162 | } 163 | 164 | // 보조 함수를 전달하여 호출 165 | console.log(counter(increase)); // 1 166 | console.log(counter(increase)); // 2 167 | 168 | // 자유 변수를 공유한다. 169 | console.log(counter(decrease)); // 1 170 | console.log(counter(decrease)); // 0 171 | ``` 172 | 173 | 혹은 클래스의 `private`같이 사용할 수도 있지만, 174 | 175 | ```js 176 | const Person = (function () { 177 | let _age = 0; // private 178 | 179 | // 생성자 함수 180 | function Person(name, age) { 181 | this.name = name; // public 182 | _age = age; 183 | } 184 | 185 | // 프로토타입 메서드 186 | Person.prototype.sayHi = function () { 187 | console.log(`Hi! My name is ${this.name}. I am ${_age}.`); 188 | }; 189 | 190 | // 생성자 함수를 반환 191 | return Person; 192 | }()); 193 | 194 | const me = new Person('Lee', 20); 195 | me.sayHi(); // Hi! My name is Lee. I am 20. 196 | console.log(me.name); // Lee 197 | console.log(me._age); // undefined 198 | 199 | const you = new Person('Kim', 30); 200 | you.sayHi(); // Hi! My name is Kim. I am 30. 201 | console.log(you.name); // Kim 202 | console.log(you._age); // undefined 203 | ``` 204 | 205 | 아래와 같이 변수 값이 유지되지 않는 문제가 생긴다. 206 | 207 | ```js 208 | const me = new Person('Lee', 20); 209 | me.sayHi(); // Hi! My name is Lee. I am 20. 210 | 211 | const you = new Person('Kim', 30); 212 | you.sayHi(); // Hi! My name is Kim. I am 30. 213 | 214 | // _age 변수 값이 변경된다! 215 | me.sayHi(); // Hi! My name is Lee. I am 30. 216 | ``` 217 | 218 | `Person.prototype.sayHi`메서드는 즉시 실행 함수가 호출될 때 생성되며, 이 때 자신의 상위 스코프인 즉시 실행 함수의 실행 컨텍스트의 렉시컬 환경의 참조를 기억한다. 따라서, 어떤 인스턴스를 호출하더라도 하나의 동일한 하나의 상위 스크포를 사용하게 되는 것이다. 219 | 220 | ## 클래스는 프로토타입의 문법적 설탕인가? 221 | 222 | 기능적으로 비슷하여 문법적 설탕이라고 볼 수도 있지만, 아래와 같은 차이점이 있다. 223 | 224 | - 클래스를 `new` 연산자 없이 호출하면 에러가 발생한다. 하지만 생성자 함수를 `new`연산자 없이 호출하면 일반 함수로서 호출된다. 225 | - 클래스는 상속을 지원하는 `extends`와 `super`키워드를 제공한다. 하지만 생성자 함수는 `extends`와 `super`키워드를 지원하지 않는다 226 | - 클래스는 호이스팅이 발생하지 않는 것처럼 동작한다. 하지만 함수 선언문으로 정의된 함수는 함수 호이스팅이, 함수 표현식으로 정의한 생성자 함수는 변수 호이스팅이 발생한다. 227 | - 클래스 내의 모든 코드에는 암묵적으로 `stirct mode`가 지정되어 실행되며 `stirct mode`를 해제할 수 없다. 하지만 생성자 함수는 암묵적으로 `stirct mode`가 지정되지 않는다. 228 | - 클래스의 `constructor` , 프로토타입 메서드, 정적 메서드는 모두 프로퍼티 어트리뷰트 `[[Enumberable]]`의 값이 `false`이다. 다시 말해, 열거되지 않는다. 229 | 230 | ## 클래스 231 | 232 | 클래스 함수이며 값처럼 사용할 수 있는 일급 객체이므로 변수에 저장할 수 있다. 233 | 234 | ```js 235 | // 익명 클래스 표현식 236 | const Person = class {}; 237 | 238 | // 기명 클래스 표현식 239 | const Person = class MyClass {}; 240 | ``` 241 | 242 | 243 | ```js 244 | // 클래스 선언문 245 | class Person { 246 | // 생성자 247 | constructor(name) { 248 | // 인스턴스 생성 및 초기화 249 | this.name = name; // name 프로퍼티는 public하다. 250 | } 251 | 252 | // 프로토타입 메서드 253 | sayHi() { 254 | console.log(`Hi! My name is ${this.name}`); 255 | } 256 | 257 | // 정적 메서드 258 | static sayHello() { 259 | console.log('Hello!'); 260 | } 261 | } 262 | 263 | // 인스턴스 생성 264 | const me = new Person('Lee'); 265 | 266 | // 인스턴스의 프로퍼티 참조 267 | console.log(me.name); // Lee 268 | // 프로토타입 메서드 호출 269 | me.sayHi(); // Hi! My name is Lee 270 | // 정적 메서드 호출 271 | Person.sayHello(); // Hello! 272 | ``` 273 | 274 | ## getter setter 275 | 276 | ```js 277 | class Person { 278 | constructor(firstName, lastName) { 279 | this.firstName = firstName; 280 | this.lastName = lastName; 281 | } 282 | 283 | // fullName은 접근자 함수로 구성된 접근자 프로퍼티다. 284 | // getter 함수 285 | get fullName() { 286 | return `${this.firstName} ${this.lastName}`; 287 | } 288 | 289 | // setter 함수 290 | set fullName(name) { 291 | [this.firstName, this.lastName] = name.split(' '); 292 | } 293 | } 294 | 295 | const me = new Person('Ungmo', 'Lee'); 296 | 297 | // 데이터 프로퍼티를 통한 프로퍼티 값의 참조. 298 | console.log(`${me.firstName} ${me.lastName}`); // Ungmo Lee 299 | 300 | // 접근자 프로퍼티를 통한 프로퍼티 값의 저장 301 | // 접근자 프로퍼티 fullName에 값을 저장하면 setter 함수가 호출된다. 302 | me.fullName = 'Heegun Lee'; 303 | console.log(me); // {firstName: "Heegun", lastName: "Lee"} 304 | 305 | // 접근자 프로퍼티를 통한 프로퍼티 값의 참조 306 | // 접근자 프로퍼티 fullName에 접근하면 getter 함수가 호출된다. 307 | console.log(me.fullName); // Heegun Lee 308 | 309 | // fullName은 접근자 프로퍼티다. 310 | // 접근자 프로퍼티는 get, set, enumerable, configurable 프로퍼티 어트리뷰트를 갖는다. 311 | console.log(Object.getOwnPropertyDescriptor(Person.prototype, 'fullName')); 312 | // {get: ƒ, set: ƒ, enumerable: false, configurable: true} 313 | ``` 314 | -------------------------------------------------------------------------------- /7회차/README.md: -------------------------------------------------------------------------------- 1 | # 7회차 2 | 3 | ## 공유 자료 4 | 5 | - [iterable 객체](https://ko.javascript.info/iterable) 6 | - [[JS] 📚 이터러블 & 이터레이터 - 💯완벽 이해](https://inpa.tistory.com/entry/JS-📚-이터러블-이터레이터-💯완벽-이해) 7 | - [reduce 사용해서 알고리즘 해결하기](https://velog.io/@sangbooom/잘-활용하면-유용한-Array-API-reduce) 8 | - [[Javascript] 2차원 배열 만들기](https://velog.io/@sangbooom/Javascript-2차원-배열-만들기) 9 | - [Array.prototype.flatMap()](https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/flatMap) 10 | -------------------------------------------------------------------------------- /7회차/김록원.md: -------------------------------------------------------------------------------- 1 | # 26장 - ES6 함수의 추가 기능 2 | ## 함수의 구분 3 | ES6 이전까지 js의 함수는 별다른 구분 없이 다양한 목적으로 사용되었다. 4 | - 일반적인 함수로도 호출 5 | - `new` 연산자와 함께 인스턴스를 생성할 수 있는 생성자 함수 6 | - 객체에 바인딩되어 메서드로서 호출 7 | 8 | 편리할 것 같지만 실수를 유발시킬 수 있으며 성능 면에서도 손해가 많았다. 9 | ES6 이전까지 모든 함수는 `callable`(호출 할 수 있음) 이면서 `constructor`(인스턴스를 생성할 수 있음)였다. 10 | 때문에 객체에 바인됭 함수, 콜백 함수 또한 일반함수, 생성자 함수로도 호출이 가능하며 프로토타입 객체도 생생하게 된다. 11 | 12 | ES6 부터는 함수를 3가지 종류로 명확히 구분하였다. 13 | | ES6 함수의 구분 | constructor | prototype | super | argumets | 14 | | -------------- | :---------: | :-------: | :---: | :------: | 15 | | `일반 함수` | O | O | X | O | 16 | | `메서드` | X | X | O | O | 17 | | `화살표 함수` | X | X | X | X | 18 | 19 | 20 |
21 | 22 | 23 | ## ES6 메서드 24 | ES6 이전에는 메서드에 대한 명확한 정의가 없었다. 일반적으로는 객체에 바인됭 함수를 일컫는 의미로 사용되었지만 ES6 사양에서 메서드에 대한 정의가 명확하게 규정되었다. 25 | ES6에서 메서드는 **메서드 축약 표현으로 정의된 함수를 의미**한다. 26 | ES6의 메서드는 몇가지 특징을 지닌다. 이 특징을 잘 숙지해야 보다 효율적인 코드를 구현할 수 있다. 27 | 28 | - 인스턴스를 생성할 수 없는 `non-constructor` 29 | - 인스턴스를 생성할 수 없으므로 `prototype` 프로퍼티가 없고 프로토타입 객체도 생성X 30 | - 표준 빌트인 객체가 제공하는 프로토타입 메서드와 정적 메서드는 모두 `non-constructor` 31 | - 자신을 바인딩한 객체를 가리키는 내부슬롯 `[[HomeObject]]`를 갖는다. 32 | - super 참조는 이 내부슬롯을 사용하여 수퍼클래서의 메서드를 참조한다. 33 | - ES6 메서드가 아닌 함수는 이 내부슬롯을 가지지 않으므로 super 참조불가 34 | 35 |
36 | 37 | ```js 38 | const obj = { 39 | x: 1, 40 | // foo는 메서드이다. 41 | foo() { return this.x; }, 42 | // bar에 바인딩된 함수는 메서드가 아닌 일반 함수이다. 43 | bar: function() { return this.x; } 44 | }; 45 | 46 | console.log(obj.foo()); // 1 47 | console.log(obj.bar()); // 1 48 | ``` 49 | 50 |
51 | 52 | ## ES6 화살표 함수 53 | `function` 키워드 대신 화살표를 사용하여 기존의 함수 정의 방식보다 간략하게 함수를 정의할 수 있다. 54 | 표현 간략화 뿐만 아니라 내부 동작도 기존의 함수보다 간략하다. 55 | 56 |
57 | 58 | **화살표 함수 정의 특징** 59 | - 화살표 함수는 함수 선언문으로 정의할 수 없으며 표현식으로 정의해야한다. 60 | - IIFE(즉시실행 함수)로 사용가능하다. 61 | - 일급 객체이므로 고차함수에 인수로 전달가능하다. 62 | - 한줄(표현식인 문)이라면 `{}`를 생략가능하다. 63 | 64 |
65 | 66 | **화살표 함수가 가지는 일반함수와의 차이** 67 | - 인스턴스를 생성할 수 없는 `non-constructor`이다.(메서드와 마찬가지) 68 | - 중복된 매개변수 이름을 선언할 수 없다. 69 | - 함수 자체에 `this`, `arguments`, `super`, `new.target` 바인딩을 갖지 않는다. 따라서 참조 시 스코프 체인을 통해 참조하게 된다. 70 | 71 |
72 | 73 | ### 화살표 함수에서의 `this` 74 | 콜백 함수 내부의 this와 콜백함수를 호출한 외부의 this와 다르기 때문에 발생하는 문제를 해결하기 위해 설계되었다. 75 | 76 | 위에서 얘기했듯이 **화살표 함수는 this 바인딩을 갖지 않는다. 따라서 this 참조 시 상위 스코프의 this를 그대로 참조한다.(lexical this)** 즉, 화살표 함수의 this가 함수가 정의된 위치에 의해 결정된다는 것을 의미한다.(함수 선언시의 상위 스코프의 this) 77 | 78 | 이로 인해 몇가지 주의할 점이 생긴다. 79 | - **객체 내 함수(일반적인 의미의 메서드) 정의 시** 80 | - 아래예제에서 person을 가리키지 않고 상위 스코프인 전역을 가리킴 81 | - 이때는 메서드 축약표현을 사용하자 82 | ```js 83 | // Bad 84 | const person = { 85 | name: 'Lee', 86 | sayHi: () => console.log(`Hi ${this.name}`) 87 | }; 88 | 89 | // sayHi 프로퍼티에 할당된 화살표 함수 내부의 this는 상위 스코프인 전역의 this가 가리키는 90 | // 전역 객체를 가리키므로 이 예제를 브라우저에서 실행하면 this.name은 빈 문자열을 갖는 91 | // window.name과 같다. 전역 객체 window에는 빌트인 프로퍼티 name이 존재한다. 92 | person.sayHi(); // Hi 93 | ``` 94 | - **프로토타입 객체의 프로퍼티** 95 | - 이때도 위와 같은 문제 발생 96 | - 일반 함수를 사용하자 or 프로토타입 객체의 연결을 재설정하자 97 | ```js 98 | // Bad 99 | function Person(name) { 100 | this.name = name; 101 | } 102 | 103 | Person.prototype.sayHi = () => console.log(`Hi ${this.name}`); 104 | 105 | const person = new Person('Lee'); 106 | // 이 예제를 브라우저에서 실행하면 this.name은 빈 문자열을 갖는 window.name과 같다. 107 | person.sayHi(); // Hi 108 | ``` 109 | - **클래스 필드 정의 제안** 110 | - 프로토타입 메서드가 아닌 인스턴스 메서드가 된다. 111 | - ES6 메서드를 사용하자 112 | ```js 113 | // Bad 114 | class Person { 115 | // 클래스 필드 정의 제안 116 | name = 'Lee'; 117 | sayHi = () => console.log(`Hi ${this.name}`); 118 | } 119 | 120 | // 위와 같음 121 | class Person { 122 | constructor() { 123 | this.name = 'Lee'; 124 | // 클래스가 생성한 인스턴스(this)의 sayHi 프로퍼티에 화살표 함수를 할당한다. 125 | // sayHi 프로퍼티는 인스턴스 프로퍼티이다. 126 | this.sayHi = () => console.log(`Hi ${this.name}`); 127 | } 128 | } 129 | 130 | 131 | const person = new Person(); 132 | person.sayHi(); // Hi Lee 133 | ``` 134 | 135 |
136 | 137 | ## Rest 파라미터 138 | Rest 파라미터(나머지 매개변수)는 매개변수 이름 앞에 세개의 점 `...`을 붙여서 정의한 매개변수를 의미한다. 139 | Rest 파라미터는 함수에 전달된 인수들의 목록을 배열로 전달받는다. 140 | - 매개변수에 할당된 인수를 제외한 나머지 인수들로 구성된 배열에 할당된다. 141 | - 반드시 마지막 파라미터여야한다. 142 | - 단 하나만 선언할 수 있다. 143 | - 매개변수 개수를 나타내는 함수 객체의 length 프로퍼티에 영향을 주지 않는다. 144 | 145 | ES5에서는 가변인자 함수의 경우 매개변수를 통해 인수를 전달받는 것이 불가능 하므로 `arguments` 객체를 활용하여 인수를 전달받았다. 이 객체는 순회가능한 유사배열객체이며 함수 내부에서 지역변수처럼 사용할 수 있다. 146 | ES6에서는 둘 모두 사용할 수 있지만 화살표 함수의 경우 `arguments`객체를 갖지 않으므로 Rest 파라미터를 사용해야한다. 147 | 148 |
149 | 150 | ## 매개변수 기본값 151 | 매개변수 기본값을 사용하면 함수 내에서 수행하던 인수 체크 및 초기화를 간소화할 수 있다. 152 | - 매개변수에 인수를 전달하지 않은 경우와 `undefined`를 전달한 경우에만 유효하다. 153 | - Rest 파라미터에는 기본값을 지정하지 못한다. 154 | - 함수 객체의 length 프로퍼티와 arguments객체에 아무런 영향을 주지 않는다. 155 | ```js 156 | function logName(name = 'Lee') { 157 | console.log(name); 158 | } 159 | 160 | logName(); // Lee 161 | logName(undefined); // Lee 162 | logName(null); // null 163 | ``` 164 | 165 |
166 |
167 |
168 | 169 | # 27장 - 배열 170 | 배열은 여러개의 값을 순차적으로 나열한 자료구조이다. 171 | 배열이 가지고 있는 값을 `요소`라고 부르고, 172 | 배열의 요소는 배열에서 자신의 위치를 나태니는 `인덱스`를 가진다. 173 | 또한 배열은 요소의 개수 즉, 배열의 길이를 나타내는 length 프로퍼티를 가진다. 174 | 175 | 배열은 객체지만 일반객체와는 구별되는 몇가지 차이점이 있다. 바로 값의 순서를 가지고 있으며 length 프로퍼티가 있다는 것이다. 176 | 177 |
178 | 179 | ### js에서의 배열의 특징 180 | 181 | **`자료구조에서의 배열`** 182 | 자료구조에서 배우는 배열은 동일한 크기의 메모리 공간이 빈틈없이 연속적으로 나열된 자료구조를 말한다. 183 | 즉, 데이터 타입이 통일되어 있으며 연속적으로 인접해 있다.( = 밀집 배열 ) 184 | 이는 임의의 접근에 O(1)의 시간복잡도를 가지고 있어 매우 효율적이며 빠르다. 하지만 배열 중간에 요소 삽입, 삭제의 경우 이후 요소들을 이동시켜야하기때문에 시간이 걸린다. 185 | 186 |
187 | 188 | **`js에서의 배열`** 189 | 요소들이 동일한 크기를 갖지 않으며 메모리 구조상 연속적으로 이어져 있지 않을 수 있다. 190 | 이처럼 js 배열은 엄밀히 말하면 일반적인 의미의 배열과 다르다. 배열의 동작을 흉내낸 특수한 객체이다. 191 | **해시테이블로 구현한 객체**이기 때문에 요소 접근의 경우 일반배열보다 느릴 수 있지만 삽입, 삭제의 경우에 일반적인 배열보다 빠른 성능을 가진다. 192 | js엔진은 배열 요소 접근에 느릴 수 밖에 없다는 구조적인 단점을 보완하기 위해 최적화하여 구현하였다.(일반 객체보다는 빠르게 요소에 접근가능하다.) 193 | 194 |
195 | 196 | **`length 프로퍼티 조작`** 197 | length 프로퍼티는 요소를 추가하거나 삭제하면 자동으로 갱신된다. 198 | 하지만 직접 length 프로퍼티를 조작할 수 있는데, 현재 배열의 크기보다 작은 수를 할당한다면 배열의 뒷부분이 날라간다. 만약 현재 배열의 크기보다 큰 수를 할당한다면 배열의 길이가 늘어나지는 않는다(배열 뒤에 empty 가 추가됨. 뿐만아니라 배열 중간중간이 비어있을 수도 있음. - 희소배열) 199 | 200 |
201 | 202 | **`지향하자`** 203 | js가 희소 배열을 허용하지만 희소배열은 사용하지 않는 것이 좋다. 204 | 성능에도 좋지 않음, 연속적인 값의 집합이라는 배열의 의미와도 맞지 않다. 205 | 배열에는 같은 타입의 요소를 연속적으로 위치시키는 것이 최선이다. 206 | 207 |
208 | 209 | ### 배열 생성 210 | 211 | > 💡 유사 배열 212 | 배열처럼 인덱스로 프로퍼티 값에 접근할 수 있고, length 프로퍼티를 갖는 객체 213 | 마치 배열처럼 for 문으로 순회할 수 있다. 214 | 215 | 1. 배열 리터럴 216 | ```js 217 | const arr = [1, 2, 3]; 218 | console.log(arr.length); // 3 219 | ``` 220 | 2. Array 생성자 함수 221 | - 전달된 인수가 한개라면 해당 크기만큼의 배열을 생성한다(희소배열로) 222 | - 인수가 두개 이상이거나 인수가 하나여도 숫자가 아니라면 전달받은 인수들로 배열을 만든다. 223 | - `new`를 사용하지 않아도 배열을 생성하는 함수로 동작한다. 224 | ```js 225 | const arr = new Array(10); 226 | console.log(arr); // [empty × 10] 227 | console.log(arr.length); // 10 228 | Array(1, 2, 3); // -> [1, 2, 3] 229 | ``` 230 | 3. Array.of 231 | - ES6에서 도입되었으며 전달된 인수를 요소로 갖는 배열을 생성한다. 232 | ```js 233 | Array.of(1); // -> [1] 234 | Array.of(1, 2, 3); // -> [1, 2, 3] 235 | Array.of('string'); // -> ['string'] 236 | ``` 237 | 4. Array.from 238 | - 유사 배열 객체 or 이터러블 객체를 인수로 전달받아 배열로 변환하여 반환한다. 239 | - 두번째 인수로 콜백함수를 보낼 수 있다. 이 콜백함수는 두 가지 인자를 가지고 이 함수로 만들어지는 배열을 조작할 수 있다.(첫번째 인자는 만들어질 요소값, 두번째 인자는 인덱스) 240 | ```js 241 | // 유사 배열 객체를 변환하여 배열을 생성한다. 242 | Array.from({ length: 2, 0: 'a', 1: 'b' }); // -> ['a', 'b'] 243 | 244 | // 이터러블을 변환하여 배열을 생성한다. 문자열은 이터러블이다. 245 | Array.from('Hello'); // -> ['H', 'e', 'l', 'l', 'o'] 246 | ``` 247 | 248 |
249 | 250 | ### 배열요소 다루기 251 | 1. 참조 252 | ```js 253 | const arr = [1, 2]; 254 | 255 | // 인덱스가 0인 요소를 참조 256 | console.log(arr[0]); // 1 257 | // 인덱스가 1인 요소를 참조 258 | console.log(arr[1]); // 2 259 | // 인덱스가 2인 요소를 참조. 배열 arr에는 인덱스가 2인 요소가 존재하지 않는다. 260 | console.log(arr[2]); // undefined 261 | ``` 262 | 2. 추가와 갱신 263 | - 인덱스를 사용해 값을 할당할 수 있다. 264 | - length 프로퍼티 값보다 큰 인덱스로 새로운 요소를 추가하면 희소배열이 된다. 265 | - 인덱스는 0 이상의 정수(또는 정수 형태의 문자열)을 사용해야한다. 그렇지 않으면 그냥 프로퍼티로 들어간다.(length에도 영향을 안줌) 266 | ```js 267 | const arr = [0]; 268 | 269 | // 배열 요소의 추가 270 | arr[1] = 1; 271 | 272 | console.log(arr); // [0, 1] 273 | console.log(arr.length); // 2 274 | arr[100] = 100; 275 | 276 | console.log(arr); // [0, 1, empty × 98, 100] 277 | console.log(arr.length); // 101 278 | ``` 279 | 3. 삭제 280 | - delete 연산자를 사용할 수 있지만 프로퍼티를 삭제하는 것이기때문에 희소배열이 된다. 281 | - splice 메서드를 이용하여 완전 삭제를 진행하자 282 | ```js 283 | // delete 연산자사용 284 | let arr = [1, 2, 3]; 285 | 286 | // 배열 요소의 삭제 287 | delete arr[1]; 288 | console.log(arr); // [1, empty, 3] 289 | // length 프로퍼티에 영향을 주지 않는다. 즉, 희소 배열이 된다. 290 | console.log(arr.length); // 3 291 | 292 | 293 | // splice 메서드 사용 294 | let arr = [1, 2, 3]; 295 | // Array.prototype.splice(삭제를 시작할 인덱스, 삭제할 요소 수) 296 | // arr[1]부터 1개의 요소를 제거 297 | arr.splice(1, 1); 298 | console.log(arr); // [1, 3] 299 | 300 | // length 프로퍼티가 자동 갱신된다. 301 | console.log(arr.length); // 2 302 | ``` 303 | 304 | ### 배열 메서드 305 | 정적 메서드와 프로토타입 메서드를 제공한다. 306 | 배열 메서드는 결과물을 반환하는 패턴이 두 가지이므로 주의가 필요하다. 307 | **원본 배열을 직접 변경하는 메서드와 원본 배열을 직접 변경하지 않고 새로운 배열을 생성하여 반환하는 메서드가 있다.** 308 | -------------------------------------------------------------------------------- /7회차/김민지.md: -------------------------------------------------------------------------------- 1 | # 7회차 2 | 3 | ## 26장 ES6 함수의 추가 기능 4 | 5 | ### 26.1 함수의 구분 6 | 7 | ES6 이전의 함수는 동일한 함수라도 다양한 형태로 호출할 수 있다. 8 | 9 | ```js 10 | var foo = function () { 11 | return 1; 12 | }; 13 | 14 | // 일반적인 함수로서 호출 15 | foo(); // -> 1 16 | 17 | // 생성자 함수로서 호출 18 | new foo(); // -> foo {} 19 | 20 | // 메서드로서 호출 21 | var obj = { foo: foo }; 22 | obj.foo(); // -> 1 23 | ``` 24 | 25 | 이처럼 ES6 이전의 함수는 사용 목적에 따라 명확히 구분되지 않는다. **즉, ES6 이전의 모든 함수는 일반 함수로서 호출할 수 있는 것은 물론 생성자 함수로서 호출할 수 있다.** 26 | 27 | ```js 28 | var foo = function () {}; 29 | 30 | // ES6 이전의 모든 함수는 callable이면서 constructor다. 31 | foo(); // -> undefined 32 | new foo(); // -> foo {} 33 | ``` 34 | 35 | 주의할 것은 ES6 이전에 일반적으로 메서드라고 부르던 객체에 바인딩된 함수도 callable이며 constructor라는 것이다. 따라서 객체에 바인딩된 함수도 일반 함수로서 호출할 수 있는 것은 물론 생성자 함수로서 호출할 수도 있다. 36 | 37 | ```js 38 | // 프로퍼티 f에 바인딩된 함수는 callable이며 constructor다. 39 | var obj = { 40 | x: 10, 41 | f: function () { 42 | return this.x; 43 | }, 44 | }; 45 | 46 | // 프로퍼티 f에 바인딩된 함수를 메서드로서 호출 47 | console.log(obj.f()); // 10 48 | 49 | // 프로퍼티 f에 바인딩된 함수를 일반 함수로서 호출 50 | var bar = obj.f; 51 | console.log(bar()); // undefined 52 | 53 | // 프로퍼티 f에 바인딩된 함수를 생성자 함수로서 호출 54 | console.log(new obj.f()); // f {} 55 | ``` 56 | 57 | 위 예제와 같이 객체에 바인딩된 함수를 생성자 함수로 호출하는 경우가 흔치는 않겠지만 문법상 가능하다는 것은 문제가 있다. 그리고 이는 성능 면에서도 문제가 있다. 객체에 바인딩된 함수가 constructor라는 것은 객체에 바인딩된 함수가 prototype 프로퍼티를 가지며, 프로토타입 객체도 생성한다는 것을 의미하기 때문이다. 58 | 59 | 함수에 전달되어 보조 함수의 역할을 수행하는 콜백 함수도 마찬가지다. 콜백 함수도 constructor이기 때문에 불필요한 프로토타입 객체를 생성한다. 60 | 61 | ```js 62 | // 콜백 함수를 사용하는 고차 함수 map. 콜백 함수도 constructor이며 프로토타입을 생성한다. 63 | [1, 2, 3].map(function (item) { 64 | return item * 2; 65 | }); // -> [ 2, 4, 6 ] 66 | ``` 67 | 68 | 이처럼 ES6 이전의 모든 함수는 사용 목적에 따라 명확한 구분이 없으므로 호출 방식에 특별한 제약이 없고 생성자 함수로 호출되지 않아도 프로토타입 객체를 생성한다. 이는 혼란스러우며 실수를 유발할 가능성이 있고 성능에도 좋지 않다. 69 | 70 | 이러한 문제를 해결하기 위해 ES6에서는 함수를 사용 목적에 따라 세 가지 종류로 명확히 구분했다. 71 | 72 | ![images](https://user-images.githubusercontent.com/48766355/201520562-13653812-a436-4dcc-a08b-44438f2148b2.png) 73 | 74 | ### 26.2 메서드 75 | 76 | **ES6 메서드는 메서드 축약 표현으로 정의된 함수만을 의미한다.** 77 | 78 | ```js 79 | const obj = { 80 | x: 1, 81 | // foo는 메서드이다. 82 | foo() { 83 | return this.x; 84 | }, 85 | // bar에 바인딩된 함수는 메서드가 아닌 일반 함수이다. 86 | bar: function () { 87 | return this.x; 88 | }, 89 | }; 90 | 91 | console.log(obj.foo()); // 1 92 | console.log(obj.bar()); // 1 93 | ``` 94 | 95 | **ES6 메서드는 인스턴스를 생성할 수 없는 non-constructor다.** 96 | 97 | ```js 98 | new obj.foo(); // -> TypeError: obj.foo is not a constructor 99 | new obj.bar(); // -> bar {} 100 | ``` 101 | 102 | ES6 메서드는 인스턴스를 생성할 수 없으므로 prototype 프로퍼티가 없고 프로토타입도 생성하지 않는다. 103 | 104 | ```js 105 | // obj.foo는 constructor가 아닌 ES6 메서드이므로 prototype 프로퍼티가 없다. 106 | obj.foo.hasOwnProperty('prototype'); // -> false 107 | 108 | // obj.bar는 constructor인 일반 함수이므로 prototype 프로퍼티가 있다. 109 | obj.bar.hasOwnProperty('prototype'); // -> true 110 | ``` 111 | 112 | 표준 빌트인 객체가 제공하는 프로토타입 메서드와 정적 메서드는 모두 non-constructor다. 113 | 114 | ```js 115 | String.prototype.toUpperCase.prototype; // -> undefined 116 | String.fromCharCode.prototype; // -> undefined 117 | 118 | Number.prototype.toFixed.prototype; // -> undefined 119 | Number.isFinite.prototype; // -> undefined 120 | 121 | Array.prototype.map.prototype; // -> undefined 122 | Array.from.prototype; // -> undefined 123 | ``` 124 | 125 | **ES6 메서드는 자신을 바인딩한 객체를 가리키는 내부 슬롯 `[[HomeObject]]`를 갖는다.** 126 | 127 | ```js 128 | const base = { 129 | name: 'Lee', 130 | sayHi() { 131 | return `Hi! ${this.name}`; 132 | }, 133 | }; 134 | 135 | const derived = { 136 | __proto__: base, 137 | // sayHi는 ES6 메서드다. ES6 메서드는 [[HomeObject]]를 갖는다. 138 | // sayHi의 [[HomeObject]]는 sayHi가 바인딩된 객체인 derived를 가리키고 139 | // super는 sayHi의 [[HomeObject]]의 프로토타입인 base를 가리킨다. 140 | sayHi() { 141 | return `${super.sayHi()}. how are you doing?`; 142 | }, 143 | }; 144 | 145 | console.log(derived.sayHi()); // Hi! Lee. how are you doing? 146 | ``` 147 | 148 | ES6 메서드가 아닌 함수는 super 키워드를 사용할 수 없다. ES6 메서드가 아닌 함수는 내부 슬롯 `[[HomeObject]]`를 갖지 않기 때문이다. 149 | 150 | ```js 151 | const derived = { 152 | __proto__: base, 153 | // sayHi는 ES6 메서드가 아니다. 154 | // 따라서 sayHi는 [[HomeObject]]를 갖지 않으므로 super 키워드를 사용할 수 없다. 155 | sayHi: function () { 156 | // SyntaxError: 'super' keyword unexpected here 157 | return `${super.sayHi()}. how are you doing?`; 158 | }, 159 | }; 160 | ``` 161 | 162 | 이처럼 ES6 메서드는 본연의 기능(super)을 추가하고 의미적으로 맞지 않는 기능(constructor)은 제거했다. 따라서 메서드를 정의할 때 프로퍼티 값으로 익명 함수 표현식을 할당하는 ES6 이전의 방식은 사용하지 않는 것이 좋다. 163 | 164 | ### 26.3 화살표 함수 165 | 166 | 화살표 함수와 일반 함수의 차이는 다음과 같다. 167 | 168 | 1. 화살표 함수는 인스턴스를 생성할 수 없는 non-constructor다. 169 | 170 | ```js 171 | const Foo = () => {}; 172 | // 화살표 함수는 생성자 함수로서 호출할 수 없다. 173 | new Foo(); // TypeError: Foo is not a constructor 174 | ``` 175 | 176 | - 화살표 함수는 인스턴스를 생성할 수 없으므로 prototype 프로퍼티가 없고 프로토타입도 생성하지 않는다. 177 | 178 | ```js 179 | const Foo = () => {}; 180 | // 화살표 함수는 prototype 프로퍼티가 없다. 181 | Foo.hasOwnProperty('prototype'); // -> false 182 | ``` 183 | 184 | 2. 중복된 매개변수 이름을 선언할 수 없다. 185 | 186 | - 일반 함수는 중복된 매개변수 이름을 선언해도 에러가 발생하지 않는다. 187 | 188 | ```js 189 | function normal(a, a) { 190 | return a + a; 191 | } 192 | console.log(normal(1, 2)); // 4 193 | ``` 194 | 195 | - 단, strict mode에서 중복된 매개변수 이름을 선언하면 에러가 발생한다. 196 | 197 | ```js 198 | 'use strict'; 199 | 200 | function normal(a, a) { 201 | return a + a; 202 | } 203 | // SyntaxError: Duplicate parameter name not allowed in this context 204 | ``` 205 | 206 | - 화살표 함수에서도 중복된 매개변수 이름을 선언하면 에러가 발생한다. 207 | 208 | ```js 209 | const arrow = (a, a) => a + a; 210 | // SyntaxError: Duplicate parameter name not allowed in this context 211 | ``` 212 | 213 | 3. 화살표 함수는 함수 자체의 this, arguments, super, new.target 바인딩을 갖지 않는다. 214 | 215 | - 따라서 화살표 함수 내부에서 this, arguments, super, new.target을 참조하면 스코프 체인을 통해 상위 스코프의 this, arguments, super, new.target을 참조한다. 216 | - 만약 화살표 함수와 화살표 함수가 중첩되어 있다면 상위 화살표 함수에도 this, arguments, super, new.target 바인딩이 없으므로 스코프 체인 상에서 가장 가까운 상위 함수 중에서 화살표 함수가 아닌 함수의 this, arguments, super, new.target을 참조한다. 217 | 218 | ## 27장 배열 219 | 220 | ### 27.1 배열이란? 221 | 222 | 배열은 여러 개의 값을 순차적으로 나열한 자료구조다. 배열이 가지고 있는 값을 **요소**라고 부른다. 자바스크립트의 모든 값은 요소가 될 수 있다. 배열의 요소는 배열에서 자신의 위치를 나타내는 0 이상의 정수인 **인덱스**를 갖는다. 인덱스는 배열의 요소에 접근할 때 사용한다. 배열은 요소의 개수, 즉 배열의 길이를 나타내는 **length 프로퍼티**를 갖는다. 223 | 224 | 자바스크립트에 배열이라는 타입은 존재하지 않는다. 배열은 객체 타입이다. 배열은 객체지만 일반 객체와는 구별되는 독특한 특징이 있다. 225 | 226 | ![images](https://user-images.githubusercontent.com/48766355/201521626-1bf99f07-94f9-4634-aca4-cf3c1a186da3.png) 227 | 228 | ### 27.2 자바스크립트 배열은 배열이 아니다 229 | 230 | 자료구조에서 말하는 배열은 동일한 크기의 메모리 공간이 빈틈없이 연속적으로 나열된 자료구조를 말한다. 즉, 배열의 요소는 하나의 데이터 타입으로 통일되어 있으며 서로 연속적으로 인접해 있다. 이러한 배열을 **밀집 배열**이라 한다. 231 | 232 | ![images](https://user-images.githubusercontent.com/48766355/201521693-a11c231d-72a8-447a-8eef-199f6805d168.png) 233 | 234 | 이처럼 일반적인 의미의 배열은 각 요소가 동일한 데이터 크기를 가지며, 빈틈없이 연속적으로 이어져 있으므로 다음과 같이 인덱스를 통해 단 한 번의 연산으로 임의의 요소에 접근할 수 있다. 이는 매우 효율적이며, 고속으로 동작한다. 235 | 236 | > **검색 대상 요소의 메모리 주소 = 배열의 시작 메모리 주소 + 인덱스 \* 요소의 바이트 수** 237 | 238 | 배열은 인덱스를 통해 효율적으로 요소에 접근할 수 있다는 장점이 있다. 하지만 정렬되지 않은 배열에서 특정한 요소를 검색하는 경우 배열의 모든 요소를 처음부터 특정 요소를 발견할 때까지 차례대로 검색해야 한다. 239 | 240 | 또한 배열에 요소를 삽입하거나 삭제하는 경우 배열의 요소를 연속적으로 유지하기 위해 요소를 이동시켜야 하는 단점도 있다. 241 | 242 | ![images](https://user-images.githubusercontent.com/48766355/201521843-02977732-adcd-4ff1-a343-790d1f5fa975.png) 243 | 244 | 자바스크립트의 배열은 지금까지 살펴본 자료구조에서 말하는 일반적인 의미의 배열과 다르다. 즉, 배열의 요소를 위한 각각의 메모리 공간은 동일한 크기를 갖지 않아도 되며, 연속적으로 이어져 있지 않을 수도 있다. 배열의 요소가 연속적으로 이어져 있지 않는 배열을 **희소 배열**이라 한다. 245 | 246 | 이처럼 자바스크립트의 배열은 엄밀히 말해 일반적 의미의 배열이 아니다. **자바스크립트의 배열은 일반적인 배열의 동작을 흉내 낸 특수한 객체다.** 247 | 248 | 일반적인 배열과 자바스크립트 배열의 장단점을 정리해보면 다음과 같다. 249 | 250 | - 일반적인 배열은 인덱스로 요소에 빠르게 접근할 수 있다. 하지만 요소를 삽입 또는 삭제하는 경우에는 효율적이지 않다. 251 | - 자바스크립트 배열은 해시 테이블로 구현된 객체이므로 인덱스로 요소에 접근하는 경우 일반적인 배열보다 성능적인 면에서 느릴 수밖에 없는 구조적인 단점이 있다. 하지만 요소를 삽입 또는 삭제하는 경우에는 일반적인 배열보다 빠른 성능을 기대할 수 있다. 252 | 253 | 자바스크립트 배열은 인덱스로 배열 요소에 접근하는 경우에는 일반적인 배열보다 느리지만 요소를 삽입 또는 삭제하는 경우에는 일반적인 배열보다 빠르다. 자바스크립트 배열은 인덱스로 접근하는 경우의 성능 대신 배열 요소를 삽입 또는 삭제하는 경우의 성능을 선택한 것이다. 254 | 255 | 인덱스로 배열 요소에 접근할 때 일반적인 배열보다 느릴 수밖에 없는 구조적인 단점을 보완하기 위해 대부분의 모던 자바스크립트 엔진은 배열을 일반 객체와 구별하여 좀 더 배열처럼 동작하도록 최적화하여 구현했다. 256 | 257 | ### 27.3 length 프로퍼티와 희소 배열 258 | 259 | length 프로퍼티 값은 요소의 개수, 즉 배열의 길이를 바탕으로 결정되지만 임의의 숫자 값을 명시적으로 할당할 수도 있다. 현재 length 프로퍼티 값보다 작은 숫자 값을 할당하면 배열의 길이가 줄어든다. 260 | 261 | ```js 262 | const arr = [1, 2, 3, 4, 5]; 263 | 264 | // 현재 length 프로퍼티 값인 5보다 작은 숫자 값 3을 length 프로퍼티에 할당 265 | arr.length = 3; 266 | 267 | // 배열의 길이가 5에서 3으로 줄어든다. 268 | console.log(arr); // [1, 2, 3] 269 | ``` 270 | 271 | 주의할 것은 현재 length 프로퍼티 값보다 큰 숫자 값을 할당하는 경우다. 이때 length 프로퍼티 값은 변경되지만 실제로 배열의 길이가 늘어나지는 않는다. 272 | 273 | ```js 274 | const arr = [1]; 275 | 276 | // 현재 length 프로퍼티 값인 1보다 큰 숫자 값 3을 length 프로퍼티에 할당 277 | arr.length = 3; 278 | 279 | // length 프로퍼티 값은 변경되지만 실제로 배열의 길이가 늘어나지는 않는다. 280 | console.log(arr.length); // 3 281 | console.log(arr); // [1, empty × 2] 282 | ``` 283 | 284 | 현재 length 값보다 큰 숫자 값을 length 프로퍼티에 할당하는 경우 length 프로퍼티 값은 성공적으로 변경되지만 실제 배열에는 아무런 변함이 없다. 값 없이 비어 있는 요소를 위해 메모리 공간을 확보하지 않으며 빈 요소를 생성하지도 않는다. 285 | 286 | ```js 287 | console.log(Object.getOwnPropertyDescriptors(arr)); 288 | /* 289 | { 290 | '0': {value: 1, writable: true, enumerable: true, configurable: true}, 291 | length: {value: 3, writable: true, enumerable: false, configurable: false} 292 | } 293 | */ 294 | ``` 295 | 296 | 이처럼 배열의 요소가 연속적으로 위치하지 않고 일부가 비어 있는 배열을 희소 배열이라 한다. 자바스크립트는 희소 배열을 문법적으로 허용한다. 297 | 298 | ```js 299 | // 희소 배열 300 | const sparse = [, 2, , 4]; 301 | 302 | // 희소 배열의 length 프로퍼티 값은 요소의 개수와 일치하지 않는다. 303 | console.log(sparse.length); // 4 304 | console.log(sparse); // [empty, 2, empty, 4] 305 | 306 | // 배열 sparse에는 인덱스가 0, 2인 요소가 존재하지 않는다. 307 | console.log(Object.getOwnPropertyDescriptors(sparse)); 308 | /* 309 | { 310 | '1': { value: 2, writable: true, enumerable: true, configurable: true }, 311 | '3': { value: 4, writable: true, enumerable: true, configurable: true }, 312 | length: { value: 4, writable: true, enumerable: false, configurable: false } 313 | } 314 | */ 315 | ``` 316 | 317 | 일반적인 배열의 length는 배열 요소의 개수, 즉 배열의 길이와 언제나 일치한다. 하지만 **희소 배열은 length와 배열 요소의 개수가 일치하지 않는다. 희소 배열의 length는 희소 배열의 실제 요소 개수보다 언제나 크다.** 318 | 319 | 자바스크립트는 문법적으로 희소 배열을 허용하지만 희소 배열은 사용하지 않는 것이 좋다. 의도적으로 희소 배열을 만들어야 하는 상황은 발생하지 않는다. 희소 배열은 연속적인 값의 집합이라는 배열의 기본적인 개념과 맞지 않으며, 성능에도 좋지 않은 영향을 준다. 최적화가 잘 되어 있는 모던 자바스크립트 엔진은 요소의 타입이 일치하는 배열을 생성할 때 일반적인 의미의 배열처럼 연속된 메모리 공간을 확보하는 것으로 알려져 있다. 320 | 321 | 배열을 생성할 경우에는 희소 배열을 생성하지 않도록 주의하자. **배열에는 같은 타입의 요소를 연속적으로 위치시키는 것이 최선이다.** 322 | -------------------------------------------------------------------------------- /7회차/이병현.md: -------------------------------------------------------------------------------- 1 | 2 | ## 함수의 구분 3 | 4 | ```js 5 | var foo = function () { 6 | return 1; 7 | }; 8 | 9 | // 일반적인 함수로서 호출 10 | foo(); // -> 1 11 | 12 | // 생성자 함수로서 호출 13 | new foo(); // -> foo {} 14 | 15 | // 메서드로서 호출 16 | var obj = { foo: foo }; 17 | obj.foo(); // -> 1 18 | ``` 19 | 20 | 함수를 다양한 방식으로 사용할 수 있다. 또한, 21 | 22 | _ES6 이전의 모든 함수는 일반 함수로서 호출할 수 있는 것은 물론 생성자 함수로써 호출할 수 있다._ 23 | 24 | ES6 이후로 메서드, 화살표 함수가 등장하며 기존의 일반 함수와는 다른 형태를 가진 함수의 형태가 생기게 된다. 25 | 26 | ## 메서드 27 | 28 | 메서드는 인스턴스를 생성할 수 없는 `non-constructor`이다. 자신을 바인딩한 객체를 가리키는 내부 슬롯 `[[HomeObject]]`를 가지며, `super`참조는 내부 슬롯 `[[HomeObject]]`를 사용하여 수퍼클래스의 메서드를 참조한다. 29 | 30 | 31 | ```js 32 | const base = { 33 | name: 'Lee', 34 | sayHi() { 35 | return `Hi! ${this.name}`; 36 | } 37 | }; 38 | 39 | const derived = { 40 | __proto__: base, 41 | // sayHi는 ES6 메서드다. ES6 메서드는 [[HomeObject]]를 갖는다. 42 | // sayHi의 [[HomeObject]]는 sayHi가 바인딩된 객체인 derived를 가리키고 43 | // super는 sayHi의 [[HomeObject]]의 프로토타입인 base를 가리킨다. 44 | sayHi() { 45 | return `${super.sayHi()}. how are you doing?`; 46 | } 47 | }; 48 | 49 | console.log(derived.sayHi()); // Hi! Lee. how are you doing? 50 | ``` 51 | 52 | 53 | ## 화살표 함수 54 | 55 | ```js 56 | const Foo = () => {}; 57 | // 화살표 함수는 생성자 함수로서 호출할 수 없다. 58 | new Foo(); // TypeError: Foo is not a constructor 59 | ``` 60 | 61 | 화살표 함수는 생성자 함수로서 호출할 수 없다. 62 | 63 | ```js 64 | 65 | // 화살표 함수는 상위 스코프의 this를 참조한다. 66 | () => this.x; 67 | 68 | // 익명 함수에 상위 스코프의 this를 주입한다. 위 화살표 함수와 동일하게 동작한다. 69 | (function () { return this.x; }).bind(this); 70 | ``` 71 | 72 | 화살표 함수는 `this`바인딩이 존재하지 않지만 내부에서 `this`를 호출하면 상위 스코프의 `this`를 찾게된다. 73 | 74 | 75 | ## Rest 파라미터 76 | 77 | ```js 78 | function foo(param, ...rest) { 79 | console.log(param); // 1 80 | console.log(rest); // [ 2, 3, 4, 5 ] 81 | } 82 | 83 | foo(1, 2, 3, 4, 5); 84 | 85 | function bar(param1, param2, ...rest) { 86 | console.log(param1); // 1 87 | console.log(param2); // 2 88 | console.log(rest); // [ 3, 4, 5 ] 89 | } 90 | 91 | bar(1, 2, 3, 4, 5); 92 | ``` 93 | 94 | 함수에 전달된 인수들의 목록을 배열로 전달받는다. 95 | 96 | ## 매개변수 기본값 97 | 98 | ```js 99 | function logName(name = 'Lee') { 100 | console.log(name); 101 | } 102 | 103 | logName(); // Lee 104 | logName(undefined); // Lee 105 | logName(null); // null 106 | ``` 107 | 108 | ES6에 도입된 매개변수 기본값은 `undefined`인 경우에만 가능하다. (기본적으로 `undefined`를 가지며 `null`인 경우 무시됨) 109 | 110 | 111 | 112 | ## 자바스크립트 배열은 배열이 아니다 113 | 114 | 자바스크립트의 배열은 일반적인 배열처럼 각각의 요소가 동일한 크기를 가지지 않아도 되며, 연속적으로 이어져 있지 않을 수도 있으며 이렇게 이어져있지 않는 배열을 **희소 배열(sparse array)** 라 한다. 115 | 116 | 또한 일반적인 배열의 동작을 흉내낸 객체이다. 해시 테이블로 구현된 객체이므로 인덱스로 접근하는 일반적 배열보다 느릴 수 있지만, 삽입, 삭제시에는 빠를 수 있다. 117 | 118 | 하지만, 모던 자바스크립트 엔진은 배열을 좀 더 일반적인 배열처럼 최적화하기 때문에 일반 객체보다 2배 정도 빠르다고 한다. 119 | 120 | ```js 121 | const arr = []; 122 | 123 | console.time('Array Performance Test'); 124 | 125 | for (let i = 0; i < 10000000; i++) { 126 | arr[i] = i; 127 | } 128 | console.timeEnd('Array Performance Test'); 129 | // 약 340ms 130 | 131 | const obj = {}; 132 | 133 | console.time('Object Performance Test'); 134 | 135 | for (let i = 0; i < 10000000; i++) { 136 | obj[i] = i; 137 | } 138 | 139 | console.timeEnd('Object Performance Test'); 140 | // 약 600ms 141 | ``` 142 | 143 | ## 유사 배열 객체 144 | 145 | ```js 146 | // 유사 배열 객체 147 | const arrayLike = { 148 | '0': 'apple', 149 | '1': 'banana', 150 | '2': 'orange', 151 | length: 3 152 | }; 153 | 154 | // 유사 배열 객체는 마치 배열처럼 for 문으로 순회할 수도 있다. 155 | for (let i = 0; i < arrayLike.length; i++) { 156 | console.log(arrayLike[i]); // apple banana orange 157 | } 158 | ``` 159 | 160 | 객체같은 형태를 지니고 있으면, 마치 배열 처럼 for문을 돌 수도 있다. 161 | 162 | -------------------------------------------------------------------------------- /7회차/천승아.md: -------------------------------------------------------------------------------- 1 | # ✍️ 공부 내용 정리 2 | 3 | ## 6장 ES6 함수의 추가 기능 4 | 5 | ### 26-1. 함수의 구분 6 | 7 | - ES6 이전의 함수는 일반 함수로써 호출, 생성자 함수로써 호출이 가능하다. -> callable && constructor 8 | - 객체에 바인딩 된 함수(메서드)와 콜백 함수도 마찬가지이다. 9 | - 바인딩 된 함수가 prototype 프로퍼티를 가지며 프로토타입 객체를 생성한다. 10 | 11 | ```js 12 | // 프로퍼티 f에 바인딩된 함수는 callable이며 constructor다. 13 | var obj = { 14 | x: 10, 15 | f: function () { 16 | return this.x; 17 | }, 18 | }; 19 | 20 | // 프로퍼티 f에 바인딩된 함수를 메서드로서 호출 21 | console.log(obj.f()); // 10 22 | 23 | // 프로퍼티 f에 바인딩된 함수를 일반 함수로서 호출 24 | var bar = obj.f; 25 | console.log(bar()); // undefined 26 | 27 | // 프로퍼티 f에 바인딩된 함수를 생성자 함수로서 호출 28 | console.log(new obj.f()); // f {} 29 | ``` 30 | 31 | - ES6 이전 함수는 목적에 따라 구분이 없이 사용하여 호출 방식에 제약이 없고, 생성자 함수로 호출하지 않아도 프로토타입 객체를 생성한다. 32 | - ES6는 함수 사용 목적에 따라 세 가지 종류로 구분함 33 | 1. 일반 함수: constructor, prototype, arguments 34 | 2. 메서드: super, arguments 35 | 3. 화살표 함수 36 | 37 | ### 26-2. 메서드 38 | 39 | - ES6 사양에서 메서드: 메서드 축약 표현으로 정의된 함수 40 | - 본연의 기능(super)를 추가하고 의미적으로 맞지 않는 기능(constructor) 제거 41 | 42 | ```js 43 | const obj = { 44 | x: 1, 45 | // foo는 메서드이다. 46 | foo() { 47 | return this.x; 48 | }, 49 | // bar에 바인딩된 함수는 메서드가 아닌 일반 함수이다. 50 | bar: function () { 51 | return this.x; 52 | }, 53 | }; 54 | 55 | console.log(obj.foo()); // 1 56 | console.log(obj.bar()); // 1 57 | ``` 58 | 59 | > 메서드는 non-constructor: 생성자 함수로 호출 불가 / 인스턴스 생성이 불가하므로 prototype 프로퍼티 X, 프로토타입 생성하지 않음 60 | 61 | ```js 62 | new obj.foo(); // -> TypeError: obj.foo is not a constructor 63 | new obj.bar(); // -> bar {} 64 | 65 | // obj.foo는 constructor가 아닌 ES6 메서드이므로 prototype 프로퍼티가 없다. 66 | obj.foo.hasOwnProperty("prototype"); // -> false 67 | 68 | // obj.bar는 constructor인 일반 함수이므로 prototype 프로퍼티가 있다. 69 | obj.bar.hasOwnProperty("prototype"); // -> true 70 | ``` 71 | 72 | > 메서드는 super 키워드를 통해 내부 슬롯 [[HomeObject]]을 사용하여 수퍼클래스의 메서드를 참조함 73 | 74 | ```js 75 | const base = { 76 | name: "Lee", 77 | sayHi() { 78 | return `Hi! ${this.name}`; 79 | }, 80 | }; 81 | 82 | const derived = { 83 | __proto__: base, 84 | // sayHi는 ES6 메서드다. ES6 메서드는 [[HomeObject]]를 갖는다. 85 | // sayHi의 [[HomeObject]]는 sayHi가 바인딩된 객체인 derived를 가리키고 86 | // super는 sayHi의 [[HomeObject]]의 프로토타입인 base를 가리킨다. 87 | sayHi() { 88 | return `${super.sayHi()}. how are you doing?`; 89 | }, 90 | }; 91 | 92 | console.log(derived.sayHi()); // Hi! Lee. how are you doing? 93 | ``` 94 | 95 | ## 26-3. 화살표 함수 96 | 97 | - 표현 방식, 내부 동작이 기존 함수보다 간략한 함수 정의 방법 98 | - 콜백 함수 내에서 this의 전역 객체 참조를 해결하기 위한 대안 99 | 100 | > 화살표 함수도 즉시 실행 함수로 사용할 수 있다. 101 | 102 | ```js 103 | // 1. person에 name 인수를 sayHi()에서 노출하는 메서드를 갖는 객체를 반환 104 | // 2. person 객체의 sayHi 호출 105 | const person = ((name) => ({ 106 | sayHi() { 107 | return `Hi? My name is ${name}.`; 108 | }, 109 | }))("Lee"); 110 | 111 | console.log(person.sayHi()); // Hi? My name is Lee. 112 | ``` 113 | 114 | #### 1. 화살표 함수, 일반 함수 차이 115 | 116 | 1. 화살표 함수는 non-constructor -> 생성자 X, prototype / 프로토타입 X 117 | 2. strict mode가 아니어도 중복된 매개변수 선언은 에러 발생 118 | 3. 함수 자체의 this, arguments, super, new.target 바인딩을 갖지 않음 119 | 120 | - 스코프 체인을 통해 상위 스코프의 바인딩 참조 121 | 122 | #### 2. this 123 | 124 | - 화살표 함수, 일반 함수 구별되는 가장 큰 특징 125 | - 콜백 함수 내부 this 문제에 유용 126 | 127 | ```js 128 | class Prefixer { 129 | constructor(prefix) { 130 | this.prefix = prefix; 131 | } 132 | 133 | add(arr) { 134 | // add 메서드는 인수로 전달된 배열 arr을 순회하며 배열의 모든 요소에 prefix를 추가한다. 135 | // ① 136 | return arr.map(function (item) { 137 | return this.prefix + item; // ② 138 | // -> TypeError: Cannot read property 'prefix' of undefined 139 | // -> 콜백 함수에서 this는 전역 객체 -> but 클래스 내부에서는 strict mode 적용 -> undefined로 할당 140 | }); 141 | } 142 | } 143 | 144 | const prefixer = new Prefixer("-webkit-"); 145 | console.log(prefixer.add(["transition", "user-select"])); 146 | ``` 147 | 148 | - ES6 이전에는 아래와 같은 방법으로 해결 149 | 1. this를 that에 회피 150 | 2. Array.prototype.map 두 번째 인수로 this 전달 151 | 3. Function.prototype.bind 메서드로 this 바인딩 152 | - ES6는 화살표 함수로 이를 해결한다. 153 | - 화살표 함수는 함수 자체의 this 바인딩을 갖지 않아 상위 스코프의 this를 그대로 참조함 154 | - 렉시컬 스코프처럼 this가 함수가 정의된 위치에 따라 결정 된다는 것을 의미함. 155 | 156 | 1. 화살표 함수가 중첩되어 있다면 스코프 체인상에서 가장 가까운 **상위 함수 중 화살표 함수가 아닌 함수의 this를 참조**한다. 157 | 158 | ```js 159 | // 중첩 함수 foo의 상위 스코프는 즉시 실행 함수다. 160 | // 따라서 화살표 함수 foo의 this는 상위 스코프인 즉시 실행 함수의 this를 가리킨다. 161 | (function () { 162 | const foo = () => console.log(this); 163 | foo(); 164 | }.call({ a: 1 })); // { a: 1 } 165 | 166 | // bar 함수는 화살표 함수를 반환한다. 167 | // bar 함수가 반환한 화살표 함수의 상위 스코프는 화살표 함수 bar다. 168 | // 하지만 화살표 함수는 함수 자체의 this 바인딩을 갖지 않으므로 bar 함수가 반환한 169 | // 화살표 함수 내부에서 참조하는 this는 화살표 함수가 아닌 즉시 실행 함수의 this를 가리킨다. 170 | (function () { 171 | const bar = () => () => console.log(this); 172 | bar()(); 173 | }.call({ a: 1 })); // { a: 1 } 174 | ``` 175 | 176 | 2. 화살표 함수가 전역 함수일 경우 this는 전역 객체 177 | 3. 프로퍼티에 할당한 화살표 함수 역시 가까운 상위 함수 중 this 참조 178 | 4. Function.prototype.call, Function.prototype.apply, Function.prototype.bind 메서드로 this 교체 불가능 -> 함수 자체의 this 바인딩이 없으므로 (호출은 가능) 179 | 180 | > 따라서 ES6에서 메서드, 프로토타입 객체의 프로퍼티를 화살표 함수로 정의하는 것을 피해야 한다. 181 | 182 | ```js 183 | // Bad: 메서드 184 | const person = { 185 | name: "Lee", 186 | sayHi: () => console.log(`Hi ${this.name}`), 187 | }; 188 | 189 | // sayHi 프로퍼티에 할당된 화살표 함수 내부의 this는 상위 스코프인 전역의 this가 가리키는 190 | // 전역 객체를 가리키므로 이 예제를 브라우저에서 실행하면 this.name은 빈 문자열을 갖는 191 | // window.name과 같다. 전역 객체 window에는 빌트인 프로퍼티 name이 존재한다. 192 | person.sayHi(); // Hi 193 | 194 | --- 195 | 196 | // Bad: 프로토타입 객체 프로퍼티 197 | function Person(name) { 198 | this.name = name; 199 | } 200 | 201 | Person.prototype.sayHi = () => console.log(`Hi ${this.name}`); 202 | 203 | const person = new Person("Lee"); 204 | // 이 예제를 브라우저에서 실행하면 this.name은 빈 문자열을 갖는 window.name과 같다. 205 | person.sayHi(); // Hi 206 | ``` 207 | 208 | > ES6에서 메서드, 프로토타입 객체 프로퍼티는 아래와 같이 정의해야 한다. 209 | 210 | - 메서드: ES6 메서드 축약 표현으로 대체 211 | - 프로토타입 프로퍼티 212 | - 일반 함수를 할당 213 | - 객체 리터럴을 바인딩하고 프로토타입의 constructor 프로퍼티, 생성자 함수 간 연결 재설정 -> 예제 **26-43** 214 | 215 | > 클래스 메서드를 정의할 때에도 ES6 축약 표현으로 메서드를 생성하는 것이 좋다. 216 | 217 | - 클래스 필드에 할당한 화살표 함수는 프로토타입 메서드가 아니라 인스턴스 메서드가 되기 때문 218 | 219 | ```js 220 | // 이렇게 보다 221 | class Person { 222 | constructor() { 223 | this.name = "Lee"; 224 | // 클래스가 생성한 인스턴스(this)의 sayHi 프로퍼티에 화살표 함수를 할당한다. 225 | // sayHi 프로퍼티는 인스턴스 프로퍼티이다. 226 | this.sayHi = () => console.log(`Hi ${this.name}`); 227 | } 228 | } 229 | 230 | // 요렇게! 231 | class Person { 232 | // 클래스 필드 정의 233 | name = "Lee"; 234 | 235 | sayHi() { 236 | console.log(`Hi ${this.name}`); 237 | } 238 | } 239 | const person = new Person(); 240 | person.sayHi(); // Hi Lee 241 | ``` 242 | 243 | #### 3. super 244 | 245 | - 화살표 함수는 자체의 super 바인딩을 갖지 않아 this와 마찬가지로 상위 스코프의 super를 참조한다. 246 | 247 | #### 4. arguments 248 | 249 | - 화살표 함수는 자체의 arguments 바인딩을 갖지 않아 this와 마찬가지로 상위 스코프의 arguments를 참조한다. 250 | - 화살표 함수는 상위 스코프의 arguments 객체 참조는 가능하지만, 자신에게 전달된 인수 목록을 확인할 수 없고 상위 함수에게 전달된 인수 목록을 참조하므로 큰 도움이 되지 않는다. 251 | -> 대신 rest 파라미터 사용 252 | 253 | ```js 254 | (function () { 255 | // 화살표 함수 foo의 arguments는 상위 스코프인 즉시 실행 함수의 arguments를 가리킨다. 256 | const foo = () => console.log(arguments); // [Arguments] { '0': 1, '1': 2 } 257 | foo(3, 4); 258 | })(1, 2); 259 | 260 | // 화살표 함수 foo의 arguments는 상위 스코프인 전역의 arguments를 가리킨다. 261 | // 하지만 전역에는 arguments 객체가 존재하지 않는다. arguments 객체는 함수 내부에서만 유효하다. 262 | const foo = () => console.log(arguments); 263 | foo(1, 2); // ReferenceError: arguments is not defined 264 | ``` 265 | 266 | ## 26-4. Rest 파라미터 267 | 268 | - 함수에 전달된 인수들 목록을 배열로 전달 받음 269 | - rest 파라미터는 함수 객체 length 프로퍼티에 영향을 주지 않는다. 270 | - length: 함수 정의 시 선언한 매개변수 개수 271 | 272 | ```js 273 | function foo(...rest) {} 274 | console.log(foo.length); // 0 275 | 276 | function bar(x, ...rest) {} 277 | console.log(bar.length); // 1 278 | 279 | function baz(x, y, ...rest) {} 280 | console.log(baz.length); // 2 281 | ``` 282 | 283 | > Rest 파라미터와 arguments 객체 284 | 285 | 1. arguments 객체 286 | 287 | - 함수 호출 시 전달할 인수들의 정보를 담은 유사 배열 객체 -> 지역 변수처럼 사용 288 | - 유서 배열이므로 Function.prototype.call, Function.prototype.apply 메서드로 arguments 를 배열로 변환해야 하는 번거로움 289 | 290 | ```js 291 | function sum() { 292 | // 유사 배열 객체인 arguments 객체를 배열로 변환한다. 293 | var array = Array.prototype.slice.call(arguments); 294 | 295 | return array.reduce(function (pre, cur) { 296 | return pre + cur; 297 | }, 0); 298 | } 299 | 300 | console.log(sum(1, 2, 3, 4, 5)); // 15 301 | ``` 302 | 303 | 2. rest 파라미터 304 | 305 | - 가변 인자 인수 목록을 배열로 직접 전달받음 -> 배열 변환할 필요 X 306 | 307 | -> 화살표 함수는 가변 인자 함수 구현 시 반드시 Rest 파라미터 사용해야 한다. 308 | 309 | ## 26-5. 매개변수 기본값 310 | 311 | - ES6부터 도입 312 | - 기본값은 **인수를 전달하지 않은 경우, undefined 전달한 경우**에만 유효함 313 | - rest 파라미터는 기본값 지정할 수 없음 314 | - 함수 객체 length 프로퍼티에 영향 주지 않음 315 | 316 | --- 317 | 318 | ## 🔗 참고 319 | 320 | - [https://ko.javascript.info/iterable](https://ko.javascript.info/iterable) 321 | -------------------------------------------------------------------------------- /8회차/천승아.md: -------------------------------------------------------------------------------- 1 | # ✍️ 공부 내용 정리 2 | 3 | ## 28장 Number 4 | 5 | 표준 빌트인 객체 Number는 숫자를 다룰 때 유용한 프로퍼티와 메서드를 제공한다. 6 | 7 | ### Number 생성자 함수 8 | 9 | - 인수 전달하지 않고 new + 호출하면 0을 할당한 인스턴스 반환 10 | - new 연산자 없이 호출하면 숫자를 반환 11 | 12 | ```js 13 | const numObj = new Number(); 14 | console.log(numObj); // Number {[[PrimitiveValue]]: 0} 15 | ``` 16 | 17 | ### Number 프로퍼티 18 | 19 | - Number.MAX_VALUE (< Infinity), Number.MIN_VALUE (> 0), 20 | - Number.MAX_SAFE_INTEGER, Number.MIN_SAFE_INTEGER 21 | - Number.POSITIVE_INFINITY (== InFinity), Number.NEGATIVE_INFINITY (== -Infinity) 22 | 23 | ### Number 메서드 24 | 25 | - Number.isFinite: 정상적인 유한수인 지 검사 (암묵적 변환 X) 26 | - Number.isInteger: 정수 검사 (암묵적 변환 X) 27 | - Number.prototype.toFixed: 숫자 반올림하여 문자열로 반환 28 | 29 | --- 30 | 31 | ## 29장 Math 32 | 33 | Math는 생성자 함수가 아니며, 수학적인 상수와 함수를 위한 프로퍼티와 메서드를 제공한다. 34 | 35 | ### Math 메서드 36 | 37 | - Math.abs: 절대값 반환 38 | - Math.round, Math.ceil, Match.floor 39 | - Math.sqrt, Math.random, Math.pow 40 | - Math.max: 인수 중 가장 큰 값 반환 -> 인수 없으면 -Infinity 41 | 42 | ```js 43 | // 배열 요소 중에서 최대값 취득 44 | Math.max.apply(null, [1, 2, 3]); // -> 3 45 | 46 | // ES6 스프레드 문법 47 | Math.max(...[1, 2, 3]); // -> 3 48 | ``` 49 | 50 | - Math.min: 인수 중 가장 작은 값 반환 -> 인수 없으면 Infinity 51 | 52 | ```js 53 | // 배열 요소 중에서 최소값 취득 54 | Math.min.apply(null, [1, 2, 3]); // -> 1 55 | 56 | // ES6 스프레드 문법 57 | Math.min(...[1, 2, 3]); // -> 1 58 | ``` 59 | 60 | --- 61 | 62 | ## 30장 Date 63 | 64 | - Date는 날짜/시간을 위한 메서드를 제공하는 빌트인 객체 or 생성자 함수이다. 65 | - UTC(협정 세계시) == GMT(그리니치 평균시) 66 | - KST(한국 표준시): UTC + 9:00 -> KST는 UTC보다 9시간이 빠르다. 67 | - ex) UTC 00:00 AM == KST 09:00 AM 68 | - 현재 날짜 / 시간은 JS가 실행된 시스템 시계에 의해 결정 69 | 70 | ### Date 생성자 함수 71 | 72 | - new Date(datestring) 73 | 74 | ```js 75 | new Date("May 26, 2020 10:00:00"); 76 | // -> Tue May 26 2020 10:00:00 GMT+0900 (대한민국 표준시) 77 | 78 | new Date("2020/03/26/10:00:00"); 79 | // -> Thu Mar 26 2020 10:00:00 GMT+0900 (대한민국 표준시) 80 | ``` 81 | 82 | - new Date(year, month[, day, hour, minute, second, millisecond]) 83 | 84 | ```js 85 | // 월을 나타내는 2는 3월을 의미한다. 2020/3/1/00:00:00:00 86 | new Date(2020, 2); 87 | // -> Sun Mar 01 2020 00:00:00 GMT+0900 (대한민국 표준시) 88 | 89 | // 월을 나타내는 2는 3월을 의미한다. 2020/3/26/10:00:00:00 90 | new Date(2020, 2, 26, 10, 00, 00, 0); 91 | // -> Thu Mar 26 2020 10:00:00 GMT+0900 (대한민국 표준시) 92 | 93 | // 다음처럼 표현하면 가독성이 훨씬 좋다. 94 | new Date("2020/3/26/10:00:00:00"); 95 | // -> Thu Mar 26 2020 10:00:00 GMT+0900 (대한민국 표준시) 96 | ``` 97 | 98 | ### Date 메서드 99 | 100 | - Date.prototype.getTimezoneOffset: UTC - 객체 로컬 시간 차이를 분 단위로 반환 101 | 102 | ```js 103 | const today = new Date("2020/7/24/12:30"); 104 | 105 | today.toString(); // -> Fri Jul 24 2020 12:30:00 GMT+0900 (대한민국 표준시) 106 | today.toDateString(); // -> Fri Jul 24 2020 107 | ``` 108 | 109 | - Date.prototype.toTimeString: 사람이 읽을 수 있는 형식으로 Date 객체 **시간을 포함**한 문자열 반환 110 | - Date.prototype.toISOString: ISO 형식으로 날짜/시간 표현한 문자열 반환 111 | 112 | --- 113 | 114 | ## 31장 RegExp 115 | 116 | - 패턴을 가진 문자열의 집합을 표현하기 위해 사용하는 형식 언어 (JS는 ES3부터 도입) 117 | - 문자열 대상으로 패턴 매칭 기능 118 | 119 | ### 정규 표현식의 생성 120 | 121 | - `/regexp/i` 122 | - /: 시작, 종료 기호 123 | - regexp: 패턴 124 | - i: 플래그 (대소문자 구별 X) 125 | 126 | ### RegExp 메서드 127 | 128 | - RegExp.prototype.exec: 매칭 결과를 배열로 반환 (없으면 null) 129 | - g 플래그 지정해도 첫 번째 매칭 결과만 반환 130 | 131 | ```js 132 | const target = "Is this all there is?"; 133 | const regExp = /is/; 134 | 135 | regExp.exec(target); // -> ["is", index: 5, input: "Is this all there is?", groups: undefined] 136 | ``` 137 | 138 | - String.prototype.match: 정규 표현식과의 매칭 결과를 배열로 반환 (exec와 유사) 139 | - g 플래그 지정하면 모든 매칭 결과를 배열로 반환 140 | 141 | ```js 142 | const target = "Is this all there is?"; 143 | const regExp = /is/g; 144 | 145 | target.match(regExp); // -> ["is", "is"] 146 | ``` 147 | 148 | ### 플래그 149 | 150 | - 정규 표현식의 검색 방식 결정 151 | 1. i: 대소문자 구별 X 152 | 2. g: 패턴과 일치하는 모든 문자열 전역 검색 153 | 3. m: 행의 바뀌어도 패턴 검색 계속 함 154 | 155 | ### 문자열 검색 156 | 157 | > 임의의 문자열 검색 158 | 159 | - .: 무엇이든 한 개의 문자 160 | 161 | ```js 162 | const target = "Is this all there is?"; 163 | 164 | // 임의의 3자리 문자열을 대소문자를 구별하여 전역 검색한다. 165 | const regExp = /.../g; 166 | 167 | target.match(regExp); // -> ["Is ", "thi", "s a", "ll ", "the", "re ", "is?"] 168 | ``` 169 | 170 | > 반복 검색 171 | 172 | - {m,n}: 최소 m번, 최대 n번 반복되는 문자열 의미 173 | 174 | ```js 175 | const target = "A AA B BB Aa Bb AAA"; 176 | 177 | // 'A'가 최소 2번 이상 반복되는 문자열을 전역 검색한다. 178 | const regExp = /A{2,}/g; 179 | 180 | target.match(regExp); // -> ["AA", "AAA"] 181 | ``` 182 | 183 | - `A+`: +는 {1,}과 같으며 `A`가 한번 이상 반복되는 문자열을 의미함 184 | 185 | ```js 186 | const target = "A AA B BB Aa Bb AAA"; 187 | 188 | // 'A'가 최소 한 번 이상 반복되는 문자열('A, 'AA', 'AAA', ...)을 전역 검색한다. 189 | const regExp = /A+/g; 190 | 191 | target.match(regExp); // -> ["A", "AA", "A", "AAA"] 192 | ``` 193 | 194 | `?`: 앞선 패턴이 최대 한 번(0번 포함) 이상 반복되는 문자열 -> ?는 {0,1}과 같다. 195 | 196 | ```js 197 | const target = "color colour"; 198 | 199 | // 'colo' 다음 'u'가 최대 한 번(0번 포함) 이상 반복되고 'r'이 이어지는 문자열 'color', 'colour'를 전역 검색한다. 200 | const regExp = /colou?r/g; 201 | 202 | target.match(regExp); // -> ["color", "colour"] 203 | ``` 204 | 205 | - 더 많은 예제: 예제 31-19 ~ 31-30 206 | 207 | ### 자주 사용하는 정규표현식 208 | 209 | 예제 31-31 ~ 31-42 210 | 211 | --- 212 | 213 | ## 32장 String 214 | 215 | 표준 빌트인 객체 String은 원시 타입 문자열을 다룰 때 유용한 프로퍼티와 메서드를 제공한다. 216 | 217 | ### String 생성자 함수 218 | 219 | - new + 생성자 함수 호출: String 인스턴스 객체 생성 220 | - new + 인수 없음 호출: 빈 문자열 할당한 String 래퍼 객체 생성 221 | - new 없이 호출: 문자열 반환 == 명시적 타입 변환 222 | - 인수 문자열 강제 변환 223 | 224 | ### String 메서드 225 | 226 | > 배열: 원본 직접 변경 메서드 / 새로운 배열 생성하여 반환하는 메서드 있음
String 객체: 언제나 새로운 문자열 반환 == 문자열은 원시 값 == **String 래퍼 객체는 읽기 전용**으로 제공 227 | 228 | - String.prototype.search: 정규 표현식 매치 문자열 반환 229 | - String.prototype.charAt: 인덱스가 범위 벗어나면 빈 문자열 반환 230 | - String.prototype.repeat: 문자열을 정수만큼 반복해 연결된 새로운 문자열 반환 231 | - String.prototype.replace 232 | -------------------------------------------------------------------------------- /9회차/README.md: -------------------------------------------------------------------------------- 1 | # 공유자료 2 | 3 | ## TC39 4 | - [TC39](https://github.com/tc39/proposals/blob/main/finished-proposals.md) 5 | 6 | ## WeakMap 7 | - [Jotai 라이브러리에서 WeakMap](https://github.com/pmndrs/jotai/blob/72a49060a2bef28072ea8c61d43c4a4509c84e90/src/core/store.ts#L169) 8 | - Map과 WeakMap의 차이를 공부해보아요 -------------------------------------------------------------------------------- /images/img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hy57in/study-javascript-deep-dive/aed951621cd80b2830fc238d625027502e88d41c/images/img.png -------------------------------------------------------------------------------- /study-rules.md: -------------------------------------------------------------------------------- 1 | # 스터디 룰 정하기 2 | 3 | ## 스터디 시간 4 | 5 | 일시 : 매주 일요일 22:00 ~ 23:00 6 | 7 | 장소 : 게더타운 8 | 9 | ## 스터디 방식 10 | 11 | ### 스터디 공통 목표 12 | 13 | 1. 꾸준히 공부한다. 14 | 2. 인사이트를 얻는다. 15 | 16 | ### 스터디 전 준비 사항 17 | 18 | - 책 읽기 19 | - 회차 폴더 안에 {이름}.md 파일 안에 공유할 내용 정리하기 20 | - 공부한 내용 정리 21 | - 질문(면접 질문 느낌) 22 | - 토론 주제 적어오기 23 | - 인상 깊은 점 24 | - 새로 알게된 개념 25 | 26 | ### 스터디 진행 방식 27 | 28 | - 룰렛 돌려서 걸린 사람이 진행 29 | - 진행자가 스터디 기록하기 30 | - 진행자가 먼저 공유할 내용(질문) 발표 31 | - 각자 적어온 질문 공유, 답변해보기 32 | - 공부해 온 주제에 대해 자유롭게 토론, 인사이트 공유 33 | - 정적이 흐르면 진행자가 토론 유도 34 | - 4:4 로 나누는것도 고려 35 | 36 | ## 스터디 진행 일정 37 | 38 | | 회차 | 일시 | 목차 | 참여자 | 비고 | 39 | | ---- | ------------------- |----------------|-------------|-----| 40 | | 0 | 9월 25일 (일) 22:00 | 스터디룰 정하기 | 수진,승아 외 All | | 41 | | 1 | 10월 02일 (일) 22:00 | 4,5,6,7,8,9 | | 다같이 | 42 | | 2 | 10월 09일 (일) 22:00 | 10,11,19 | | 43 | | 3 | 10월 16일 (일) 22:00 | 12,18,16,17,26 | | 44 | | 4 | 10월 23일 (일) 22:00 | 13,14,15,23 | | 45 | | 5 | 10월 30일 (일) 22:00 | 20,21,22,24,25 | | 46 | | 6 | 11월 06일 (일) 22:00 | 27,34,35,36,37 | | 47 | | 7 | 11월 13일 (일) 22:00 | 38,39 | | 48 | | 8 | 11월 20일 (일) 22:00 | 40,42,41,43 | | 49 | | 9 | 11월 27일 (일) 22:00 | 45,46,47,48 | | 50 | | 10 | 12월 04일 (일) 22:00 | | | 51 | | 11 | 12월 11일 (일) 22:00 | | | 52 | | 12 | 12월 18일 (일) 22:00 | | | 53 | | 13 | 12월 25일 (일) 22:00 | | | 54 | | 14 | 01월 01일 (일) 22:00 | | | 55 | 56 | ## 스터디 규칙 57 | 58 | - 불참시 벌금 1만원 59 | - 일주일 전에 이야기하면 불참권 차감 X (예정된 일정 등에 사용) 60 | - 진행자로 선정되었는데 준비를 안해왔으면 옐로카드 1장 61 | - 지각은 10분까지 인정. 이후에 들어오면 옐로카드 1장 62 | - 옐로카드 2장 = 벌금 1만원 63 | 64 | ## 진행 목차 65 | 66 | 4. 변수 / 5. 표현식과 문 67 | 6. 데이터 타입 / 7. 연산 68 | 8. 제어문 / 9. 타입변환과 단축평가 69 | 70 | ~123쪽 71 | 72 | 10. 객체 리터럴 / 11. 원시값과 객체의 비교 73 | 74 | 19. 프로토타입 75 | 76 | ~153쪽 77 | 78 | 12. 함수/ 18. 함수와 일급객체 79 | 80 | 16. 프로퍼티 어트리뷰트 / 17. 생성자 함수에 의한 객체 생성 81 | 82 | 26. 함수의 추가 기능 83 | 84 | 13. 스코프 85 | 86 | 14. 전역변수의 문제점 87 | 88 | 15. let, const와 블록 레벨 스코프 89 | 90 | 23. 실행컨텍스트 91 | 92 | 20. strict mode 93 | 94 | 21. 빌트인 객체 95 | 96 | 22. this 97 | 98 | 24. 클로저 99 | 100 | 25. 클래스 101 | 102 | 27. 배열 103 | 104 | 34. 이터러블 105 | 106 | 35. 스프레드 문법 107 | 108 | 36. 디스트럭쳐링 할당 109 | 110 | 37. Set과 Map 111 | 112 | 28. Number / 29. Math / 30. Date / 31. RegExp 113 | 32. String / 33. Symbol 114 | 115 | 38. 브라우저의 렌더링 과정 116 | 117 | 39. DOM 118 | 119 | 40. 이벤트 120 | 42. 비동기 프로그래밍 121 | 41. 타이머 / 43. Ajax 122 | 123 | 45. 프로미스 124 | 46. 제너레이터와 async await 125 | 47. 에러 처리 / 48. 모듈 --------------------------------------------------------------------------------