Примерно полгода назад мы решили начать использовать ES6 в CKEditor 5. Теперь весь код использует классы, стрелочные функции, let и const, карты и наборы. Эти вещи легко принять, и могут возникнуть некоторые сомнения относительно того, как и когда их использовать.

Генераторы и итераторы разные, потому что они меняют то, как вы думаете о своем API. Это не просто еще одна деталь в реализации, поскольку они имеют смысл только при глобальном использовании.

Это повторяющийся вопрос - стоит ли нам использовать для этого генераторы? Мы не злоупотребляем ими?

Препятствия

Использование генераторов меняет способ использования API - например, вы не можете повторять дважды один и тот же итератор:

const map = new Map( [ [ 1, ‘a’ ], [ 2, ‘b’ ], [ 3, ‘c’ ] ] );
const keys = map.keys();
for ( let key of keys ) {
      console.log( key ); // 1, 2, 3
}
for ( let key of keys ) {
      console.log( key ); // Not executed!
}

Вам нужно каждый раз получать новый итератор:

const map = new Map( [ [ 1, ‘a’ ], [ 2, ‘b’ ], [ 3, ‘c’ ] ] );
for ( let key of map.keys() ) {
      console.log( key ); // 1, 2, 3
}
for ( let key of map.keys() ) {
      console.log( key ); // 1, 2, 3
}

Для разработчиков, которые привыкли кэшировать свойства, это может привести к некоторым ошибкам. Особенно, когда вы используете методы, которые повторяют:

const map = new Map( [ [ 1, ‘a’ ], [ 2, ‘b’ ], [ 3, ‘c’ ] ] );
const keys = map.keys();
 
// In order to count items, the utility must iterate.
if ( utils.count( keys ) < 2 )
     return;
 
for ( let key of keys ) {
      console.log( key ); // Returns nothing!
}

Возможности

Во-первых, так работают ES6 Set и Map API. Нам придется столкнуться с этими недостатками, хотим мы того или нет. Кроме того, использование итераторов для возврата коллекций дает некоторую прибыль.

Вы запрещаете другим разработчикам изменять вашу коллекцию

Мы обсудили, следует ли клонировать массив при его возврате, чтобы разработчики не могли вносить прямые изменения. Генераторы решают эту проблему: пользователь может только перебирать полученные значения. Если он хочет сделать что-то еще, ему нужно вызвать Array.from ():

Array.from( selection.getRanges() );

Это создаст клон коллекции. Благодаря этому исходный массив никогда не будет изменен по ошибке.

Вы можете остановить выполнение кода, когда вам нужно

Обратите внимание, что иногда клонирования массива недостаточно. Возможно, вам потребуется выполнить глубокое клонирование, чтобы пользователь не мог вносить изменения. Мы встречали этот случай в Selection.getRanges ():

*getRanges() {
     for ( let range of this._ranges ) {
        yield range.clone();
    }
}

Что мне нравится в использовании генераторов, так это то, что, например, если вам нужно найти определенный диапазон, нет необходимости клонировать все диапазоны. При использовании генераторов вы прекратите клонирование, как только найдете правильный диапазон:

for ( let range of selection.getRanges() ) {
    if ( range.start.parent == someElement )
        return range;
}

Вы можете упростить свой код

Генераторы выдают элементы, и вам не нужно заботиться о том, насколько сложна структура, вы просто берете их и повторяете. Например, в DOM-подобной древовидной структуре у нас была коллекция элементов, которые могут содержать фрагменты документа, перемешанные с узлами. Вместо того, чтобы сглаживать структуру, мы смогли выполнить итерацию довольно простым способом, делегировав итерацию DocumentFragment:

*getNodes() {
     for ( let node of this.nodes ) {
           if ( node instanceof DocumentFragment )
                yield* node;
           else
                yield node;
     }
}

"Ой, извини, я нарушил твою концентрацию?"

Я думаю, что нам следует использовать генераторы каждый раз, когда мы возвращаем коллекции (getChildren (), getAttributes (), getRanges () и т. Д.) Или в противном случае оставайтесь в безопасности в нашей зоне комфорта.

Генераторы - это самое большое изменение в нашем понимании кода с момента появления объективно-ориентированного программирования. Но для меня после первого WTF! На данный момент код стал намного лучше и чище.