도움말
인텔리퀀트의 사용방법과 메뉴얼입니다.

11. 반복자 및 제네레이터

JavaScript 1.7 에 도입

집합체의 각 항목을 처리하는 것은 매우 일반적인 작업입니다. JavaScript는 집합체에 반복하는 몇 가지 방법을 제공하며, 간단한 for 및 for each 루프에서 map () , filter () 와 배열 내포 까지 있습니다. JavaScript 1.7에 도입 된 반복기 및 제네레이터는 언어의 핵심 부분에 직접 반복 아이디어를 도입하고, for ... in 및 for each 루프 동작을 사용자 지정하는 방법을 제공합니다.

반복자

반복자는 일련의 처리에서 현재 위치를 계속 추적하는 동안 집합체 항목 하나씩에 어떻게 접근 하는지를 알고있는 객체입니다. JavaScript에서 반복자는 일련의 과정의 다음 항목을 반환 next () 메서드를 제공하는 개체입니다. 이 방법은 일련의 처리가 종료 한 경우, 임의로 StopIteration 예외를 발생시킬 수 있습니다.

반복자 객체 생성되면 next () 를 반복 호출하여 명시 적으로 또는 JavaScript의 for ... in 및 for each 생성자에서 암시 적으로 사용 될 수 있습니다.

개체와 배열을위한 간단한 반복자는 Iterator () 함수를 사용하여 만들 수 있습니다 :

var  lang = {name : 'JavaScript' , birthYear : 1995};
var  it = Iterator (lang);

초기화 후에는 개체의 키와 값 쌍에 차례로 액세스하기 위해 next () 메서드를 호출 할 수 있습니다 :

var  pair = it.next (); // 쌍은 [ "name", "JavaScript"]
pair = it.next (); // 쌍은 [ "birthYear", 1995]
pair = it.next (); // StopIteration 예외가 발생

next () 메서드를 직접 호출하는 대신 for ... in 루프를 사용할 수 있습니다. 루프는 StopIteration 예외가 발생했을 때 자동으로 종료합니다.

var  it = Iterator (lang);
for  ( var  pair in  it)
  print (pair); // 각각의 key, value] 쌍을 차례로 표시
 

개체의 키에 대해서만 반복하고 싶다면, Iterator () 함수의 제 2 인수에 true 를 전달합니다 :

var  it = Iterator (lang, true );
for  ( var  Key in  it)
  print (key); // 각 key를 차례로 표시
 

개체의 내용에 액세스 Iterator () 를 사용의 장점은 Object.prototype 에 추가 한 고유 한 속성이 일련의 처리에 포함되지 않는 것입니다.

Iterator () 는 배열도 사용할 수 있습니다 :

var  langs = 'JavaScript' , 'Python' , 'C + +' ];
var  it = Iterator (langs);
for  ( var  pair in  it)
  print (pair); // 각각의 index, language] 쌍을 차례로 표시

개체와 마찬가지로, 제 2 인수에 true 를 전달 배열의 인덱스에 반복 할 수 있습니다 :

var  langs = 'JavaScript' , 'Python' , 'C + +' ];
var  it = Iterator (langs, true );
for  ( var  I in  it)
  print (i); // 0, 1, 2 출력
 

for 루프에서 let 키워드를 이용하여 블록 범위에 포함 된 변수를 인덱스 값으로 지정할 수, 인덱스와 값을 분할하여 할당 할 수 있습니다 :

var  langs = 'JavaScript' , 'Python' , 'C + +' ];
var  it = Iterator (langs);
for  (let [i, lang] in  it)
 print (i + ':'  + lang); // "0 : JavaScript '등을 출력

자신의 반복자의 정의

항목의 집합체를 나타내는 일부 개체는 특정 방법으로 반복하는 것이 좋습니다.

범위의 객체의 반복은 그 범위의 숫자를 차례로 반환해야합니다.
트리의 잎은 깊이 우선 또는 너비 우선 탐색 법에 따를 수 있습니다.
데이터베이스 쿼리의 출력을 나타내는 개체의 반​​복은 출력 세트 전체가 하나의 배열에 포함되지 않은 경우에도 행을 차례로 반환해야합니다.
무한 수열 (피보나치 수열 등)의 반복은 무한 길이의 데이터 구조를 만들 필요없이 결과를 차례로 상환한다.
JavaScript에서는 자체 반복 논리의 코드를 작성하고 그것을 개체와 연결할 수 있습니다.

하한과 상한 값을 저장하는 간단한 Range 개체를 만듭니다.

Function  Range (low, high) {
  this . low = low;
  this . high = high;
}

여기서 위의 범위에 포함 된 정수 시퀀스를 반환 자체 반복자를 만듭니다. 반복자 인터페이스는 일련의 데이터에서 항목을 반환하거나 StopIteration 예외를 던지는, next () 메소드를 제공해야합니다.

Function  RangeIterator (range) {
  this . range = range;
  this . current = this . range.low;
}
RangeIterator.prototype.next = function () {
  if  ( this . current> this . range.high)
    throw  StopIteration;
  else
    return  this . current + +;
};

RangeIterator 은 range의 인스턴스와 함께 인스턴스화 된 일련의 데이터에서 어디까지 진행되었는지를 추적하기위한 current 속성을 유지합니다.

그리고 RangeIterator 를 Range 개체에 연결하는 데, Range 에 특별한 방법이다 __iterator__ 을 추가해야합니다. 이 메서드는 Range 의 인스턴스에서 반복 할 때 호출, 반복의 논리를 구현하는 RangeIterator 의 인스턴스를 돌려줍니다.

Range.prototype.__iterator__ = function () {
  return  New  RangeIterator ( this );
};

자신의 반복 후크하면 range 인스턴스의 반복을 다음과 같이 할 수 있습니다 :

var  range = New  Range (3, 5);
for  ( var  I in  range)
  print (i); // 차례로 3, 4, 5를 출력

제네레이터 : 반복자 구축 더 나은 방법

자신의 반복은 유용한 수단이지만, 내부의 상태를 명시 적으로 관리 할 필요가 있기 때문에, 반복자의 작성은 신중 프로그래밍하는 것이 요구됩니다. 제네레이터는 강력한 대안을 제공합니다. 이것은 자신의 상태를 관리 할 수​​있는 함수 하나를 작성하여, 반복 알고리즘의 정의를 가능하게합니다.

생성기는 반복자의 생성 원으로 일하는 특별한 종류의 함수입니다. 하나 이상의 yield 식을 포함하는 경우, 함수 발생기입니다.

주의 : HTML에서 yield 키워드는 < script type="application/javascript;version=1.7" > (또는 상위 버전)의 블록에 포함 된 코드 블록에서만 사용할 수 있습니다. XUL 의 script 태그는이 기능의 사용시 특별한 블록을 필요로하지 않습니다. 생성기 함수가 함수 본문에서 호출해도 바로 실행되지 않습니다. 대신 발생기 반복자 오브젝트를 돌려줍니다. 각 발생기 반복자의 next () 메서드를 호출하면, 함수의 본체를 다음 yield 식까지 실행하고 그 결과를 돌려줍니다. 함수의 종료 또는 return 문에 도달하면 StopIteration 예외가 발생합니다.

이 동작은 다음 예에서 잘 표현되어 있습니다 :

Function  simpleGenerator () {
  yield "first" ;
  yield "second" ;
  yield "third" ;
  for  ( var  I = 0; i <3; i + +)
    yield i;
}
 
var  g = simpleGenerator ();
print (g.next ()); // "first"를 출력
print (g.next ()); // "second"을 출력
print (g.next ()); // "third"을 출력
print (g.next ()); // 출력 : 0
print (g.next ()); // 1을 출력
print (g.next ()); // 2를 출력
print (g.next ()); // StopIteration가 발생

생성기 함수는 클래스의 __iterator__ 메서드로 직접 사용할 수 자체 반복자를 만드는 데 필요한 코드의 양을 획기적으로 줄일 수 있습니다. 다음은 앞의 Range 를 생성기를 사용하여 다시 작성합니다 :

Function  Range (low, high) {
  this . low = low;
  this . high = high;
}
Range.prototype.__iterator__ = function () {
  for  ( var  I = this . low; i <= this . high; i + +)
    yield i;
};
var  range = New  Range (3, 5);
for  ( var  I in  range)
  print (i); // 차례로 3, 4, 5를 출력

모든 제네레이터가 종료하는 것은 아닙니다. 무한 시퀀스를 나타내는 생성기를 만들 수 있습니다. 다음 생성기는 피보나치 수열을 구현하고, 각 요소는 자신의 전 요소 두 값을 합계 한 것입니다 :

Function  fibonacci () {
  var  Fn1 = 1;
  var  Fn2 = 1;
  while  (1) {
    var  Current = fn2;
    fn2 = fn1;
    fn1 = fn1 + current;
    yield current;
  }
}
 
var  sequence = fibonacci ();
print (sequence.next ()); // 1
print (sequence.next ()); // 1
print (sequence.next ()); // 2
print (sequence.next ()); // 3
print (sequence.next ()); // 5
print (sequence.next ()); // 8
print (sequence.next ()); // 13

생성기 함수는 인수를 취할 수, 그것은 시작에 함수가 호출 될 때 연결됩니다. 생성기는 return 문 ( StopIteration 예외를 발생시켜) 종료 할 수 있습니다. 다음 fibonacci () 는 선택적 limit 인수를 가진 파생 형으로, limit가 전달 된 경우에 종료 할 것입니다.

Function  fibonacci (limit) {
  var  Fn1 = 1;
  var  Fn2 = 1;
  while  (1) {
    var  Current = fn2;
    fn2 = fn1;
    fn1 = fn1 + current;
    if  (limit && current> limit)
      return ;
    yield current;
  }
}

고급 생성기

제네레이터 창출 값을 요청에 따라 계산하고, 많은 계산이 필요한 일련의 데이터를 효율적으로 표현하고 앞과 무한으로 이어지는 데이터를 표현 할 수 있도록합니다.

발생기 반복자 객체는 next () 메소드 외에도 생성기의 내부 상태를 변경하는 데 사용할 수있는 send () 메서드를 가지고 있습니다. send () 에 전달 된 값은 생성기가 마지막에 멈춰 섰다 yield 의 결과로 간주됩니다. 특정 값을 전달 send () 를 사용하기 전에 적어도 한 번 next () 를 호출하여 제네레이터를 시작해야합니다.

다음 fibonacci 생성기는 수열을 다시 시작하기 위해 send () 을 사용하고 있습니다 :

Function  fibonacci () {
  var  Fn1 = 1;
  var  Fn2 = 1;
  while  (1) {
    var  Current = fn2;
    fn2 = fn1;
    fn1 = fn1 + current;
    var  reset = yield current;
    if  (reset) {
        fn1 = 1;
        fn2 = 1;
    }
  }
}
 
var  sequence = fibonacci ();
print (sequence.next ());      // 1
print (sequence.next ());      // 1
print (sequence.next ());      // 2
print (sequence.next ());      // 3
print (sequence.next ());      // 5
print (sequence.next ());      // 8
print (sequence.next ());      // 13
print (sequence.send ( true )); // 1
print (sequence.next ());      // 1
print (sequence.next ());      // 2
print (sequence.next ());      // 3

주의 : 흥미로운 점은 send (undefined) 의 호출은 next () 와 동일하다 수 있습니다. 그러나 send () 를 호출하면 undefined 이외의 값으로 새로운 제네레이터를 시작하면 TypeError 예외가 발생할 것입니다.
제네레이터의 throw () 메서드를 호출하여 발생하는 예외를 전달하여 생성기에 ​​예외를 발생시킬 수 있습니다. 이 예외는 제네레이터가 현재 머물고있는 상황에서 현재 머물고있는 yield 가 throw value 글에 바뀌었던 것처럼 던져 있습니다.

발생한 예외를 처리하는 동안 yield에 우연히 않으면 예외가 throw () 의 호출을 통해 전파 이후 next () 를 호출 StopIteration 가 발생할 수 있습니다.

생성기는 자신을 닫시키는 close () 메소드를 가지고 있습니다. 생성기를 닫을의 효과는 다음과 같습니다 :

실행중인 생성기 함수 활성 모든 finally 절이 실행됩니다
finally 절 StopIteration 이외의 예외가 발생하면 예외는 close () 메서드 호출에 전달합니다.
생성기가 종료됩니다.

제네레이터 식

JavaScript 1.8 에 도입
배열 내포 중요한 단점은 메모리에 새로운 배열 전체를 구축하고 있다는 것이다. 배열 내포에 입력 자체가 작은 배열 일 때의 오버 헤드는 작 습니다만, 입력이 큰 배열이나 처리가 많은 (혹은 정말 무한) 생성기 인 경우 배열 만들기 문제가 될 수 있습니다 .

생성기는 아이템을 필요할 때 요청에 따라 산출하는 일련의 데이터 계산을 줄일 수 있습니다. 제네레이터 식 구문으로 배열 내포와 거의 동일합니다. 이쪽은 중괄호 대신 괄호를 사용하여 (또한, for each ... in 대신 for ... in 을 사용), 배열을 구축하는 대신, 즉시 실행되지 제네레이터를 작성 있습니다. 이들은 생성기를 간단하게 한 구문과 생각할 수 있습니다.

정수의 대규모 수열을 반복하는 반복자 it 가정합니다. 수열의 값을 두 배로 반복하는 새로운 반복자를 작성하려고합니다. 배열 내포에서는 2 배의 값을 포함하는 충분한 배열을 메모리에 만듭니다.

var  doubles = [i * 2 for  (I in  it);

한편 생성기 식은 필요할 때 요청에 따라 2 배의 값을 생성하는 반복자를 만듭니다 :

var  it2 = (i * 2 for  (I in  it));
print (it2.next ()); // it의 첫 번째 값을 두 배로
print (it2.next ()); // it의 두 번째 값을 두 배로

생성기 표현식이 함수의 인수로 사용되는 경우 함수 호출에 사용되는 괄호는 제네레이터 식 외부의 괄호를 생략 할 수 있습니다 :

var  result = doSomething (i * 2 for  (I in  it));

저작권 공지

이 문서의 모든 저작권은 Mozilla.org에 있습니다. 이 문서는 "모질라 기여자"들에 의해 작성 되었습니다. 원문 보기
저희가 한글로 번역한 2차적저작물에 대한 저작권 역시 Mozilla.org에 있습니다.