Generator
이터러블을 생성하는 함수
이터레이션 프로토콜을 준수해 이터러블을 생성하는 것보다 간편하게 이터러블을 구현할 수 있다.
- function* () 형태로 생성
- Generator 함수를 호출하면 함수 블록을 실행하지 않고 Generator 오브젝트를 생성해 반환한다.
- Generator 오브젝트는 이터레이터 오브젝트다.
// function* 선언문
function* sports(one) {
};
// function* 표현식
const book = function* (one) {
};
// Generator Function
const music = Object.getPrototypeOf(
function* () {}).constructor;
const get = new music();
function* 선언문
아래 코드는 선언문 형태의 Generator 함수를 선언하고, 호출한 변수에 Generator 오브젝트를 생성해 반환했다.
sports 라는 Generator 함수의 타입을 찍어보면 당연히 함수가 나오게 된다.
이 때 obj라는 변수에 Generator 함수를 호출하고 매개변수로 1, 2를 전달한 후 타입을 찍어보면 Generator 오브젝트가 생성 후 반환되어 obj는 오브젝트라는 결과가 나온다.
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}
이터레이터 오브젝트이기 때문에 next() 메서드로 전개하면 마지막 코드와 같은 값이 반환된다.
function* 표현식
아래 코드는 표현식 형태의 Generator 함수를 작성했다.
일반적으로 Generator 함수의 이름은 작성하지 않으며, Generator 함수 왼쪽에 선언한 변수 이름이 함수 이름이 된다. 따라서 Generator 함수의 이름은 sports이며, 파라미터로 100이 들어와 yield 100이 된다. next() 메서드로 전개하면 마지막 코드와 같은 값이 반환된다.
const sports = function* (one) {
yield one;
}
const obj = sports(100);
console.log(obj.next()) // {value: 100, done: false}
Generator Function
GeneratorFunction.constructor를 사용해 Generator 함수를 생성한다.
GeneratorFunction은 Generator 함수를 생성하기 위한 내장 생성자 함수다.
파라미터를 문자열로 작성하고, 1번째 파라미터는 함수의 매개변수를 나타내고, 2번째 파라미터는 함수 코드가 된다.
선언문, 표현식으로 작성한 것보다 다소 코드가 복잡하게 생겼다.
const fn = new Function("one", "return one");
console.log(fn(100)) // 100
const create = Object.getPrototypeOf(
function* () {}).constructor;
const sports = new create("one", "yield one");
const obj = sports(100);
console.log(obj.next()) // {value: 100, done: false}
fn이라는 이름의 상수에 new Function 구문으로 새로운 함수를 생성하고, 파라미터를 문자열로 작성했다.
위에서 말했던 것처럼 one이 함수 fn의 매개변수고 return one이 함수의 코드가 되었다.
파라미터로 100을 전달하니 그대로 100이 출력된 것을 확인할 수 있다.
create라는 이름의 변수에 값을 설정했다.
create 선언 후 뒤에 적힌 코드가 바로 Generator 함수를 생성하는 방법 중 하나다.
Generator 함수 자체에는 생성자가 없기 때문에 저렇게 constructor를 만들어줘야 한다.
Generator 함수의 속성에 생성자를 만들어주었다.
위 사진처럼 그냥 Generator 함수만 선언하면 아무 것도 갖고 있는 게 없다는 걸 알 수 있다.
생성자를 만들어주면 new 연산자로 Generator 함수가 되고, fn과 똑같은 파라미터와 똑같은 코드를 갖게 된다.
다만 Generator 함수가 되면 반환하는 값이 이터레이터 오브젝트와 같은 형태로 반환된다.
일반적으로 이렇게 Generator 함수를 만드는 것은 권장되지 않는다고 한다. 당연할 듯
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}
Generator 함수에 생성자를 할당하면 new 연산자로 생성자 함수를 호출할 수 있다.
그렇게 만든 sports 상수는 Generator 함수가 되고, 그렇기 때문에 이터러블을 생성해 next()로 전개할 수 있다.
yield
Generator 함수의 실행을 일시 중단하고 값을 반환한다.
- next()로 호출할 때마다 한 줄씩 순차적으로 실행된다.
- [returnValue] = yield[표현식] 일 때 오른쪽의 평가 결과가 설정되지 않고 다음 next()에서 파라미터로 넘겨준 값이 설정된다.
아래 코드와 같이 next()로 호출하면 한 줄씩 반환된다.
function* numberGenerator() {
yield 1;
yield 2;
yield 3;
}
const generator = numberGenerator();
console.log(generator.next()); // { value: 1, done: false }
console.log(generator.next()); // { value: 2, done: false }
console.log(generator.next()); // { value: 3, done: false }
console.log(generator.next()); // { value: undefined, done: true }
아래 코드의 경우 2번째 console.log를 보면 yield 뒤에 값이 없어 undefined를 반환한다.
3번째 console.log에서는 80이 나오는데, obj에서 파라미터로 30을 전달했고, 그 30이 one으로 들어와 80이 반환된다.
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(200)); // {value: undefined, done: true}
yield를 표현식으로 평가하면 호출한 곳으로 {value: 값, done: true/false}를 반환한다.
1번째 console.log에는 yield one이 실행돼 10이 출력된다. (two 변수에 10을 할당하지 않는다. one의 값인 10을 반환한다.)
2번째 console.log에는 2번째 next() 안에 설정된 값이 전달되는데 아무 것도 전달하지 않았으니 two = undefined가 되어 NaN를 반환한다.
3번째 console.log에는 3번째 next() 안에 설정된 값인 20이 바로 앞의 param 변수에 설정돼 20 + 10으로 30이 출력된다.
4번째 console.log에는 실행할 yield가 없어 undefined, true가 반환된다.
function* sports(one) {
let 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}
next()
yield 단위로 실행되며 yield 수만큼 next()를 작성해야 끝까지 실행할 수 있다.
상수 obj를 선언하면서 sports에 인자로 10을 전달했다.
1번째 줄에서 value + 20 으로 30이 할당되고, 그 다음줄에서 ++로 31이 된다.
obj.next(20)으로 인자 20이 전달되면 20 + 31에 ++연산자가 붙어 52가 된다.
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를 작성하지 않으면 아래와 같이 진행된다.
인수로 10이 전달되고 ++ 연산자로 value는 11이 되었다.
yield를 작성하지 않아 next()에서 전개할 부분이 없어 value를 undefined로 인지하고, 그래서 done을 true라고 인지한다.
function* sports(value) {
++value;
console.log(value); // 11
};
const obj = sports(10);
console.log(obj.next()); // {value: undefined, done: true}
만약 yield가 아니라 return으로 반환하면 어떻게 될까?
return으로 값을 반환하지만 return이라 끝났다고 사료되어 done: true가 된다.
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}
**참고
'👋🏻 JavaScript > 📖 자바스크립트 ES6+' 카테고리의 다른 글
[JS] Symbol 오브젝트를 알아보자 (0) | 2023.06.24 |
---|---|
[JS] yield와 제너레이터 오브젝트의 메소드 return(), throw()를 알아보자 (0) | 2023.06.23 |
[JS] Math 오브젝트를 알아보자 (0) | 2023.06.22 |
[JS] Array 오브젝트의 이터레이터 오브젝트 생성을 알아보자 (0) | 2023.06.22 |
[JS] Array 오브젝트의 find, findIndex, fill, includes, flat, flatMap을 알아보자 (0) | 2023.06.21 |