Объясните синтаксис инкапсулированной анонимной функции
Резюме
Можете ли вы объяснить обоснование синтаксиса для инкапсулированных анонимных функций в JavaScript? Почему это работает: (function(){})();
, но это не так: function(){}();
?
Что я знаю
В JavaScript создается некоторая именованная функция:
function twoPlusTwo(){
alert(2 + 2);
}
twoPlusTwo();
Вы также можете создать анонимную функцию и присвоить ее переменной:
var twoPlusTwo = function(){
alert(2 + 2);
};
twoPlusTwo();
Вы можете инкапсулировать блок кода, создав анонимную функцию, затем обернув ее в скобки и выполнив ее немедленно:
(function(){
alert(2 + 2);
})();
Это полезно при создании модульных скриптов, чтобы избежать загромождения текущей области или глобальной области с потенциально конфликтующими переменными - как в случае с скриптами Greasemonkey, плагинами jQuery и т.д.
Теперь я понимаю, почему это работает. Скобки заключают в себе содержимое и выставляют только результат (я уверен, что есть лучший способ описать это), например, с (2 + 2) === 4
.
Что я не понимаю
Но я не понимаю, почему это не работает одинаково:
function(){
alert(2 + 2);
}();
Можете ли вы мне это объяснить?
Ответы
Ответ 1
Он не работает, потому что он анализируется как FunctionDeclaration
, и идентификатор имени объявления функции является обязательным.
Когда вы заключаете его в круглые скобки, оно оценивается как выражение FunctionExpression
, а выражения функций могут именоваться или нет.
Грамматика FunctionDeclaration
выглядит следующим образом:
function Identifier ( FormalParameterListopt ) { FunctionBody }
И FunctionExpression
s:
function Identifieropt ( FormalParameterListopt ) { FunctionBody }
Как вы можете видеть, токен Identifier
(Identifier opt) в FunctionExpression
является необязательным, поэтому мы можем иметь выражение функции без определенного имени:
(function () {
alert(2 + 2);
}());
Или именованное выражение функции:
(function foo() {
alert(2 + 2);
}());
Круглые скобки (формально называемые оператором группировки) могут заключать только выражения, и вычисляется выражение функции.
Два производства грамматики могут быть неоднозначными, и они могут выглядеть точно так же, например:
function foo () {} // FunctionDeclaration
0,function foo () {} // FunctionExpression
Анализатор знает, является ли он FunctionDeclaration
или FunctionExpression
, в зависимости от контекста, в котором он появляется.
В приведенном выше примере второй является выражением, поскольку оператор запятой также может обрабатывать только выражения.
С другой стороны, FunctionDeclaration
может фактически появляться только в том, что называется " Program
" кодом, то есть кодом снаружи в глобальной области видимости и внутри FunctionBody
других функций.
Следует избегать функций внутри блоков, потому что они могут привести к непредсказуемому поведению, например:
if (true) {
function foo() {
alert('true');
}
} else {
function foo() {
alert('false!');
}
}
foo(); // true? false? why?
Ответ 2
Несмотря на то, что это старый вопрос и ответ, в нем обсуждается тема, которая по сей день вызывает много разработчиков для цикла. Я не могу подсчитать количество разработчиков JavaScript-разработчиков, с которыми я беседовал, кто не мог рассказать мне разницу между объявлением функции и выражением функции, и кто не знал, что непосредственно вызванное выражение функции.
Я хотел бы упомянуть, однако, очень важную вещь, которая заключается в том, что фрагмент кода Premasagar не работает, даже если он дал ему идентификатор имени.
function someName() {
alert(2 + 2);
}();
Причина, по которой это не сработает, заключается в том, что механизм JavaScript интерпретирует это как объявление функции, за которым следует полностью несвязанный оператор группировки, который не содержит выражения, а операторы группировки должны содержать выражение. Согласно JavaScript, приведенный выше фрагмент кода эквивалентен следующему.
function someName() {
alert(2 + 2);
}
();
Еще одна вещь, которую я хотел бы отметить, что может быть полезной для некоторых людей, заключается в том, что любой идентификатор имени, который вы предоставляете для выражения функции, практически бесполезен в контексте кода, за исключением самого определения функции.
var a = function b() {
// do something
};
a(); // works
b(); // doesn't work
var c = function d() {
window.setTimeout(d, 1000); // works
};
Конечно, использование идентификаторов имен с вашими определениями функций всегда полезно, когда дело доходит до отладки кода, но что-то еще полностью...: -)
Ответ 3
Отличные ответы уже опубликованы. Но я хочу отметить, что объявления функций возвращают пустую запись завершения:
14.1.20 - семантика выполнения: оценка
ФункцияDeclaration: function
BindingIdentifier (
FormalParameters )
{
FunctionBody }
Этот факт непросто наблюдать, потому что большинство способов получить возвращаемое значение преобразует объявление функции в выражение функции. Однако eval
показывает это:
var r = eval("function f(){}");
console.log(r); // undefined
Ответ 4
В javascript это называется выражением немедленного вызова функций (IIFE).
Чтобы сделать это функциональным выражением, вы должны:
-
заключите это используя()
-
поместите пустой оператор перед ним
-
присвоить его переменной.
В противном случае он будет рассматриваться как определение функции, и тогда вы не сможете одновременно вызывать/вызывать его следующим образом:
function (arg1) { console.log(arg1) }();
Вышеуказанное даст вам ошибку. Потому что вы можете немедленно вызвать только выражение функции.
Это может быть достигнуто несколькими способами: Способ 1:
(function(arg1, arg2){
//some code
})(var1, var2);
Способ 2:
(function(arg1, arg2){
//some code
}(var1, var2));
Способ 3:
void function(arg1, arg2){
//some code
}(var1, var2);
способ 4:
var ll = function (arg1, arg2) {
console.log(arg1, arg2);
}(var1, var2);
Все вышеизложенное немедленно вызовет выражение функции.
Ответ 5
У меня есть еще одно небольшое замечание. Ваш код будет работать с небольшим изменением:
var x = function(){
alert(2 + 2);
}();
Я использую вышеупомянутый синтаксис вместо более широко распространенной версии:
var module = (function(){
alert(2 + 2);
})();
потому что мне не удалось получить отступы для правильной работы файлов javascript в vim. Кажется, что vim не любит фигурные скобки внутри открытой круглой скобки.
Ответ 6
Возможно, более короткий ответ будет заключаться в том, что
function() { alert( 2 + 2 ); }
является функцией literal, которая определяет (анонимную) функцию. Дополнительный() -пар, который интерпретируется как выражение, не ожидается при заполнении, только литералы.
(function() { alert( 2 + 2 ); })();
находится в выражении выражения, который вызывает анонимную функцию.
Ответ 7
(function(){
alert(2 + 2);
})();
Выше приведен правильный синтаксис, поскольку все, что передается внутри скобки, рассматривается как выражение функции.
function(){
alert(2 + 2);
}();
Выше недопустимый синтаксис. Поскольку синтаксический анализатор java script ищет имя функции после ключевого слова функции, поскольку он не находит ничего, он вызывает ошибку.
Ответ 8
Они могут использоваться с параметрами-аргументами, такими как
var x = 3;
var y = 4;
(function(a,b){alert(a + b)})(x,y)
будет выглядеть как 7
Ответ 9
Эти дополнительные скобки создают дополнительные анонимные функции между глобальным пространством имен и анонимной функцией, содержащей код. А в функциях Javascript, объявленных внутри других функций, можно обращаться только к пространству имен родительской функции, которая их содержит. Поскольку дополнительный объект (анонимная функция) между глобальным охватом и фактическим охватом кода не сохраняется.