Комментарии 12
А если вместо eval
сгенерировать js-файл, в котором экспортируется массив с объявлением всех 256 вариантов функций? Все-таки eval
в чистом виде — вещь довольно спорная.
А в чем для транслятора должна быть принципиальная разница между текстом функции из файла и текстом, сгенерированным в рантайме?
В том-то и дело, что для транслятора — никакой. А вот если CI из соображений безопасности не пропускает пуши с eval
— то разница существенная.
"Не пропускает пуши с eval" - это такая же паранойя, как и "goto - зло". То есть грамотный разработчик хоть на лету файл сгенерирует и сделает require
, но это вынуждает его искать лишние лазейки.
Да, "тривиально" запушить eval
должно быть сложно для большинства разработчиков, но для "грамотных" должна быть возможность сделать именно это, а не заниматься прокладкой бэкдоров.
return new Function('obj', res.join(','));
У меня получились ровно те же результаты, что и для eval
.
Зато он не "evil" )
eval имеет дурную репутацию прежде всего из-за доступа к тому скоупу, где запускается.
Ну, в случае indirect call он доступа туда не имеет.
Думается, что еще быстрее будет вариант с switch на 2**x вариантов. Он в итоге может транслироваться в простую jump таблицу (не проверял как оно там в v8, но думаю оптимизация достаточно очевидная).
Мне стало интересно и я уже проверил - для 1M объектов производительность не улучшается. Занятно то, что если использовать Object.assign()
то скорость даже проседает.
64K - 51ms, 1M - 437ms, 8M - 3458ms - везде не лучший результат
Полный код
const objs = require('./objs-64K.json');
const keys = Array(8).fill()
.map((_, i) => String.fromCharCode('a'.charCodeAt() + i)); // ключи ['a', .., 'h']
const hrtime = process.hrtime.bigint;
const copy = keys.map(key => `obj['${key}-copy'] = obj['${key}']`); // copy-сегменты
const caseText = Array(1 << keys.length).fill() // copy-блоки для каждой маски
.map((_, mask) => {
const res = [];
const orig = mask;
for (let i = 0; mask; i++, mask >>= 1) {
if (mask & 1) {
res.push(copy[i]);
}
}
return orig ? `case ${orig}:` + res.join(',') + `;break;\n` : '';
})
.join('');
const fnCopy = new Function('obj', `switch (obj['mask']) {${caseText}}`)
const ts = hrtime();
objs.forEach(fnCopy);
console.log(Number(hrtime() - ts)/1e6, 'ms');
Node.js: Клонирование ключей. Is eval() evil?