IT story

ES6 클래스에서 반복자를 만드는 방법

hot-time 2020. 12. 30. 19:14
반응형

ES6 클래스에서 반복자를 만드는 방법


JS1.7 SomeClass.prototype.__iterator__ = function() {...}구문 과 같은 방식으로 ES6 클래스에서 반복자를 어떻게 만들 수 있습니까?

[16:00 편집]

다음 작업 :

class SomeClass {
    constructor() {
    }

    *[Symbol.iterator]() {
        yield '1';
        yield '2';
    }

    //*generator() {
    //}

}

an_instance = new SomeClass();
for (let v of an_instance) {
    console.log(v);
}

WebStorm 플래그 *[Symbol.iterator]()는 별표 바로 뒤에 '예상되는 함수 이름'경고와 함께 표시되지만 그렇지 않으면 Traceur에서 제대로 컴파일되고 실행됩니다. (참고 WebStorm은에 대한 오류를 생성하지 않습니다 *generator().)


적절한 반복기 메서드를 정의합니다. 예를 들면 :

class C {
  constructor() { this.a = [] }
  add(x) { this.a.push(x) }
  [Symbol.iterator]() { return this.a.values() }
}

편집 : 샘플 사용 :

let c = new C
c.add(1); c.add(2)
for (let i of c) console.log(i)

클래스 인스턴스에 대해 반복기반환 하는 Symbol.iterator속성 을 지정해야 합니다. 반복자는 메서드가 있어야 하며 마녀는 차례로 필드 가있는 개체를 반환합니다 . 단순화 된 예 :SomeClassnext()donevalue

function SomeClass() {
  this._data = [1,2,3,4];
}

SomeClass.prototype[Symbol.iterator] = function() {
  var index = 0;
  var data  = this._data;

  return {
    next: function() {
      return { value: data[++index], done: !(index in data) }
    }
  };
};

또는 ES6 클래스 및 화살표 기능 사용 :

class SomeClass {
  constructor() {
    this._data = [1,2,3,4];
  }

  [Symbol.iterator]() {
    var index = -1;
    var data  = this._data;

    return {
      next: () => ({ value: data[++index], done: !(index in data) })
    };
  };
}

그리고 사용법 :

var obj = new SomeClass();
for (var i of obj) { console.log(i) }

업데이트 된 질문 에서 생성기 함수를 통해 클래스 반복기실현했습니다 . 그렇게 할 수 있지만 반복기가 생성기가 될 수 없다는 것을 이해해야합니다. 실제로 es6의 반복자는 특정 방법 을 가진 모든 객체입니다.next()


다음은 ES6에서 2d 행렬 사용자 정의 클래스를 반복하는 예입니다.

class Matrix {
    constructor() {
        this.matrix = [[1, 2, 9],
                       [5, 3, 8],
                       [4, 6, 7]];
    }

    *[Symbol.iterator]() {
        for (let row of this.matrix) {
            for (let cell of row) {
                yield cell;
            }
        }
    }
}

그러한 클래스의 사용법은 다음과 같습니다.

let matrix = new Matrix();

for (let cell of matrix) {
    console.log(cell)
}

출력되는

1
2
9
5
3
8
4
6
7

문서 : 반복 프로토콜

반복기 프로토콜반복 가능한 프로토콜 기술을 모두 구현하는 예제 클래스 :

class MyCollection {
  constructor(elements) {
    if (!Array.isArray(elements))
      throw new Error('Parameter to constructor must be array');

    this.elements = elements;
  }

  // Implement "iterator protocol"
  *iterator() {
    for (let key in this.elements) {
      var value = this.elements[key];
      yield value;
    }
  }

  // Implement "iterable protocol"
  [Symbol.iterator]() {
    return this.iterator();
  }
}

다음 기술 중 하나를 사용하여 요소에 액세스합니다.

var myCollection = new MyCollection(['foo', 'bar', 'bah', 'bat']);

// Access elements of the collection using iterable
for (let element of myCollection)
  console.log('element via "iterable": ' + element);

// Access elements of the collection using iterator
var iterator = myCollection.iterator();
while (element = iterator.next().value)
  console.log('element via "iterator": ' + element);

하위 객체에 저장하는 ES6 반복기 클래스의 예 :

class Iterator {
    data;

    constructor(data = {}) {
        this.data = JSON.parse(JSON.stringify(data));
    }

    add(key, value) { this.data[key] = value; }

    get(key) { return this.data[key]; }

    [Symbol.iterator]() {
        const keys = Object.keys(this.data).filter(key => 
        this.data.hasOwnProperty(key));
        const values = keys.map(key => this.data[key]).values();
        return values;
    }
}

설명

Making an object iterable means this object has a method named with the Symbol.iterator. When this method gets called, it should return an interface called iterator.

This iterator must have a method next that returns the next result. This result should be an object with a value property that provides the next value, and a done property, which should be true when there are no more results and false otherwise.

Implementation

I will also implement an iterator for a class called Matrix which all elements will range from 0 to width * height - 1. I will create a different class for this iterator called MatrixIterator.

class Matrix {
    constructor(width, height) {
        this.width = width;
        this.height = height;
        this.content = [];

        for (let y = 0; y < height; y++) {
            for (let x = 0; x < width; x++) {
                this.content[y * width + x] = y * width + x;
            }
        }
    }

    get(x, y) {
        return this.content[y * this.width + x];
    }

    [Symbol.iterator]() {
        return new MatrixIterator(this);
    }
}


class MatrixIterator {
    constructor(matrix) {
        this.x = 0;
        this.y = 0;
        this.matrix = matrix;
    }

    next() {
        if (this.y == this.matrix.height) return {done: true};

        let value = {
            x: this.x,
            y: this.y,
            value: this.matrix.get(this.x, this.y)
        };

        this.x++;

        if (this.x == this.matrix.width) {
            this.x = 0;
            this.y++;
        }

        return {value, done: false};
    }
}

Notice that Matrix implements the iterator protocol by defining Symbol.iterator symbol. Inside this method, an instance of MatrixIterator is created which takes this, i.e., the Matrix instance as parameter, and inside MatrixIterator, the method next is defined. I particularly like this way of implementing an iterator because it clearly shows the iterator and the implementation of the Symbol.iterator.

Alternatively, one can also not define directly Symbol.iterator, and instead add a function to prototype[Symbol.iterator] as follows:

Matrix.prototype[Symbol.iterator] = function() {
    return new MatrixIterator(this);
};

Usage Example

let matrix = new Matrix(3, 2);
for (let e of matrix) {
    console.log(e);
}

ReferenceURL : https://stackoverflow.com/questions/28739745/how-to-make-an-iterator-out-of-an-es6-class

반응형