Помощь!Я учусь любить 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 предпочло простоту одного метода.Таким образом, им не пришлось бы вводить понятие групп символических методов или интерфейсов ...