Подтвердить что ты не робот

Объясните синтаксис инкапсулированной анонимной функции

Резюме

Можете ли вы объяснить обоснование синтаксиса для инкапсулированных анонимных функций в 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);
}();

Можете ли вы мне это объяснить?

4b9b3361

Ответ 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).

Чтобы сделать это функциональным выражением, вы должны:

  1. заключите это используя()

  2. поместите пустой оператор перед ним

  3. присвоить его переменной.

В противном случае он будет рассматриваться как определение функции, и тогда вы не сможете одновременно вызывать/вызывать его следующим образом:

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