Есть еще один способ создать функцию. Используется редко, но иногда альтернативы нет.
Синтаксис создания функции:
let func = новая функция ([arg1, arg2, ...argN], functionBody);
Функция создается с аргументами arg1...argN
и заданным functionBody
.
Это легче понять, посмотрев на пример. Вот функция с двумя аргументами:
let sum = new Function('a', 'b', 'return a + b'); Предупреждение(сумма(1, 2)); // 3
А вот функция без аргументов, только тело функции:
letsayHi = new Function('alert("Hello")'); сказатьПривет(); // Привет
Основное отличие от других способов, которые мы видели, заключается в том, что функция создается буквально из строки, передаваемой во время выполнения.
Все предыдущие декларации требовали от нас, программистов, написания кода функции в скрипте.
Но new Function
позволяет превратить любую строку в функцию. Например, мы можем получить новую функцию с сервера и затем выполнить ее:
let str = ... получать код с сервера динамически... пусть func = новая функция (str); функция();
Он используется в очень специфических случаях, например, когда мы получаем код с сервера или для динамической компиляции функции из шаблона в сложных веб-приложениях.
Обычно функция запоминает, где она родилась, в специальном свойстве [[Environment]]
. Он ссылается на лексическую среду, в которой он создан (мы рассмотрели это в главе Область видимости переменных, замыкание).
Но когда функция создается с использованием new Function
, ее [[Environment]]
устанавливается для ссылки не на текущую лексическую среду, а на глобальную.
Таким образом, такая функция не имеет доступа к внешним переменным, а только к глобальным.
функция getFunc() { пусть значение = «тест»; let func = new Function('alert(value)'); возврат функции; } ПолучитьФунк()(); // ошибка: значение не определено
Сравните это с обычным поведением:
функция getFunc() { пусть значение = «тест»; пусть func = function() { alert(value); }; возврат функции; } ПолучитьФунк()(); // "тест" из лексического окружения getFunc
Эта особенность new Function
выглядит странно, но на практике оказывается очень полезной.
Представьте, что нам нужно создать функцию из строки. Код этой функции неизвестен на момент написания скрипта (поэтому мы не используем обычные функции), но будет известен в процессе выполнения. Мы можем получить его с сервера или из другого источника.
Наша новая функция должна взаимодействовать с основным скриптом.
Что, если бы он мог получить доступ к внешним переменным?
Проблема в том, что перед публикацией JavaScript в производство он сжимается с помощью минификатора — специальной программы, которая сжимает код за счет удаления лишних комментариев, пробелов и — что важно — переименовывает локальные переменные в более короткие.
Например, если функция let userName
, минификатор заменяет ее на let a
(или другую букву, если она занята) и делает это везде. Обычно это безопасно, поскольку переменная является локальной, и ничто за пределами функции не может получить к ней доступ. А внутри функции минификатор заменяет каждое упоминание о ней. Минификаторы умны, они анализируют структуру кода и ничего не ломают. Это не просто тупая программа «найти и заменить».
Поэтому, если бы new Function
имела доступ к внешним переменным, она не смогла бы найти переименованное userName
.
Если бы new Function
имела доступ к внешним переменным, у нее были бы проблемы с минификаторами.
Кроме того, такой код будет архитектурно плох и подвержен ошибкам.
Чтобы передать что-то функции, созданной как new Function
, мы должны использовать ее аргументы.
Синтаксис:
let func = новая функция ([arg1, arg2, ...argN], functionBody);
По историческим причинам аргументы также можно задавать в виде списка, разделенного запятыми.
Эти три декларации означают одно и то же:
новая функция('a', 'b', 'return a + b'); // базовый синтаксис новая функция('a,b', 'вернуть a + b'); // через запятую новая функция('a, b', 'return a + b'); // через запятую и пробелы
Функции, созданные с помощью new Function
, имеют [[Environment]]
ссылающуюся на глобальную лексическую среду, а не на внешнюю. Следовательно, они не могут использовать внешние переменные. Но это на самом деле хорошо, потому что страхует нас от ошибок. Явная передача параметров является гораздо лучшим методом с архитектурной точки зрения и не вызывает проблем с минификаторами.