Конструктор функции против оператора функции - PullRequest
10 голосов
/ 26 сентября 2010

сегодня Я читал, что у нас есть способ объявления функции с помощью конструктора функций . Но я никогда не видел реальной реализации, которая использует конструктор Function в реальном. Поэтому я хотел бы спросить, есть ли обстоятельства, которые мы можем получить, используя конструктор Function, а не объявление function()? И в чем заключаются скрытые различия? (Если есть)

Конструктор функций

var func = new Function("x", "y", "return x*y;"); // pass the context by String

функция ():

var func = function(x, y){ return x*y; }

Спасибо

Ответы [ 4 ]

10 голосов
/ 26 сентября 2010

Конструктор Function является формой eval, которой обычно следует избегать (это медленный и считается небезопасным).На самом деле нет смысла использовать конструктор Function по сравнению со встроенным оператором функции, если вы не хотите создавать функцию из динамических компонентов, что довольно редко.Для этой формы есть законное использование, однако большую часть времени она используется излишне, поэтому на нее смотрят свысока и, как правило, избегают.

Кроме того, функции, созданные с помощью конструктора функций, не будут хранить ссылку на среду, в которой они были определены (замыкание).При выполнении они извлекают эти переменные напрямую из глобальной области.

var f, a;
(function () {
   var a = 123;
   f = new Function("return a");
})();

f(); //undefined

a = "global"
f(); // "global"

В то время как обычные функции сохраняют ссылку на среду, в которой они были определены:

var f;
(function () {
   var a = 123;
   f = function () { return a; }
})();
f(); //123
2 голосов
/ 26 сентября 2010

Если вы пишете анализатор Javascript и интерпретируете строку как функцию, вы можете использовать конструктор функции.Например, если вам дают:

"function(x){return x+2}"

И у вас есть какой-то лексический парсер, и он обнаруживает, что подстрока действительно является функцией, чтобы перевести ее в реальную функцию, которую вы использовали бы new Function, и добавить ееваше дерево.

В противном случае, я не могу придумать ничего особенного.

2 голосов
/ 26 сентября 2010

Что ж, очевидная разница при работе со строками заключается в том, что у вас есть возможность метапрограммирования путем конструирования строки во время выполнения (так же, как вы могли бы eval). Тем не менее, это обоюдоострый, и, возможно, приводит к ряду других проблем с буквальной конкатенацией (внедрение), и, возможно, просто сложность. Если вам не нужна строковая версия, я бы не стал ее использовать, если честно.

Дополнительным преимуществом обычной (не строковой) версии является то, что большинство миниатюр (или обфускаторов) javascript будут знать, что с ними делать. Это кажется маловероятным для строки, т. Е. Они оставят ее «как есть» (не минимизировано и не запутано).

0 голосов
/ 25 апреля 2017

Дополнительные примечания к публикации Кристиана Санчеса.

Вы никогда не сможете получить доступ к локальным переменным области действия в рамках оценки функций.

Позволяет увидеть разницу между 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-файлах.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...