Как стать автором
Обновить

Комментарии 12

А если вместо eval сгенерировать js-файл, в котором экспортируется массив с объявлением всех 256 вариантов функций? Все-таки eval в чистом виде — вещь довольно спорная.

А в чем для транслятора должна быть принципиальная разница между текстом функции из файла и текстом, сгенерированным в рантайме?

В том-то и дело, что для транслятора — никакой. А вот если CI из соображений безопасности не пропускает пуши с eval — то разница существенная.

"Не пропускает пуши с eval" - это такая же паранойя, как и "goto - зло". То есть грамотный разработчик хоть на лету файл сгенерирует и сделает require, но это вынуждает его искать лишние лазейки.

Да, "тривиально" запушить eval должно быть сложно для большинства разработчиков, но для "грамотных" должна быть возможность сделать именно это, а не заниматься прокладкой бэкдоров.

Почему именно eval, а не конструктор Function? MDN, например, обещает для него лучшую производительность, чем для простого eval.

(Здесь ниже был еще один вопрос, но я внимательней почитал код и все понял. Возможно, это знак, что код с eval менее читаемый :)

return new Function('obj', res.join(','));

У меня получились ровно те же результаты, что и для eval.

Зато он не "evil" )

eval имеет дурную репутацию прежде всего из-за доступа к тому скоупу, где запускается.

Думается, что еще быстрее будет вариант с switch на 2**x вариантов. Он в итоге может транслироваться в простую jump таблицу (не проверял как оно там в v8, но думаю оптимизация достаточно очевидная).

Кстати, этот самый switch можно тоже генерировать через eval, чтобы не писать руками все варианты. @Kilor, сможете проверить, станет ли быстрее?

Мне стало интересно и я уже проверил - для 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');

Зарегистрируйтесь на Хабре, чтобы оставить комментарий