JS Function-constructor пересматривается каждый раз?
В MDN о функциях и области действия функций, что это означает, анализируя каждый раз, когда оценивается? Можно ли это наблюдать по кодам?
Цитата в разделе Конструктор функций против объявления функции против выражения функции:
Функции, определенные выражениями функций и объявлениями функций, анализируются только один раз, в то время как функции, определенные конструктором Function, - нет. То есть строка тела функции, передаваемая конструктору Function, должна анализироваться каждый раз, когда она оценивается. Хотя выражение функции каждый раз создает замыкание, тело функции не обрабатывается повторно, поэтому выражения функции по-прежнему работают быстрее, чем "новая функция (...)". Поэтому конструктора Function следует избегать всякий раз, когда это возможно.
Однако следует отметить, что выражения функций и объявления функций, вложенные в функцию, сгенерированную путем анализа строки конструктора Function , не анализируются повторно. Например:
var foo = (new Function("var bar = \'FOO!\';\nreturn(function() {\n\talert(bar);\n});"))();
foo(); //The segment "function() {\n\talert(bar);\n}" of the function body string is not re-parsed.
Я написал фрагмент кода, чтобы (попытаться) проверить и понять его:
var bar = 'FOO!';
var foo = (new Function("return(function() {\n\talert(bar);\n});"))();
bar = 'FOO! again';
foo(); //The segment "function() {\n\talert(bar);\n}" of the function body string is not re-parsed.
var bar2 = 'FOO!2';
var foo2 = function() { alert(bar2); };
bar2 = 'FOO!2 again';
foo2();
Оба оповещения "опять-версия".
Что значит перепарсированный или нет?
Можно ли это проиллюстрировать результатами кода?
Благодарю.
К вашему сведению, я попробовал другой фрагмент кода:
var bar = 'FOO!';
var string1 = "return(function() {\n\talert(bar);\n});";
var foo = (new Function(string1))();
bar = 'FOO! again';
foo(); //The segment "function() {\n\talert(bar);\n}" of the function body string is not re-parsed.
string1 = "return(function() {\n\talert(bar + ' more');\n});";
foo();
Оба предупреждения "FOO! Снова", а не "FOO! Снова больше".
2 ответа
Они хотели подчеркнуть, что парсер JS должен работать каждый раз, когда Function
конструктор называется - в основном просто очевидное. Кэширование переданной строки кода не выполняется.
Это актуально [только] по сравнению с замыканиями. Предположим, у нас есть эти две функции:
function makeAlerterParse(string) {
return Function("alert("+JSON.stringify(string)+");");
}
function makeAlerterClosure(string) {
return function alerter() { alert(string); };
}
Обе декларации функций будут проанализированы при загрузке скрипта - никаких сюрпризов. Тем не менее, в закрытии также alerter
Выражение функции уже проанализировано. Давайте сделаем несколько предупреждений:
var alerter1 = makeAlerterParser("1"); // Here the parser will be invoked
alerter1(); // no parsing, the function is instantiated already and
alerter1(); // can be interpreted again and again.
var alerter2 = makeAlerterClosure("2"); // Here, no parser invocation -
alerter2(); // it's just a closure whose code was already known
alerter2(); // but that has now a special scope containing the "2" string
Все еще нет ничего удивительного? Хорошо, тогда ты уже все понял. Предупреждение только в том, что явный вызов типа
for (var fnarr=[], i=0; i<100; i++)
fnarr[i] = makeAlerterParse(i);
будет действительно 100 вызовов парсера JS, в то время как закрывающая версия поставляется бесплатно.
Насколько я понимаю, с помощью конструктора функций движок сохраняет строку тела как строку, а не как функцию, которую он создает; таким образом, его необходимо будет повторно анализировать (преобразовывать из строки в функцию) каждый раз, когда вы его используете.
Принимая во внимание, что объявление функции или выражение анализирует его в первый раз и сохраняет его в памяти как функцию, поэтому всякий раз, когда вы его используете, оно отправляется в область памяти функции для доступа к ней.
Если мы посмотрим на ваш пример, я думаю, что это можно прочитать так:
var bar = 'FOO!';
var foo = (new Function("return(function() {\n\talert(bar);\n});"))();
// function() {\n\talert(bar);\n} is a function declaration, so when it's evaluated
// the first time, the engine pulls out the function and stores it as an anonymous function
bar = 'FOO! again';
foo(); //The segment "function() {\n\talert(bar);\n}" of the function body string is not re-parsed.
"Foo! again '- ожидаемый результат, так как функция просто ссылается на переменную bar
так что однажды foo
построен, он просто указывает на переменную, а не принимает ее значение.
Я думаю foo
будет храниться что-то вроде этого:
"return function_location"
Который будет анализироваться каждый раз, когда он выполняется.
В вашем последнем примере это не предупреждает "FOO! снова больше ", потому что когда вы использовали конструктор, он сохранил его как строку, а не как указатель на переменную. Но что интересно в вашем последнем примере, это то, что он хранит внешнюю переменную в виде строки, но сохраняет внутреннюю переменную такой, какая она есть.