2010-09-26 5 views
8

сегодня I've read we have a way of declaring the function by Function constructor. Но я никогда не видел фактической реализации, которая использует конструктор 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; } 

Благодаря

+2

См.: [Законное использование конструктора функций] (http://stackoverflow.com/questions/3026089/legitimate-uses-of-the-function-constructor) – CMS

+1

Хорошее видео о функциях javascript http: // developer. yahoo.com/yui/theater/video.php?v=crockonjs-3 – Thomas

+0

@CMS @Thomas, Спасибо за ссылки. – c4il

ответ

10

Конструктор функций представляет собой форму 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 
+0

Мне интересно, что они думали при создании этого объекта Function. – Tarik

+0

Ну, это очень полезно для чего-то вроде соревнования js1k :-) – Pointy

+3

@ Daniel, Обратите внимание, что «медленная» часть будет только функцией * компиляции *, после того как объект функции будет построен, он будет вести себя точно так же, как если бы он был создан с помощью 'FunctionDeclaration' или' FunctionExpression'. – CMS

2

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

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

+0

Можете ли вы привести пример проблем программирования мета? – Tarik

+1

@Braveyard - сделано * правильно * мета-программирование в порядке; но это также отличный способ открыть xss отверстия, если это сделано неправильно. Например, если пользователь может злоупотреблять строкой, чтобы другие пользователи могли публиковать свои файлы cookie. –

2

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

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

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

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

0

Дополнительные примечания к проводке Cristian Санчеса».

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

Позволяет увидеть разницу между функцией и eval.

Функционально-Пример:

var f, a = 1; 
(function() { 
    var a = 123; 
    f = new Function("return a"); 
})(); 
console.log(f()) // 1 

Функция-конструктор ничего не знаю о локальной области видимости, не знает, потому что он создает новый изолированный Scope под окном/глобальный-объект - не в том месте, где его - это основное отличие от 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() выдает ошибку - "а" не определено в его собственной (и глобальной) области. Функция-конструктор имеет свой собственный специальный объем, не зная ничего снаружи, кроме его родительской области - окна/глобального объекта.

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 

Вы не можете выполнять функции непосредственно вызова/применить, тоже (не "новый" требуется).

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.

С строгим режимом «это» (правильно) undefined по умолчанию - пока вы не привяжете объект.

Function('console.log(this)')(); // this is 'window' in sloppy mode 
Function('"use strict"; console.log(this)')(); // this is undefined - correct behavior 

Если вы хотите, вы можете связать «это» явно

// 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 

Тот же результат с прямым вызовом, без «нового» ключевого слова.

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 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 Мб в комплекте скриптов), в то время как вы хотите отобразить загрузочный бар или что-то еще для пользователя, вместо этого загружая все это с помощью тега script, в то время как пользователь ждет загрузки страницы без какого-либо визуального ответа.

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

«Функция» и «eval» являются плохими только с ненадежным кодом (когда на него влияют другие пользователи) или в циклах (медленная производительность при компиляции), но вполне нормально загружать & оценивать свои собственные сценарии при запуске , если код такой же, как во внешних Javascript-файлах.

Смежные вопросы