Generator function
- Generator Function
- function* 키워드를 사용한 함수
- Generator Function 형태
- function* 선언문, function* 표현식, GeneratorFunction
function* sports(one) {} const book = function*(one) {} const music = Object.getPrototypeOf(function* (){}).constructor; const gen = new music();
- function* 선언문, function* 표현식, GeneratorFunction
- 작성 방법
- function* 다음에 소괄호() 작성, 이어서 작성해도 되고 하나 이상 띄어도 된다.
- function* 선언문
- 형태: functoin* fName() {}
- 파라미터: [param[,…]] (option)
-
반환: Generator Object
- function* 다음에 함수 이름 작성
- 제너레이터 함수를 호출하면 함수 블록{}을 실행하지 않고 Generator Object를 생성하여 반환
function* sports(one, two) { yield one + two; }; console.log(typeof sports); // function const obj = sports(1, 2); console.log(typeof obj); // object console.log(obj.next()); // {value:3, done: false}
- Generator Function의 타입은 function이다.
- const obj = sports91, 2);
- sports 함수를 호출하면 Generator Object를 생성하여 반환
- 이 때, 함수 코드를 실행하지 않는다. 파라미터 값은 생성한 오브젝트에 설정된다.
- new 연산자를 사용할 수 없다. 단일 함수로 사용
- typeof obj
- 생성한 Generator Object 타입은 object
- obj.next()
- Generator Object가 iterator object이므로 next() 함수를 호출할 수 있으며 이 때 함수코드가 실행된다.
- Generator Object는 iterator object
- 함수 코드 실행
- Generator object의 메소드를 호출할 때
- function* 표현식
- 형태: const name = function*() {}
- 파라미터: [param [, …]]; (option)
-
반환: Generator Object
- function* 다음에 함수 이름 작성은 선택
- 일반적으로 함수 일므은 작성하지 않음
- function* 왼쪽에 변수를 선언하며 변수 이름이 함수 이름이 된다.
const sports = function* (one) { yield one; }; const obj = new sports(100); console.log(obj.next()); // {value: 100, done: false}
- 함수를 선언하는 형태만 다를 뿐 다른 것은 function* 선언문과 같다.
- GeneratorFunction
- 형태: new GenerationFunction()
- 파라미터: [param, [, …]], functionBody(option)
-
반환: Generator Object
- GeneratorFunction.constructor를 사용하야 제너레이터 함수를 생성
- parameter를 문자열로 작성
const fn = new Function("one", "return one"); console.log(fn(100)); // 100 const create = Object.getPrototypeOf( function*(){}).constructor; console.log(create); // function GeneratorFunction() { [native code] } const sports = new create("one", "yield one"); console.log(typeof sports); // function const obj = sports(100); console.log(obj.next()); // {value: 100, done: false}
- const create = Object.getPrototypeOf( function*(){}).constructor;
- 제너레이터 함수를 생성하는 cosntructor(생성자)를 할당
- constructor가 할당되므로 new 연산자로 생성자 함수를 호출할 수 있다.
- sports = new create(param)
- GeneratorFunction을 사용하여 제너레이터 함수를 생성하고 sports 변수 할당
- param에 파라미터와 함수 코드 작성
- const obj = sports(100);
- Generator Function을 호출
- Generator Object 생성, 반환
- 함수 코드를 실행하지는 않고, 100이 one에 매핑된다.
- obj.next()
- Generator Object는 Iterator Object이며 obj에 iterator object가 할당되어 있으므로 next()를 호출할 수 있다.
- const create = Object.getPrototypeOf( function*(){}).constructor;
- 마지막 파라미터가 함수 코드가 되고 앞은 파라미터 이름이 된다.
- parameter를 문자열로 작성
- Generator Function 구조
- generator function을 펼치면 prototype이 있다.
- 이것을 펼치면 cosntructor가 있어야 하는데 없고, 메서드 또한 없다.
- __proto__가 있으며 이것을 펼치면 constructor가 있다.
- __proto__에 다른 오브젝트의 prototype에 연결된 프로퍼티를 인스턴스 개념으로 생성하여 첨부한 것이 표시된다.
- 즉, GeneratorFunction의 constructor가 첨부된 것
- generator function을 펼치면 prototype이 있다.
yield 키워드
- syntax: [returnValue] = yield[표현식];
- yield 키워드 사용 형태
- next()로 호출할 때마다 하나씩 실행
function* sports(one) { yield one + 10; yield; const value = yield one + 50; }; const obj = sports(30); console.log(obj.next()); // {value: 40, done: false} console.log(obj.next()); // {value: undefined, done: false} console.log(obj.next()); // {value: 80, done: false} console.log(obj.next()); // {value: undefined, done: true}
- next()로 호출할 때마다 하나씩 실행
- yield 키워드는
- 제너레이터 함수 실행을 멈추거나 다시 실행할 때 사용
- yield 오른쪽의 표현식을 평가하고 결과 반환
- 표현식을 작성하지 않으면 undefined 반환
- [returnValue]
- 오른쪽의 평가 결과가 설정되지 않고 다음 next()에서 파라미터로 넘겨준 값이 설정
- yield 표현식을 평가하면 호출한 곳으로 {value: 값, done: true/false}를 반환
function* sports(one) { yield one; const check = 20; }; const obj = sports(10); console.log(obj.next()); // {value: 10, done: false} console.log(obj.next()); // {value: undefined, done: true}
- 두 번째 obj.next()에서는 그 다음 yield 키워드가 없으므로 {value: undefined, done: true} 를 반환
- value 값
- yield 표현식의 평가 결과 설정
- yield를 실행하지 못하면 undefined
- done 값
- yield를 실행하면 false
- yield를 실행하지 못하면 true
function* sports(one) {
const two = yield one;
let param = yield one + two;
yield param + one;
}
const obj = sports(10);
console.log(obj.next(); // {value: 10, done: false}
console.log(obj.next(); // {value: NaN, done: false}
console.log(obj.next(20); // {value: 30, done: false}
console.log(obj.next(); // {value: undefined, done: true}
- yield 옆에 있는 returnValue에 할당하지 않는다. 그래서 NaN 출력
- param은 next함수에 파라미터를 사용할 수 있다.
next()
- 형태: generatorObject.next()
- 파라미터: 제너레이터로 넘겨줄 파라미터 값(option)
-
반환: {value: 값, done: true/false}
- next()는 yield 단위로 실행 -yield 수만큼 next()를 작성해야 yield 전체를 실행
- next()를 호출하면 이전 yield 다음 yield까지 실행
function* sports(value) { value += 20; const param = yield ++value; value = param + value; yield ++value; }; const obj = sports(10); console.log(obj.next()); // {value: 31, done: false} console.log(obj.next(20)); // {value: 52, done: false}
- yield를 작성하지 않았을 때
function* sports(value) { ++value; console.log(value); }; const obj = sports(10); console.log(obj.next()); // 11, {value: undefined, done: true}
- 제너레이터 함수에 return 문을 작성했을 때
function* sports(value) { return ++value; }; const obj = sports(10); console.log(obj.next()); // {value: 11, done: true} console.log(obj.next()); // {value: undefined, done: true}
- yield가 없고 return이 있기 때문에 값을 반환은 하지만 done은 false가 아닌 true가 된다.
- 함수는 호출할 때마다 변수에 초기값을 설정
- 제너레이터 함수는
- 제너레이터 오브젝트를 생성할 때 초깃값을 설정
- next()로 실행할 때마다 초깃값을 설정하지 않음
- 변숫값을 그대로 유지
const sports = function* (param) { const one = param + 10; yield one; var two = 2; yield one + two; }; const obj = sports(10); console.log(obj.next()); // {value: 20, done: false} console.log(obj.next()); // {value: 22, done: false}
- Generator function에 2개의 yield가 있고, const one과 two가 있다.
- obj의 [[Scope]]를 펼치면 0: local, one: undefined, param: 10, two: undefined
- param에 10이 있다는 것은 sports 함수 안으로 들어간 것이며, sports 함수가 호출되어 실행 컨텍스트의 초기화 단게에서 초깃값을 설정한것이다.
- 단지, 함수 안의 코드를 실행하지 않은 것이다.
- obj.next()를 호출하면 sports 제너레이터 함수 안으로 이동
- const one = param + 10; 에서 멈추면, one: undefined, param: 10, two: undefined 이다.
- 이 값은 제너레이터 오브젝트를 만들 때 설정한 값
- const one = param + 10;
- one의 변수값이 20으로 변경
- yield one; 에서 {value: 20, done: false} 반환
- 함수를 빠져 나온 후 다시 obj.next()를 호출하면 함수 안으로 이동, 함수 안의 변수에 초깃값을 설정, 앞의 obj.next()로 one 변수에 할당한 값이 그대로 남는다.
- 이 것이 Generator function의 특징
- 제너레이터 오브젝트를 생성할 때 초깃값을 설정하고 next()를 호출할 때마다 초깃값을 설정하지 않는다.
yield 반복
let status = true;
function* sports() {
let count = 0;
while(status) {
yield ++count;
};
};
const obj = sports();
console.log(obj.next()); // {value: 1, done: false}
console.log(obj.next()); // {value: 2, done: false}
status = false;
console.log(obj.next()); // {value: undefined, done: false}
- yield 반복하는 형태
- next()가 호출될 때마다 status 상태에 따라 while이 반복된다.
- next() 호출되면 반복이 한번 실행(yield를 만나기 전까지)되기 때문에 done이 false이다.
다수의 yield 처리
function* sports() {
return yield yield yield;
}
const obj = sports();
console.log(obj.next()); // {value: undefined, done: false}
console.log(obj.next(10)); // {value: 10, done: false}
console.log(obj.next(20)); // {value: 20, done: false}
console.log(obj.next(30)); // {value: 30, done: true}
- 한줄에 yield와 return 작성
- return yield yield yield;
- 첫 번째 next() 호출
- 첫 번째 yield를 수행합니다. yield에 반환 값이 없으므로 {value: undefined, done: false} 반환
- 두 번째 next(10) 호출
- 파라미터 값: 10
- 두 번째 yield를 수행
- 함수에 파라미터 값을 받을 변수가 없으면 파라미터로 넘겨준 값 반환
- 세 번째 next(20) 호출
- 파라미터 값: 20
- 세 번째 yield를 수행
- 함수에 파라미터 값을 받을 변수가 없으면 파라미터로 넘겨준 값 반환
- 네 번째 next(30) 호출
- 파라미터 값: 30
- 처리할 yield가 없으므로 done:true 반환
- return 문을 작성했으므로 파라미터로 넘겨 준 값을 반환 {value: 30, done: true} 반환
- return 문을 작성하지 않으면 30이 아닌 undefined를 반환
yield 분할 할당
function* sports() {
return [yield yield];
};
const obj = sports();
console.log(obj.next()); // {value: undefined, done: false}
console.log(obj.next(10)); // {value: 10, done: false}
const last = obj.next(20);
console.log(last); // {value: [20], done: true}
console.log(last.value); // [20]
- 대괄호 [] 안에 다수의 yield 작성
- return [yield yield];
- next(), next(10) 호출
- yield를 연속 작성한 것과 같아 yield를 2개 모두 수행했으므로 더 이상 처리할 yield가 없다.
- 세 번째 next(20) 호출
- return param이 되고, 배열 안에 param을 넣어 반환
for-of 문
function* sports(count) {
while(true) {
yield ++count;
}
};
for(let point of sports(10) {
console.log(point); // 11, 12, 13
if(point > 12) {
break;
}
}
- 생성한 제너레이터 오브젝트를 저장할 변수가 없으며 엔진 내부에 저장
- const engine = sports(10); 과 같으며 engine이 엔진 내부의 이름으로 가정
- for-of문을 실행하면 {value: 11, done: false}가 아닌 value값만 point에 담는다.
Generator Object Method
- return()
- 형태: generatorObject.return()
- 파라미터: 제너레이터로 넘겨 줄 값(option)
-
반환: return()의 파라미터 값
- 이터레이터를 종료시킨다.
function* sports(count) { while(true) { yield ++count; }; const obj = sports(10); console.log(obj.next()); // {value: 11, done: false} console.log(obj.return(20)); // {value: 20, done: true} console.log(obj.next(50)); // {value: undefined, done: true}
- return() 파라미터 값을 {value: param, done: true}로 value에 설정한다.
- throw()
- 형태: generatorObject.throw()
- 파라미터: 에러 메시지, Error object
-
반환: {value: 에러 메시지, done: true}
- Error를 의도적으로 발생
- Generator function의 catch()문에서 에러를 받음
function* sports() { try { yield 10; } catch(message) { yield message; } yield 20; }; const obj = sports(); console.log(obj.next()); // {value: 10, done: false} const.obj(throw("에러 발생")); // {value: 에러 발생, done: false} - catch문에 있기 때문에 false 반환 console.log(obj.next()); // {value: 20, done: true}
- Generator function에 throw 문을 작성
function* sports() { throw "에러 발생"; yield 10; }; const obj = sports(); try { const result = obj.next(); } catch(message) { console.log(message); // 에러 발생 } console.log(obj.next()); // {value: undefined, done: true}
- Generator function에 에러가 발생하는 경우 종료한다.
yield* 표현식
- yield*
- Syntax: yield* 표현식
- yield* 표현식에 따라 처리하는 방법이 다르다.
- yield* 의 표현식이 배열
- next()로 호출할 때마다 배열의 엘리먼트를 하나씩 처리
function* sports() { yield* [10, 20]; }; const obj = sports(); console.log(obj.next()); // {value: 10, done: false} console.log(obj.next()); // {value: 20, done: false}
- next()로 호출할 때마다 배열의 엘리먼트를 하나씩 처리
- yield* 의 표현식이 제너레이터 함수
- 함수의 yield를 먼저 처리
function* point(count) { yield count + 5; yield count + 10; }; function* sports(value) { yield* point(value); yield value + 20; }; const obj = sports(10); console.log(obj.next()); // {value: 15, done: false} console.log(obj.next()); // {value: 20, done: false} console.log(obj.next()); // {value: 30, done: false}
- 첫 번째, 두 번째 next() 호출 -> yield* point(value) 호출 -> point의 yield를 먼저 반환
- 세 번째 next() 호출 -> sports()의 value+20; 반환
- 함수의 yield를 먼저 처리
- yield* 표현식에서 자신 호출
- 재귀 호출
function* sports(point) { yield point; yield* sports(point + 10); }; const obj = sports(10); console.log(obj.next()); // {value: 10, done: false} console.log(obj.next()); // {value: 20, done: false} console.log(obj.next()); // {value: 30, done: false}
- 첫 번째 yield에서 빠져 나오기 때문에 무한루프에 빠지지 않는다.
- int count = 0; while(true) { count += 10; yield point + count }; 와 같다
- 재귀 호출
** 출처1. 인프런 강좌_자바스크립트 ES6+