본문으로 건너뛰기

JavaScript 제너레이터

무료2015-11-01#JS#JavaScript的yield#js的迭代器#JavaScript的迭代器#js的生成器#JavaScript generator

ES6 는 (이터레이터의) 제너레이터를 제공합니다. 본고에서는 그 문법과 기본적인 사용법을 자세히 설명합니다

一.문법

생성 문법:function* + yield

사용 문법:next(returnValue).value/done

P.S.이터레이터제너레이터function* 로 정의되는 것을 이터레이터의 제너레이터 (약칭 제너레이터) 라고 합니다. 이를 호출하면 이터레이터가 반환되기 때문입니다

二.이터레이터의 역할

1.함수 스로틀링

시간이 많이 소요되는 복잡한 작업을 yield 로 나누어 조금씩 수행합니다. 함수 커링 currying 이라고도 합니다. 자세한 내용은 JS 학습노트 11_ 고급 테크닉 을 참조하십시오

2.무한 시퀀스 생성

예를 들어 피보나치 수열 등

3.순회 용이

내부 상태를 수동으로 관리할 필요가 없습니다

三.JavaScript 제너레이터 예제

1.기본 사용법

function* fun(a) {
    yield a + 1;
    yield a * 2;
    yield a * 2 + 1;
}

var iter = fun(3);
iter.next();
// => Object {value: 4, done: false}
iter.next();
// => Object {value: 6, done: false}
iter.next();
// => Object {value: 7, done: false}


function* fun(a) {
    yield a=a + 1;
    yield a=a * 2;
    yield a=a * 2 + 1;
}

var iter = fun(3);
// => iter.next();
Object {value: 4, done: false}
iter.next();
// => Object {value: 8, done: false}
iter.next();
// => Object {value: 17, done: false}
iter.next();
// => Object {value: undefined, done: true}

함수 실행이 yield 를 만나면 먼저 yield 뒤의 값을 return 하고 함수 실행의 context 를 저장합니다 (브레이크포인트를 저장하는 것과 유사한 효과). 다음에 next() 를 호출하면 context 를 복원하고 yield 의 다음 문장부터 실행을 시작하여 yield 또는 return 을 만나면 종료합니다

2.고급 사용법

function* fib() {
    var a = 1;
    var b = 1;

    while (true) {
        var current = b;
        b = a;
        a = a + current;

        var reset = yield current;
        if (reset) {
            a = 1;
            b = 1;
        }
    }
}

var fibSeq = fib();
fibSeq.next();
// => Object {value: 1, done: false}
fibSeq.next();
// => Object {value: 1, done: false}
fibSeq.next();
// => Object {value: 2, done: false}
fibSeq.next();
// => Object {value: 3, done: false}
fibSeq.next();
// => Object {value: 5, done: false}
fibSeq.next(false);
// => Object {value: 8, done: false}
fibSeq.next(true);
// => Object {value: 1, done: false}
fibSeq.next(false);
// => Object {value: 1, done: false}
fibSeq.next(false);
// => Object {value: 2, done: false}

next() 는 매개변수를 받을 수 있으며, 이 매개변수는 yield 의 반환값으로 함수에 전달됩니다. 이를 통해 함수 내부의 상태를 제어할 수 있습니다. 예를 들어 위의 reset 여부 등

3.조금 복잡한 예

function* fun(a) {
    a = yield a + 1;
    a = yield a * 2;
    yield a * 2 + 1;
}

var iter = fun(3);
iter.next(0);
// => Object {value: 4, done: false}
iter.next(0);
// => Object {value: 0, done: false}
iter.next(1);
// => Object {value: 3, done: false}

유사한 것:

function* fun(a) {
    a = yield a = a + 1;
    a = yield a = a * 2;
    yield a = a * 2 + 1;
}
var iter = fun(3);
iter.next(0);
// => Object {value: 4, done: false}
iter.next(0);
// => Object {value: 0, done: false}
iter.next(1);
// => Object {value: 3, done: false}

내부 실행 메커니즘은 같고, 작성법만 조금 복잡할 뿐입니다

4.기타

또한 이터레이터에는 throw()return() 메서드가 있습니다.FF 는 둘 다 구현했지만, Chrome 은 전자만 구현했습니다. 자세한 내용은 MDN Iterators and generators 를 참조하십시오

또한 하나의 이터레이터에서 다른 이터레이터를 생성하는 문법도 있습니다. 예를 들어:

function* fun() {
    yield 1;
    yield 2;
}
var iter = fun();
var newIter = (for (i of iter) i * 2);
newIter.next();
// => Object { value: 2, done: false }
newIter.next();
// => Object { value: 4, done: false }

주의:MDN 의 예는 잘못되었습니다. 반드시 for...of 여야 하며, 반드시 둥근 괄호여야 합니다 (예시의 for...in 과 대괄호는 잘못되었습니다)

FF 는 이 문법을 지원하지만, Chrome 은 지원하지 않습니다

三.관련 문법

1.for...of、for...in、forEach

for...of 는 속성값을 순회하는 데 사용되고, for...in 은 속성명을 순회하는 데 사용됩니다. forEach 는 Array.prototype 의 메서드로, 속성명과 속성값을 동시에 순회할 수 있습니다

또한 for...of 는 DOM NodeList 와 커스텀 이터레이터 (function* + yield) 도 순회할 수 있으며, ES6 의 새로운 기능입니다

예는 다음과 같습니다:

var arr = [3, 5, 7];
arr.foo = "hello";

for (var i in arr) {
   console.log(i); // logs "0", "1", "2", "foo"
}
for (var i of arr) {
   console.log(i); // logs "3", "5", "7"
}


let arr = [3, 5, 7];
arr.foo = "hello";

arr.forEach(function (element, index) {
    console.log(element); // logs "3", "5", "7"
    console.log(index);   // logs "0", "1", "2"
});

2.yield*

yield 와 기능은 유사하며, 순회하는 데 사용되지만 yield* 뒤에는 이터레이터 객체가 오며, 뒤의 이터레이터로 들어가 순회를 완료한 후 돌아오는 역할을 합니다. 예를 들어:

function* g1() {
  yield 2;
  yield 3;
  yield 4;
}

function* g2() {
  yield 1;
  yield* g1();
  yield 5;
}

var iterator = g2();

console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { value: 4, done: false }
console.log(iterator.next()); // { value: 5, done: false }
console.log(iterator.next()); // { value: undefined, done: true }

yield* 로 인해 이터레이터를 중첩할 수 있게 되었고, 계층 구조를 "선형"으로 순회할 수 있습니다 (물론 모든 하위 구조의 이터레이터 객체를 얻을 수 있는 것이 전제이지만)

참고 자료

댓글

아직 댓글이 없습니다

댓글 작성