Создание функций "на лету"

В JavaScript существует возможность буквально создавать самим скриптом новые функции. Для этого используется такой синтаксис:

let func = new Function([arg1, arg2, ...argN], functionBody);

Функция создаётся с заданными аргументами arg1...argN и телом functionBody. Например, создадим функцию для вычисления суммы двух аргументов:

let sumTwo = new Function('a', 'b', 'return a+b;');

Здесь sumTwo ссылается на созданный объект-функцию, принимающий два аргумента с телом функции «return a+b». Запустим ее и посмотрим как она будет работать:

console.log( sumTwo(1, 2) );

Да, видим, что действительно вычисляется сумма от двух аргументов. Получается, что используя new Function() можно передавать строки с именами аргументов и строку с телом функции, а на выходе получать готовую функцию. То есть, JavaScript может создавать сам себя в процессе выполнения. И это иногда используется на практике.

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

let hello = new Function('console.log("hello");');
hello();

Надо сказать, что такое создание функций в процессе исполнения скрипта используется крайне редко. Один из примеров – прием сложных данных с сервера вместе с функцией, которая их распаковывает.

Также эти функции иначе ведут себя при замыканиях. Создадим внутри функции createMsg функцию с помощью new Function:

function createMsg() {
    let msg = "Hello";
    return new Function('console.log(msg);');
}

Теперь, при вызове функции createMsg() переменная hello ссылается на новую созданную функцию, которая выводит в консоль переменную msg:

let hello = createMsg();
hello();

Но в момент вызова возникает ошибка, связанная с отсутствие переменной msg. Почему так? Почему замыкание здесь не сработало? Дело в том, что когда функция создаётся с использованием new Function, она всегда ссылается на глобальное внешнее окружение, а не на родительское от createMsg. Поэтому такая функция имеет доступ только к глобальным переменным. И, так как глобальной переменной msg не существует, то и возникает такая ошибка.

Если в нашу программу добавить эту глобальную переменную:

let msg="global hello";

то эта строчка и будет отображена в консоли.

Видео по теме