Расположение скобок для автоматического выполнения анонимных функций JavaScript?

107

Недавно я сравнивал текущую версию json2.js с версией, которая была в моем проекте, и заметил разницу в том, как выражение функции создавалось и выполнялось самостоятельно.

Код, используемый для заключения анонимной функции в круглые скобки и последующего ее выполнения,

(function () {
  // code here
})();

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

(function () {
  // code here
}());

В принятом ответе на синтаксис инкапсулированной анонимной функции Explain JavaScript есть комментарий CMS, который «оба: (function(){})();и (function(){}());действительны».

Интересно, в чем разница? Занимает ли первое память, оставляя глобальную анонимную функцию? Где должна быть скобка?

Кевин Хакансон
источник
1
Также прочтите о цели этой конструкции или ознакомьтесь с ( техническим ) объяснением (также здесь ). Чтобы узнать, зачем нужны круглые скобки, см. Этот вопрос .
Bergi

Ответы:

66

Они практически такие же.

Первый оборачивает функцию в круглые скобки, чтобы сделать ее допустимым выражением, и вызывает ее. Результат выражения не определен.

Второй выполняет функцию, и круглые скобки вокруг автоматического вызова делают его допустимым выражением. Он также оценивается как undefined.

Я не думаю, что есть «правильный» способ сделать это, поскольку результат выражения тот же.

> function(){}()
SyntaxError: Unexpected token (
> (function(){})()
undefined
> (function(){return 'foo'})()
"foo"
> (function(){ return 'foo'}())
"foo"
Медер Омуралиев
источник
8
JSLint хочет "(function () {} ());". JSLint говорит: «Переместите вызов в скобки, содержащие функцию».
XP1
27
На самом деле вы не ограничены этими двумя, вы можете использовать практически все, что заставляет компилятор понять, что функция является частью выражения, а не оператора, например +function(){}()или !function(){}().
Tgr
49
@ XP1: JSLint хочет, чтобы многие вещи были специфичны для стиля Крокфорда, а не были существенными. Это одна из них.
TJ Crowder
@TJCrowder. Что бы вы порекомендовали? jQuery использует первый стиль, а Крокфорд - второй.
Teej
4
@ThorpeObazee: Это действительно не имеет значения, так что делайте то, что вам больше нравится. Я бы порекомендовал против некоторых из наиболее необычных ( -function(){}();, !function(){}();и, в основном, любой другой оператор, который был перед этим, functionтакже работал, но я бы придерживался версий с использованием скобок). Я вижу первое намного больше, чем второе, и это мое предпочтение; для меня это тоже имеет больше смысла, но это субъективно. FWIW: jsbin.com/ejaqow
TJ Crowder
13

В таком случае это не имеет значения. Вы вызываете выражение, которое преобразуется в функцию в первом определении, а определяете и немедленно вызываете функцию во втором примере. Они похожи, потому что выражение функции в первом примере - это просто определение функции.

Есть и другие, более очевидные полезные случаи для вызова выражений, которые разрешаются в функции:

(foo || bar)()
Триптих
источник
3
Для пояснения другим читателям (в основном потому, что я сам сначала этого не понял :)), foo и / или bar уже должны быть равны некоторой функции. (например, foo = function(){alert('hi');}если ни одна из функций не является функцией, выдается ошибка.
Александр Берд,
3
@AlexanderBird Дальнейшее уточнение - он также выдаст ошибку, если fooон "правдив", но не является функцией.
JLRishe
9

Нет никакой разницы, кроме синтаксиса.

Что касается вашего беспокойства по поводу второго способа сделать это:

Рассматривать:

(function namedfunc () { ... }())

namedfuncпо-прежнему не будет в глобальной области, даже если вы указали имя. То же самое и с анонимными функциями. Единственный способ получить его в этой области - назначить его переменной внутри скобок.

((namedfunc = function namedfunc () { ... })())

Внешние скобки не нужны:

(namedfunc = function namedfunc () { ... })()

Но вы все равно не хотели этого глобального заявления, не так ли?

Итак, это сводится к:

(function namedfunc () { ... })()

И вы можете уменьшить его еще больше: имя не нужно, поскольку оно никогда не будет использоваться (если ваша функция не является рекурсивной ... и даже тогда вы можете использовать arguments.callee)

(function () { ... })()

Я так думаю об этом (может быть, неправильно, я еще не читал спецификацию ECMAScript). Надеюсь, поможет.

Кристиан Санчес
источник
Обратите внимание, что arguments.calleeэто устарело с ES5 (и запрещено в строгом режиме).
nyuszika7h
«Внешние скобки не нужны:» - я думаю, они предотвращают ошибки при объединении файлов, иначе вам понадобится! или что-то.
Jimmyt1988,
-3

Разница просто существует потому, что Дугласу Крокфорду не нравится первый стиль для IIFE ! (серьезно) Как видно на этом видео !!.

Единственная причина существования дополнительной обертки (){в обоих стилях} состоит в том, чтобы помочь сделать этот раздел кода Выражением функции , поскольку объявление функции не может быть вызвано немедленно. Некоторые скрипты / преуменьшать-ERS просто использовать +, !, -и ~вместо того, чтобы слишком круглые скобки. Как это:

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

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

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

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

И все это точно так же, как ваши альтернативы. Выбирать среди этих случаев вы делаете самостоятельно и не имеет значения. {Те , с ()продукцией 1 байт большего файла ;-)}

БехрадХодаяр
источник