Дополнительные примечания к публикации Кристиана Санчеса.
Вы никогда не сможете получить доступ к локальным переменным области действия в рамках оценки функций.
Позволяет увидеть разницу между Function и eval.
Функция-Пример:
var f, a = 1;
(function () {
var a = 123;
f = new Function("return a");
})();
console.log(f()) // 1
Function-Constructor ничего не знает о локальной области видимости, потому что он создает новый изолированный Scope под окном / global-Object - не в той позиции, где он находится - это главное отличие для eval.
Eval-Пример:
var f, a = 1;
(function () {
var a = 123;
eval("f = function() { return a }");
})();
console.log(f()) // 123
'eval' имеет доступ к локальным переменным. Даже если вы охватите все это.
var f, a = 1;
(function () {
var a = 123;
eval("f = (function() { function f() { return a }; return f; })();");
})();
console.log(f()) // still 123
В следующем примере f () выдает ошибку - «a» не определено в его собственной (и глобальной) области видимости. Function-Constructor имеет свою собственную специальную область видимости, не зная ничего за пределами - кроме его родительской области видимости - окна / глобального объекта.
delete a;
var f;
(function ()
{
var a = 1;
f = new Function("return a");
})();
console.log(f()); // Throws error (a is not defined)
Если вы хотите использовать локальные переменные, передайте их в качестве параметров в конструктор функций внутри внутренней области видимости!
var f, a = 1;
(function () {
var a = 123;
f = new Function("a", "return a");
console.log(f(a)); // passing inner a: result = 123
})();
console.log(f(a)); // passing outer a: result = 1
или
var result, a = 1;
(function () {
var a = 123;
var f = new Function("a", "return a");
result = f(a); // store result in global var
})();
console.log(result); // 123
Вы также можете выполнить функцию напрямую с помощью call / apply («новый» не требуется).
Function('a','b','c', 'console.log(c,b,a)').call(this, 1, 2, 3); // output: 3 2 1
Function('console.log(arguments)').apply(this, [1, 2, 3]); // access through arguments[0-3]
Кстати, всегда рекомендуется устанавливать строгий режим, чтобы включить правильное поведение ES5.
В строгом режиме это по умолчанию (правильно) не определено - пока вы не свяжете объект.
Function('console.log(this)')(); // this is 'window' in sloppy mode
Function('"use strict"; console.log(this)')(); // this is undefined - correct behavior
Если хотите, можете явно связать «this»
// bind 'this' to 'window' again
Function('"use strict";console.log(this)').bind(window)();
// bind 'this' to an Object without __proto__
Function('"use strict";console.log(this)').bind(Object.create(null))();
Конечно, вы можете связать любую функцию / класс-объект для расширения объектов.
function SomeClass()
{
this.a = 1;
}
var cls = new SomeClass();
new Function('"use strict"; this.a = 5; this.b = 6;').bind(cls)();
console.log(cls.a); // a = 5 - override property
console.log(cls.b); // b = 6 - appended property
Тот же результат с прямым вызовом, без ключевого слова "new".
function SomeClass()
{
this.a = 1;
}
var cls = new SomeClass();
Function('"use strict"; this.a = 5; this.b = 6; this.foo = function(){console.log("bar")}').call(cls);
console.log(cls.a); // a = 5 - override property
console.log(cls.b); // b = 6 - appended property
console.log(cls.foo()); // bar - appended function
Каждая функция / класс создается с помощью конструктора 'Function'.
Так что вам не нужно писать «Function» в вашем коде. Вы также можете использовать конструктор-объект.
function SomeClass()
{
this.a = 1;
}
var cls = new SomeClass();
SomeClass.constructor("this.a = 2;").call(cls);
cls; // SomeClass {a: 2}, because "SomeClass.constructor" === Function
Использование 'Function' хорошо, например, для игр, если у вас есть XMLHttpRequest-Preloader, который загружает очень большой Javascript-файл (5-10 МБ связанных скриптов), в то время как вы хотите отобразить панель загрузки или что-то еще для пользователя, вместо этого загружая все это с помощью скрипта-тега, пока пользователь ожидает загрузки страницы без какого-либо визуального ответа.
Не имеет значения, загружаете ли вы большой скрипт через тег скрипта или через функцию один раз при запуске. Двигатель должен загрузить простой код и оценить его в любом случае. Нет никаких аспектов безопасности, если ваш (!) Код получен из надежного источника (вашего домена) и вы знаете, что внутри.
'Function' и 'eval' плохи только для ненадежного кода (когда на него влияют другие пользователи) или в циклах (медленная производительность из-за компиляции), но вполне нормально загружать и оценивать ваши собственные скрипты при запуске, если код такой же, как во внешних Javascript-файлах.