#javascript #arrays #object #iterator #generator
#javascript #массивы #объект #итератор #генератор
Вопрос:
Я хочу написать функцию ES5 JavaScript (т. Е. без генераторов или Symbol.iterator
), которая выполняет то, что делает следующая функция генератора в ES6:
function *keys(o) {
for (let key in o)
yield key
}
Я хочу вернуть отложенный итератор без одновременной загрузки всех ключей в память, so Object.keys
не учитывается, поскольку он возвращает массив ключей. Однако, похоже, я не могу понять, как это сделать.
Я был в отчаянии, поэтому я начал изучать, как генераторы переносятся в версии JavaScript, которые их не поддерживают. Если вы введете описанную выше функцию генератора в Facebook Regenerator, вы получите следующий результат:
var _marked =
/*#__PURE__*/
regeneratorRuntime.mark(keys);
function keys(o) {
var key;
return regeneratorRuntime.wrap(function keys$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
_context.t0 = regeneratorRuntime.keys(o);
case 1:
if ((_context.t1 = _context.t0()).done) {
_context.next = 7;
break;
}
key = _context.t1.value;
_context.next = 5;
return key;
case 5:
_context.next = 1;
break;
case 7:
case "end":
return _context.stop();
}
}
}, _marked, this);
}
После прочтения этого вывода я подумал, что реализация regeneratorRuntime.keys
может содержать ответ, но, похоже, что эта функция загружает все ключи в память одновременно:
exports.keys = function(object) {
var keys = [];
for (var key in object) {
keys.push(key);
}
keys.reverse();
// Rather than returning an object with a next method, we keep
// things simple and return the next function itself.
return function next() {
while (keys.length) {
var key = keys.pop();
if (key in object) {
next.value = key;
next.done = false;
return next;
}
}
// To avoid creating an additional object, we just hang the .value
// and .done properties off the next function object itself. This
// also ensures that the minifier will not anonymize the function.
next.done = true;
return next;
};
};
Есть идеи?
Комментарии:
1. Я сомневаюсь, что есть какой-либо способ сделать это. Нет стандартного интерфейса, который позволяет получать указатели на свойства объекта или обращаться к ним через числовой индекс. Это просто академическое упражнение или у вас есть реальная потребность в этом?
2. Это скорее академическое упражнение, но я думаю, что это было бы полезно для написания кода, который преобразует объекты в функциональном стиле без излишнего выделения памяти.
3. Насколько я понимаю, все ключи объекта уже находятся в памяти (пожалуйста, поправьте меня, если я ошибаюсь). Единственная стоимость
Object.keys
— это созданиеArray
с неглубокой копией ключей (что обычно означает простоArray
с достаточным количеством пробелов для#keys
указателей, что не так уж много). Ваш движок JS может даже использовать это, чтобы удалить фактический,Array
если он никогда не сохранялся / мутировал, получая доступ к ключам напрямую по запросу (не учитывается, но тогда стоимость массива указателей довольно тривиальна). В лучшем случае это кажется преждевременной оптимизацией, основанной на неправильном понимании JS.4. Я действительно считаю, что ключи объекта уже находятся в памяти. Полагаю, мне следовало спросить, есть ли способ сделать это без выделения массива для хранения ключей, когда я должен был бы иметь возможность просто перебирать их там, где они уже хранятся. Я согласен, что разница в производительности, вероятно, была бы незначительной; мне было просто любопытно.
Ответ №1:
Нет, нет способа создать отложенный итератор свойств. В ES6 были Reflect.enumerate
функции и генератор, позволяющие вам написать этот keys
помощник, но в ES5 такой возможности не существует — учитывая, что в ES5 не было концепции итератора, это неудивительно.