2013-04-16 2 views
8

Во многих книгах/blog posts самости вызова анонимной шаблон функции записывается следующим образом:В чем разница между этой прямоприменяемой анонимной функцией (ака IIFE) реализациями

(function() { 
    var foo = 'bar'; 
})(); 

Однако работает JSLint на это дает это погрешность:

Move the invocation into the parens that contain the function.

eg изменить его на это работает:

(function() { 
    var foo = 'bar'; 
}()); 

Вопросы

  1. Почему первая реализация не достаточно хорош для JSLint? Каковы различия?
  2. Какая форма является предпочтительной? JSLint всегда прав?
  3. Почему это работает? в конце концов function(){}() throws a SyntaxError: Unexpected token (
    Но обертывание его с помощью парсеров делает его внезапной работой? например (function(){}()) - работает отлично
    (После того, как все это JavaScript, а не Lisp, так что эффект упаковки Паренс на ошибку синтаксиса ohterwise?)

EDIT: это (я бы не сказал точного дубликата): JSLint error: "Move the invocation into the parens that contain the function", поэтому мой главный вопрос №3, почему он вообще работает?

+2

Многие, много дубликатов. Короче говоря, 1-2: Потому что Крокфорд предпочитает так. JSHint был создан только потому, что JSLint слишком предвзято. 3. Поскольку идентификатор является необязательным для выражений функций, в то время как он является обязательным для объявления функции (parens делают это выражением функции). См. Http://kangax.github.io/nfe/#expr-vs-decl –

+0

Я не использую JSLint. Если вы это сделаете, поймите, что это предвзятый инструмент. Я * предпочитаю * последовательно использовать форму '; (function ..)()' - ведущий ';' предотвращает потенциально более неясную ошибку. Обе формы приводят к эквивалентным деревьям синтаксиса. – user2246674

+3

@ user2246674 Вы ошиблись в H за L? ': P' –

ответ

5

Я не знаю, как были развиты мнения Крокфорда, но я могу объяснить, почему обертывание в работе с парсерами.

Функция function() { ... } Синтаксис в JavaScript может представлять две разные вещи: объявление функции или выражение функции.

Объявление функции - это оператор , который определяет функцию в текущей области действия под указанным именем.

function example() { 
    alert("Hello World"); 
} 

example(); 

Выражение функция является выражением, которое вычисляется в новой Function инстанции.

var secondExample = function example() { 
    alert("Hello World"); 
}; 

secondExample(); 
example(); // <-- throws an Error: example is not defined. 

Независимо от того, является ли появление синтаксиса объявлением функции или оператором функции, зависит от ожидаемого синтаксического анализатора. Парсер JavaScript прост. Он не будет смотреть вперед и заметить, что за функцией следует (), поэтому она должна рассматривать ее как выражение. Он просто видит function в начале строки и поэтому рассматривает его как инструкцию, которая вызывает синтаксическую ошибку, за которой следует (). Когда вы завертываете его в круглые скобки, синтаксический анализатор вместо этого ожидает выражение, и он работает.

Обертка в круглых скобках (где бы вы их не разместили) является самым ясным способом сделать это, но все, что заставляет парсер ожидать выражения, будет работать.Например, побитовое НЕ оператор ~:

~function() { 
    alert("Hello World"); 
}(); 
+0

Ни один из приведенных паттернов не является функцией FunctionDeclaration, и хотя это делает адрес №3, он действительно идет по касательной .. – user2246674

+1

Я не понимаю, как первый из них не является FunctionDeclaration. В [spec] (http://www.ecma-international.org/ecma-262/5.1/#sec-13) FunctionDeclration является «Идентификатором функции (FormalParameterListopt) {FunctionBody}», и мой пример соответствует. –

+1

'(function() {})()' (первая форма) vs.'(function() {}())' (вторая форма) vs 'function() {}()' (в вопросе № 3). Конечно, вопрос № 3 не учитывает контекст, поскольку 'x = function() {}()' снова будет действительным (а не FunctionDeclaration). – user2246674

1

1.

Видимо, это проблема с конвенциями. Первый пример показывает «отсутствие соглашения» (source). Что касается реальных различий, их нет. Оба будут выполняться без ошибок.

2.

В этом случае, я предпочитаю свой первый пример, но это только мое условность. JSLint часто прав. Поэтому, если вы хотите следовать их соглашениям об именах, тогда, когда он показывает предупреждение, основанное на согласии, имеет смысл соответствовать соглашению.

3.

Это работает, потому что обертывание function(){} внутри () делает это выражение, которое когда-то в паре с окончательным () немедленно вызывает его. Следовательно, у вас есть выражение с выраженной немедленной вызывной функцией.