[제너레이터와 비동기 이터레이션]
1. 제너레이터
일반 함수는 하나의 값(혹은 0개의 값)을 반환
제너레이터(generator)를 사용하면 여러 개의 값을 필요에 따라 하나씩 반환(yield)할 수 있음.
제너레이터와 이터러블 객체를 함께 사용하면 손쉽게 데이터 스트림을 만들 수 있음.
- 제너레이터 함수
특별한 문법구조 function* 이 필요함.
일반 함수와 동작 방식이 다름.
제너레이터 함수를 호출하면 코드가 실행되지 않고, 대신 실행을 처리하는 특별 객체, ‘제너레이터 객체’가 반환됨.
<코드예시>
```javascript
function* generateSequence() {
yield 1;
yield 2;
return 3;
}
// '제너레이터 함수'는 '제너레이터 객체'를 생성합니다.
let generator = generateSequence();
alert(generator); // [object Generator]
```
이 때 generateSequence() 가 다 실행되는 것이 아니라 그냥 함수 생성에서 멈춰버림.
- next() 메서드
next()는 제너레이터의 주요 메서드.
```
next()를 호출하면 가장 가까운 yield 문을 만날 때까지 실행이 지속됨
(value를 생략할 수도 있는데, 이 경우엔 undefined가 된다).
yield 문을 만나면 실행이 멈추고 산출하고자 하는 값인 value가 바깥 코드에 반환됨
```
next() 반환 객체의 두 프로퍼티
* value: 산출 값
* done: 함수 코드 실행이 끝났으면 true, 아니라면 false
<코드 예시="">
```javascript
function* generateSequence() {
yield 1;
yield 2;
return 3;
}
let generator = generateSequence();
let one = generator.next(); // 이 줄에서 generateSequence 줄의 두번째줄에서 실행이 멈추는 것이다.
alert(JSON.stringify(one)); // {value: 1, done: false}
//아래 코드를 추가하면
let two = generator.next();
alert(JSON.stringify(two)); // {value: 2, done: false}
//함수의 세번째 줄에서 멈추게 된다.
let three = generator.next();
alert(JSON.stringify(three)); // {value: 3, done: true}
```
위와 같이 세번째까지 다 넘어가면 함수가 종료 되기 때문에 done 값이 true로 바뀌게 된다.
- 제너레이터와 이터러블
제너레이터는 이터러블이다. 그러므로 for of 반복문을 사용하여 값을 얻을 수 있다.
<코드 예시="">
```javascript
function* generateSequence() {
yield 1;
yield 2;
yield 3;
}
let generator = generateSequence();
for(let value of generator) {
alert(value); // 1, 2, 3
}
```
위와 같이 코딩하면 next()를 안쓰고도 모든 값을 호출할 수 있다.
주의해야할 것은 마짐가 값에 return을 쓰게 되면 done이 true가 되므로 그 값은 반환하지 않게 되는데
yield로 쓰면 반환할 수 있다는 것이다.
* 이터러블 객체이므로 전개 문법 ...의 사용이 가능하다.
- 이터러블 대신 제너레이터 사용하기
옛날에 from... to 사이에 있는 값을 반환하는 range 함수를 만든적이 있었다. (iterable을 공부할 때)
코드는 다음과 같다.
```javascript
let range = {
from: 1,
to: 5,
// for..of 최초 호출 시, Symbol.iterator가 호출됩니다.
[Symbol.iterator]() {
// Symbol.iterator는 이터레이터 객체를 반환합니다.
// for..of는 반환된 이터레이터 객체만을 대상으로 동작하는데, 이때 다음 값도 정해집니다.
return {
current: this.from,
last: this.to,
// for..of 반복문에 의해 각 이터레이션마다 next()가 호출됩니다.
next() {
// next()는 객체 형태의 값, {done:.., value :...}을 반환해야 합니다.
if (this.current <= this.last) {
return { done: false, value: this.current++ };
} else {
return { done: true };
}
}
};
}
};
// 객체 range를 대상으로 하는 이터레이션은 range.from과 range.to 사이의 숫자를 출력합니다.
alert([...range]); // 1,2,3,4,5
```
핵심은 Symbol.iterator를 사용하여 for of 문 실행 시 이터레이터 객체만을 뽑아낸다는 것이다.
위 코드에서 Symbol.iterator를 generator로 바꾸면 다음과 같이 된다.
```javascript
let range = {
from: 1,
to: 5,
*[Symbol.iterator]() { // [Symbol.iterator]: function*()를 짧게 줄임
for(let value = this.from; value <= this.to; value++) {
yield value;
}
}
};
alert( [...range] ); // 1, 2, 3, 4, 5
```
이렇게 간단하게 변환되는 이유는 range[Symbol.iterator]()가 아래 조건을 충족시키기 때문이다.
1).next() 메서드가 있음
2)반환 값의 형태는 {value: ..., done: true/false}이어야 함
제너레이터 자체가 이터레이터를 쉽게 구현하기 위해서 만들어진 것이기 때문에 그런 것임
- 제너레이터 컴포지션
제너레이터 안에 제너레이터를 임베딩 할 수 있게 해주는 기능
먼저 제너레이터 함수를 사용하여 연속된 숫자 생성
```javascript
function* generateSequence(start, end) {
for (let i = start; i <= end; i++) yield i;
}
```
그리고 다음 규칙을 갖는 값을 생성하는 함수를 만들 것임.
* 처음엔 숫자 0부터 9까지를 생성(문자 코드 48부터 57까지),
* 이어서 알파벳 대문자 A부터 Z까지를 생성(문자 코드 65부터 90까지).
* 이어서 알파벳 소문자 a부터 z까지를 생성(문자 코드 97부터 122까지).
```javascript
function* generateSequence(start, end) {
for (let i = start; i <= end; i++) yield i;
}
function* generatePasswordCodes() {
// 0..9
yield* generateSequence(48, 57);
// A..Z
yield* generateSequence(65, 90);
// a..z
yield* generateSequence(97, 122);
}
let str = '';
for(let code of generatePasswordCodes()) {
str += String.fromCharCode(code);
}
alert(str); // 0..9A..Za..z
```
위의 코드에서 yield*를 쓰면서 generateSequence 의 실행을 위임하겠다는 뜻이란다.
그래서 generateSequence(48,57) 이렇게 되면 순차적으로 실행해서 그 값을 이터러블 값으로 반환하라는 뜻이란다.
- yield 를 사용해 제너레이터 안,밖으로 정보 교환하기
yield가 양방향 길과 같은 역할을 하기 때문에 generator는 그냥 값을 생성해주는 그 이상의 유연한 기능이 있음.
yield는 결과를 바깥으로 전달할 뿐만 아니라 값을 제너레이터 안으로 전달하기까지 함.
값을 안, 밖으로 전달하려면 generator.next(arg)를 호출하면 됨. (이때 인수 arg는 yield의 결과.)
```javascript
function* gen() {
// 질문을 제너레이터 밖 코드에 던지고 답을 기다립니다.
let result = yield "2 + 2 = ?"; // (*)
alert(result);
}
let generator = gen();
let question = generator.next().value; // <-- yield는 value를 반환합니다.
generator.next(4); // --> 결과를 제너레이터 안으로 전달합니다.
```
"2+2 = " 이라는 문자열을 전달 한 후 generator.next() 안의 args 값을 받아 (여기서는 4) 그 값을 다시 전달해준다.
<더 자세히="" 알아보자="">
```javascript
function* gen() {
let ask1 = yield "2 + 2 = ?";
alert(ask1); // 4
let ask2 = yield "3 * 3 = ?"
alert(ask2); // 9
}
let generator = gen();
alert( generator.next().value ); // "2 + 2 = ?"
alert( generator.next(4).value ); // "3 * 3 = ?"
alert( generator.next(9).done ); // true
```
1) 맨처음 gen() 이 호출 되면 generator 생성됨
2) 그 다음 generator.next() 가 호출되면 첫번째 yield를 만나게 된다.
3) generator.nex().value까지 가면 해당 yield 의 값을 반환해줌
4) generator.next(4) 를 만나면 첫 yield에는 4를 넣어주고, 그 다음 yield를 가리킴
5) generator.next(4).value 를 만나면 두번째 가리킨 yield의 값을 반환해줌
6) generator.next(9) 를 만나면 두번째 값에 9를 넣어주고
7) generator.next(9).done 을만나면 제너레이터의 done 값이 true가 되므로 그렇게 끝남.
- generator.throw
generator.throw로 에러를 던지는 것도 가능하다
다음 코드를 보면
```javascript
function* gen() {
try {
let result = yield "2 + 2 = ?"; // (1)
alert("위에서 에러가 던져졌기 때문에 실행 흐름은 여기까지 다다르지 못합니다.");
} catch(e) {
alert(e); // 에러 출력
}
}
let generator = gen();
let question = generator.next().value;
generator.throw(new Error("데이터베이스에서 답을 찾지 못했습니다.")); // (2)
```
generator.throw로 에러를 던지고
generator 안에서
try, catch 구문을 사용하여 에러를 잡아내는 것도 가능한 것이다.
### 1. async 이터레이터와 제너레이터
비동기 이터레이터를 사용하여 비동기적인 데이터 처리 가능.(네트워크 통해)
여기에 비동기 제너레이터를 사용함으로 데이터 좀 더 편리하게 처리 가능.
- async 이터레이터
비동기 이터레이터는 일반 이터레이터와 비슷하다.
이터레이터 => 비동기 이터레이터 위한 문법
1) Symbol.iterator 대신, Symbol.asyncIterator를 사용.
2) next()는 프라미스를 반환해야 함.
3) 비동기 이터러블 객체를 대상으로 하는 반복 작업은 for await (let item of iterable) 반복문을 사용해 처리해야 함.
```javascript
let range = {
from: 1,
to: 5,
// for await..of 최초 실행 시, Symbol.asyncIterator가 호출됨
[Symbol.asyncIterator]() { // (1)
// Symbol.asyncIterator 메서드는 이터레이터 객체를 반환
// 이후 for await..of는 반환된 이터레이터 객체만을 대상으로 동작하는데, 다음 값은 next()에서 정해짐
return {
current: this.from,
last: this.to,
// for await..of 반복문에 의해 각 이터레이션마다 next()가 호출됨
async next() { // (2)
// next()는 객체 형태의 값, {done:.., value :...}를 반환
// (객체는 async에 의해 자동으로 프라미스로 감싸집니다.)
// 비동기로 무언가를 하기 위해 await를 사용할 수 있습니다.
await new Promise(resolve => setTimeout(resolve, 1000)); // (3)
if (this.current <= this.last) {
return { done: false, value: this.current++ };
} else {
return { done: true };
}
}
};
}
};
(async () => {for await (let value of range) { // (4)
alert(value); // 1,2,3,4,5}
})()
```
위 코드에 대한 설명은 다음과 같다.
1) 객체를 비동기적으로 반복 가능하도록 하려면, Symbol.asyncIterator메서드가 반드시 구현되어 있어야 함. (1) 주석 참고
2) Symbol.asyncIterator는 프라미스를 반환하는 메서드인 next()가 구현된 객체를 반환해야 함. – (2) 주석 참고
3) next()는 async 메서드일 필요는 없음. 프라미스를 반환하는 메서드라면 일반 메서드도 가능
* async를 사용하면 await도 사용할 수 있기 때문에, 여기선 편의상 async메서드를 사용해 일 초의 딜레이가 생기도록 한 것임. – (3)주석 참고
4) 반복 작업을 하려면 ‘for’ 뒤에 'await’를 붙인 for await(let value of range)를 사용하면 됨. for await(let value of range)가 실행될 때 range[Symbol.asyncIterator]()가 일회 호출됨, 그 이후엔 각 값을 대상으로 next()가 호출됨. – (4) 주석 참조
* 전개문법 ... 은 비동기적으로 동작하지 않음
- async 제너레이터
제너레이터는 이터레이터 객체이다.
일반 제너레이터에선 await를 사용할 수 없다. 그리고 모든 값이 동기적으로 생산된다. for of 사용불가.
그런데 비동기적으로 처리해야하는 상황이 발생할 경우 아래와 같이 async를 붙여줌으로 구현할 수 있다.
```javascript
async function* generateSequence(start, end) {
for (let i = start; i <= end; i++) {
// await를 사용할 수 있습니다!
await new Promise(resolve => setTimeout(resolve, 1000));
yield i;
}
}
(async () => {
let generator = generateSequence(1, 5);
for await (let value of generator) {
alert(value); // 1, 2, 3, 4, 5
}
})();
```
그냥 async 키워드를 붙이기만 하면 제너레이터 안에서 프라미스와 기타 async 함수를 기반으로 동작하는 await를 사용할 수 있음.
async 제너레이터의 특징
* generator.next() 메서드는 비동기적이 됨
* 프라미스를 반환함.
```javascript
result = await generator.next(); // result = {value: ..., done: true/false}
```
일반 generator에서는 generator.next() 만 사용하면 됐으나. async에서는 await를 앞에 붙여줘야한다.
- async 이터러블
*[Symbol.iterator]를 asyncIterator로 바꾸어 사용
```javascript
let range = {
from: 1,
to: 5,
async *[Symbol.asyncIterator]() { // [Symbol.asyncIterator]: async function*()와 동일
for(let value = this.from; value <= this.to; value++) {
// 값 사이 사이에 약간의 공백을 줌
await new Promise(resolve => setTimeout(resolve, 1000));
yield value;
}
}
};
(async () => {
for await (let value of range) {
alert(value); // 1, 2, 3, 4, 5}
})();
```
이러면 1초의 간격을 두고 결과를 얻을 수 있음.
- 실제 사례
pagination을 통해 데이터를 페이지 단위로 끊어서 전송할 때.
## [모듈]
### 1. 모듈 소개
application의 크기가 커져 파일을 나눌 때
파일 각각을 '모듈(module)'이라고 부름, 모듈은 대개 클래스 하나
혹은 특정한 목적을 가진 복수의 함수로 구성된 라이브러리 하나로 구성됨.
초기 자바스크립트는 크기가 작아서 모듈 관련 문법이 없었음
점점 크기 기능 거대화 되어
- 특별한 라이브러리를 만들거나(필요한 모듈을 언제든지 불러올 수 있게 해줌)
- 코드를 모듈 단위로 구성해 주는 방법을 만듬
예시)
AMD – 가장 오래된 모듈 시스템 중 하나로 require.js라는 라이브러리를 통해 처음 개발되었음
CommonJS – Node.js 서버를 위해 만들어진 모듈 시스템
UMD – AMD와 CommonJS와 같은 다양한 모듈 시스템을 함께 사용하기 위해 만들어짐
이제는 대부분의 주요 브라우저와 Node.js가 모듈 시스템을 지원하고 있음
- 모듈이란?
모듈이란 단순히 기능별로 나눈 파일 하나, 스크립트 하나.
모듈에 지시자 export와 import를 적용하여 다른 모듈을 불러와 불러온 모듈에 있는 함수를 호출하는 것과 같은 기능 공유가 가능함.
1) export 지시자를 변수나 함수 앞에 붙이면 외부 모듈에서 해당 변수나 함수에 접근할 수 있음(모듈 내보내기).
2) import 지시자를 사용하면 외부 모듈의 기능을 가져올 수 있음(모듈 가져오기).
```javascript
// 📁 sayHi.js
export function sayHi(user) {
alert(`Hello, ${user}!`);
}
```
sayHi.js 파일에 export를 붙임
```javascript
// 📁 main.js
import {sayHi} from './sayHi.js';
alert(sayHi); // 함수
sayHi('John'); // Hello, John!
```
import 를붙여서 sayHi.js에 있는 함수를 가져옴.
(상대경로를 from 뒤에 넣어줘야 함)
```
모듈은 특수한 키워드나 기능과 함께 사용되므로
```
1) this는 undefined
```javascript
```
모듈 스코프 안의 this는 undefined가 된다. 전역 this는 window와 헷갈리면 안됨
1) 브라우저의 특정기능
브라우저 환경에서 type="module"이 붙은 모듈 스크립트와 일반 스크립트와의 차이점
//지연_실행
모듈 스크립트는 항상 지연 실행 됨
지연실행의 특징
```
(1) 외부 모듈 스크립트
```
### 2. 모듈 내보내고 가져오기
다양한 모듈의 사용법
- 선언부 앞에 export 붙이기
변수, 함수 앞에 export 붙이면 내보내기 가능함.
* 클래스 함수를 내보낼 땐 앞에 세미콜론을 붙이지 않는다.
- 선언부와 떨어진 곳에 export 붙이기
선언부와 떨어진 곳에서도 export를 사용하여 내보내는 애들을 정해줄 수 있다.
(예시)
```javascript
// 📁 say.js
function sayHi(user) {
alert(`Hello, ${user}!`);
}
function sayBye(user) {
alert(`Bye, ${user}!`);
}
export {sayHi, sayBye}; // 두 함수를 내보냄
```
당연히 export를 맨 위에 배치해도 상관 없음.
- import *
무언가를 가지고 오고 싶다면 import{...} 안에 적어주면 됨
*가져올 것이 많다?
그카면 import * as 객체 로 객체 형태로 묶어서 가져오는 것이 가능하다.
(예시)
```javascript
// 📁 main.js
import * as say from './say.js';
say.sayHi('John');
say.sayBye('John');
```
이렇게 하면 편리해보이지만 가져오는 대상을 명시하는 게 좋다.
그 이유는 다음과 같다.
1) 가져오는 과정에서 웹팩과 같은 모던 번딩 툴에서 사용안하는 애들은 가지치기를 해주기 때문에 다 가져와 버리면 그만큼 로딩 속도가 느려짐
2) 어떤 걸 가져올지 명시하면 이름을 간결하게 사용할 수 있음. say.sayhi() 보다 sayhi() 가 간결함.
3) 코드 구조 파악하기가 쉬워서 리팩토링, 유지보수가 쉬움
- import 'as'
as를 사용해서 이름을 바꿔줄 수 있다.
```javascript
// 📁 main.js
import {sayHi as hi, sayBye as bye} from './say.js';
hi('John'); // Hello, John!
bye('John'); // Bye, John!
```
- export 'as'
```javascript
// 📁 say.js
...
export {sayHi as hi, sayBye as bye};
```
export에 as를 써주면 해당 이름으로 내보내주게 된다.
- export default
모듈의 종류는 두가지
1) 복수의 함수가 있는 라이브러리 형태의 모듈(위 예시의 say.js)
2) 개체 하나만 선언되어있는 모듈(아래의 user.js. class User 하나만 내보내기 함)
* 대개는 두 번째 방식으로 모듈을 만드는 걸 선호하기에 함수, 클래스, 변수 등의 개체는 전용 모듈 안에 구현됨
* 그런데 이렇게 모듈을 만들다 보면 자연스레 파일 개수가 많아질 수밖에 없음.
그래도 잘 정리하면 문제가 되지 않음.
* export default를 사용하면 '해당 모듈엔 개체가 하나만 있다'는 사실을 명확히 나타냄
```javascript
// 📁 user.js
export default class User { // export 옆에 'default'를 추가해보았습니다.
constructor(name) {
this.name = name;
}
}
```
이렇게 export 하면
import 할 때 중괄호 없이 가져올 수 있다.
또한 export default 하면 딱 걔를 가져오겠다고 지정한 것이므로 이름이 따로 없어도 된다.
- default 'name'
export를 떨어진 곳에 선언하여 내보낼 애를 지정할 때 뒤에 default 를 붙여주면 걔는 기본으로 내보내겠다는 애가 된다.
```javascript
export {sayHi as default};
```
또한 다음과 같이
```javascript
// 📁 user.js
export default class User {
constructor(name) {
this.name = name;
}
}
export function sayHi(user) {
alert(`Hello, ${user}!`);
}
export default와 export named를 같이 쓴 경우
// 📁 main.js
import {default as User, sayHi} from './user.js';
new User('John');
```
위와 같이 사용하면 default 와 named 값을 같이 가져올 수 있다.
- default export에 관한 규칙
default export로 내보낼 시 import에서 이름을 아무거나 지정해도 상관 없이 정상 동작한다,
- 모듈 다시 내보내기
import해서 가져온 애들을 export 해서 다시 돌려줄 수 있다.
```javascript
export {sayHi} from './say.js'; // sayHi를 다시 내보내기 함
```
export.. from을 사용한다.
- default export의 다시 내보내기
default 지정한 애를 가져왔어
1) 그 후에 다시 export 해줄 때는 걔를 꼭 default를 붙여서 내보내줘야 함.
export {default as User} 이렇게
2) export * from './user.js'; // named export를 다시 내보내기
만약 이렇게 쓴 경우에는 export default 빼고 다 다시 내보낸거임.
export default 는 따로 내보내줘야 함.
### 3. 동적으로 모듈 가져오기
export, import는 '정적인' 방식임.
1) 첫번째 제약, import에는 모듈 경로로 문자열만 사용할 수 있었음
2) 두번째 제약, 런타임이나 조건부로 모듈을 불러올 수 없음
(if 문안에 import 불가)
import/export는 코드 구조의 중심을 잡아주는 역할을 하기 때문
그럼에도 불구하고 모듈을 동적으로 불러와야 한다면?
- import() 표현식
import(module) 표현식은 모듈을 읽고 이 모듈이 내보내는 것들을 모두 포함하는 객체를 담은 이행된 프라미스를 반환해줌.
호출은 어디서나 가능.
이 경우, 코드 내 어디에서 동적으로 사용할 수도 있습니다.
```javascript
(사용예시)
let modulePath = prompt("어떤 모듈을 불러오고 싶으세요?");
import(modulePath)
.then(obj => <모듈 객체="">)
.catch(err => <로딩 에러, e.g. 해당하는 모듈이 없는 경우>)
```
async 함수 안에서 await와 함께 사용하는 것도 가능함.
(예시 구체적으로!)
```javascript
// 📁 say.js
export function hi() {
alert(`안녕하세요.`);
}
export function bye() {
alert(`안녕히 가세요.`);
}
let {hi, bye} = await import('./say.js');
hi();
bye();
```
import() 안에 경로를 입력해줌으로 보다 동적인 사용이 가능하다.
## [기타]
### 1. proxy와 reflect
Proxy는 특정 객체를 감싸 프로퍼티 읽기, 쓰기와 같은 객체에 가해지는 작업을 중간에서 가로채는 객체임
가로채진 작업은 Proxy 자체에서 처리되기도 하고, 원래 객체가 처리하도록 그대로 전달되기도 함. (중간 매개체 같은 역할?)
- Proxy
기본문법:
```javascript
let proxy = new Proxy(target, handler)
```
* target – 감싸게 될 객체로, 함수를 포함한 모든 객체가 가능.
* handler – 동작을 가로채는 메서드인 '트랩(trap)'이 담긴 객체로, 여기서 프락시를 설정함
(예시: get 트랩은 target의 프로퍼티를 읽을 때, set 트랩은 target의 프로퍼티를 쓸 때 활성화됨).
proxy에 작업이 가해지고,
handler에 상응하는 트랩이 있으면 트랩이 실행되어 프락시가 이 작업을 처리할 기회를 얻게 됨.
트랩이 없으면 작업은 target에 직접 수행됨.
(사용 예시) - 트랩이 없는 경우
```javascript
let target = {};
let proxy = new Proxy(target, {}); // 빈 핸들러
proxy.test = 5; // 프락시에 값을 씁니다. -- (1)
alert(target.test); // 5, target에 새로운 프로퍼티가 생겼네요!
alert(proxy.test); // 5, 프락시를 사용해 값을 읽을 수도 있습니다. -- (2)
for(let key in proxy) alert(key); // test, 반복도 잘 동작합니다. -- (3)
```
위 예시의 프락시엔 트랩이 없기 때문에 proxy에 가해지는 모든 작업은 target에 전달됨
가로챌 수 있는 메서드들 목록.
내부 메서드 핸들러 메서드 작동 시점
[[Get]] get 프로퍼티를 읽을 때
[[Set]] set 프로퍼티에 쓸 때
[[HasProperty]] has in 연산자가 동작할 때
[[Delete]] deleteProperty delete 연산자가 동작할 때
[[Call]] apply 함수를 호출할 때
[[Construct]] construct new 연산자가 동작할 때
[[GetPrototypeOf]] getPrototypeOf Object.getPrototypeOf
[[SetPrototypeOf]] setPrototypeOf Object.setPrototypeOf
[[IsExtensible]] isExtensible Object.isExtensible
[[PreventExtensions]] preventExtensions Object.preventExtensions
[[DefineOwnProperty]] defineProperty Object.defineProperty, Object.defineProperties
[[GetOwnProperty]] getOwnPropertyDescriptor Object.getOwnPropertyDescriptor, for..in, Object.keys/values/entries
[[OwnPropertyKeys]] ownKeys Object.getOwnPropertyNames, Object.getOwnPropertySymbols, for..in, Object/keys/values/entries
- 'get'트랩으로 프로퍼티 기본값 설정하기.
프로퍼티 읽기를 가로챌 땐 get메서드가 있어야 함.
get 메서드 인수들
1) target – 동작을 전달할 객체
2) property – 프로퍼티 이름
3) receiver – 타깃 프로퍼티가 getter라면 receiver는 getter가 호출될 때 this.
대개는 proxy 객체 자신이 this가 됨.
get 예시)
```javascript
let numbers = [0, 1, 2];
numbers = new Proxy(numbers, {
get(target, prop) {
if (prop in target) {
return target[prop];
} else {
return 0; // 기본값
}
}
});
alert( numbers[1] ); // 1
alert( numbers[123] ); // 0 (해당하는 요소가 배열에 없으므로 0이 반환됨)
```
중간에 가로채서 만약에 배열에 없는 애를 호출할 경우 0으로 출력해주도록 변경해주었음
훨씬 유연한 사용이 가능함.
- 'set'트랩으로 프로퍼티 값 검증하기
set 메서드의 경우는 값을 쓸 때 이를 가로채서 조작
인수들..
```javascript
set(target, property, value, receiver):
```
1) target – 동작을 전달할 객체로 new Proxy의 첫 번째 인자
2) property – 프로퍼티 이름
3) value – 프로퍼티 값
4) receiver – get 트랩과 유사하게 동작하는 객체로, setter 프로퍼티에만 관여
- ownKeys를 사용해 반복구현
- 'deleteProperty' 와 여러 트랩을 사용해 프로퍼티 보호하기
모듈>더>코드>코드>코드예시>