Почему Javascript `iterator.next ()` возвращает объект? - PullRequest
0 голосов
/ 22 декабря 2018

Помощь!Я учусь любить Javascript после программирования на C # довольно долго, но я застрял, учась любить итеративный протокол!

Почему Javascript принял протокол , который требуетсоздать новый объект для каждой итерации?Почему next() возвращает новый объект со свойствами done и value вместо принятия протокола, подобного C # IEnumerable и IEnumerator, который не выделяет объект за счет необходимости двух вызовов (от одного до moveNext дляпосмотреть, если итерация сделана, и секунда до current, чтобы получить значение)?

Существуют ли внутренние оптимизации, которые пропускают возвращение объекта на next()?Трудно представить, учитывая, что итерируемый не знает, как объект может быть использован после возвращения ...

Генераторы, похоже, не используют следующий объект, как показано ниже:

function* generator() {
  yield 0;
  yield 1;
}

var iterator = generator();
var result0 = iterator.next();
var result1 = iterator.next();

console.log(result0.value) // 0
console.log(result1.value) // 1

Хм, вот подсказка (спасибо Берги!):

Мы ответим на один важный вопрос позже (в разделе 3.2): Почему итераторы (необязательно) могут возвращать значениепосле последнего элемента?Эта возможность является причиной обертывания элементов.В противном случае итераторы могут просто вернуть публично определенный часовой (стоп-значение) после последнего элемента.

И в Sect.3.2 они обсуждают использование Использование генераторов в качестве легких потоков .Кажется, причина возврата объекта из next в том, что value может быть возвращено, даже если done равно true!Вау.Кроме того, генераторы могут return значения в дополнение к yield и yield* -ing значениям, а значение, генерируемое return, заканчивается как в value, когда done равно true!

И все это позволяет создавать псевдо-потоки.И эта особенность, псевдопоточность, стоит выделять новый объект для каждого цикла ... Javascript.Всегда так неожиданно!


Хотя теперь, когда я думаю об этом, разрешение yield* «возвращать» значение для включения псевдопоточности все еще не оправдывает возвращение объекта.Протокол IEnumerator может быть расширен для возврата объекта после того, как moveNext() вернет false - просто добавьте свойство hasCurrent для проверки после завершения итерации, когда true указывает, что current имеет допустимое значение...

И оптимизации компилятора нетривиальны.Это приведет к довольно диким отклонениям в производительности итератора ... разве это не вызывает проблем у разработчиков библиотек?

Все эти моменты поднимаются в этом потоке , обнаруженном дружественнымТАК сообщество.Тем не менее, эти аргументы, похоже, не держали день.


Однако, независимо от того, возвращать объект или нет, никто не будет проверять значение после того, как итерация "завершена", верно?Например, большинство считает, что следующие значения будут записывать все значения, возвращаемые итератором:

function logIteratorValues(iterator) {
  var next;
  while(next = iterator.next(), !next.done)
    console.log(next.value)
}

За исключением случаев, когда это не так, потому что, хотя done равен false, итератор мог бы все еще возвратить другоеvalue. Учтите:

function* generator() {
  yield 0;
  return 1;
}

var iterator = generator();
var result0 = iterator.next();
var result1 = iterator.next();

console.log(`${result0.value}, ${result0.done}`) // 0, false
console.log(`${result1.value}, ${result1.done}`) // 1, true

Является ли итератор, который возвращает значение после того, как его "done", действительно является итератором?Что за звук хлопка одной рукой?Это кажется довольно странным ...


И здесь подробно описывает генераторы, которые мне понравились.Много времени тратится на управление потоком приложения, а не на итерацию членов коллекции.


Другое возможное объяснение состоит в том, что IEnumerable / IEnumerator требует двух интерфейсов и трех методов, а сообщество JS предпочло простоту одного метода.Таким образом, им не пришлось бы вводить понятие групп символических методов или интерфейсов ...

Ответы [ 2 ]

0 голосов
/ 22 декабря 2018

Берги уже ответил, и я проголосовал, я просто хочу добавить это:

Почему вас должно беспокоить возвращение нового объекта?Это выглядит так:

{done: boolean, value: any}

Вы знаете, вы все равно будете использовать value, так что это не накладные расходы памяти.Что осталось?done: boolean настолько мал (4 байта?), Что вы не должны беспокоиться об этом.Также указатель объекта составляет около 100 байт или что-то в этом роде (я не знаю, но вы должны согласиться, что он очень маленький).

Кроме того, JS управляет миллионами объектов и выделяет их, собирает мусор и т. Д.

PS ТАКЖЕ, Феликс Клинг в комментариях к OP предполагает, что итератор может возвращать один и тот же объект каждый раз, но с измененными свойствами (спецификация не говорит, чтобы каждый раз возвращать новый объект).

0 голосов
/ 22 декабря 2018

Существуют ли внутренние оптимизации, которые пропускают распределение возвращаемого объекта на next()?

Да.Эти объекты результата итератора малы и обычно недолговечны.В частности, в циклах for … of компилятор может выполнить тривиальный анализ escape, чтобы увидеть, что объект вообще не сталкивается с пользовательским кодом (а только с кодом оценки внутреннего цикла).Они могут быть очень эффективно обработаны сборщиком мусора или даже размещены непосредственно в стеке.

Вот несколько источников:

...