Как имитировать выход JavaScript?
Одним из новых механизмов, доступных в JavaScript 1.7, является yield, полезный для генераторов и итераторов.
В настоящее время это поддерживается только в браузерах Mozilla (что я знаю). Каковы некоторые способы моделирования этого поведения в браузерах, где он недоступен?
Ответы
Ответ 1
Ну, вы всегда можете написать внешнюю функцию, которая инициализирует переменные в закрытии, а затем возвращает объект, который делает любую работу, которую вы хотите.
function fakeGenerator(x) {
var i = 0;
return {
next: function() {
return i < x ? (i += 1) : x;
}
};
}
Теперь вы можете написать:
var gen = fakeGenerator(10);
а затем вызовите gen.next()
снова и снова. Было бы сложно смоделировать "окончательное" поведение метода "close()" на реальных генераторах, но вы могли бы оказаться где-то близко.
Ответ 2
Как и для ответа Pointy, но с помощью метода hasNext
:
MyList.prototype.iterator = function() { //MyList is the class you want to add an iterator to
var index=0;
var thisRef = this;
return {
hasNext: function() {
return index < thisRef._internalList.length;
},
next: function() {
return thisRef._internalList[index++];
}
};
};
Метод hasNext
позволяет вам делать петлю следующим образом:
var iter = myList.iterator() //myList is a populated instance of MyList
while (iter.hasNext())
{
var current = iter.next();
//do something with current
}
Ответ 3
Для нетривиальной функции генератора вы захотите использовать какой-то инструмент для перевода кода в эквивалент ES3, чтобы он мог запускаться в любом современном браузере. Я рекомендую попробовать Traceur, который можно грубо описать как переводчик источника ES6-ES3. Поскольку генераторы являются языковой функцией, предназначенной для ES6, Traceur сможет перевести их для вас.
Traceur предоставляет демонстрационную страницу где вы можете ввести код ES6 и увидеть ES3, сгенерированный "на лету" . Если вы введете что-то простое:
// Note that this declaration includes an asterisk, as specified by current ES6
// proposals. As of version 16, Firefox built-in support for generator
// functions does not allow the asterisk.
function* foo() {
var n = 0;
if (n < 10) {
n++;
yield n;
}
}
for (var n of foo()) {
console.log(n);
}
вы увидите, что эквивалентный код ES3 является нетривиальным, и для него требуется traceur.runtime
, чтобы код работал правильно в браузере. Время выполнения определено в http://traceur-compiler.googlecode.com/git/src/runtime/runtime.js, которое в настоящее время составляет 14K (unminified). Это нетривиальное количество кода, хотя, вероятно, большая часть его может быть оптимизирована с помощью компилятора Closure.
Обратите внимание, что также имеется ошибка, связанная с возможностью встраивания необходимых функций из пространства имен traceur.runtime
, что исключает необходимость включения runtime.js
в целом: https://code.google.com/p/traceur-compiler/issues/detail?id=119.
Ответ 4
Без какого-либо компилятора или препроцессора... Нет.
Ближе всего вы можете найти что-то вроде этого:
function doStuff() {
var result = { };
function firstStuf() { ...; result.next = secondStuff; return 42; };
function secondStuf() { ...; result.next = thirdStuff; return 16; };
function thirdStuf() { ...; result.next = null; return 7; };
result.next = firstStuff;
return result;
}
Но, ну... Это довольно дерьмово, и на самом деле это не большая замена.
Ответ 5
Я начал небольшой проект, который пытается сделать это с помощью какой-либо обратной связи. Поскольку невозможно создать настоящие сопрограммы в "стандартном" JavaScript, это не происходит без нескольких оговорок, например:
- невозможно сделать это, следуя протоколу итератора (например,
.next()
и т.д.),
- невозможно выполнить итерацию сразу нескольких генераторов,
- вы должны следить за тем, чтобы не допустить, чтобы неправильные объекты покидали область генератора (например, вызывая
yield
в тайм-ауте – поскольку это "простой" JavaScript, нет синтаксического ограничения, которое мешает вам это делать),
- исключения в генераторе немного сложны,
- и, что не менее важно, это очень экспериментально (только началось это несколько дней назад).
С яркой стороны у вас есть yield
!:)
Пример Fibonacci из MDC-страницы будет выглядеть следующим образом:
var fibonacci = Generator(function () {
var fn1 = 1;
var fn2 = 1;
while (1){
var current = fn2;
fn2 = fn1;
fn1 = fn1 + current;
this.yield(current);
}
});
console.log(fibonacci.take(10).toArray());
Вывод:
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
Проект находится на BitBucket в https://bitbucket.org/balpha/lyfe.