Зачем вам нужно вызывать анонимную функцию в той же строке? - PullRequest
368 голосов
/ 17 июля 2009

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

// Create a new anonymous function, to use as a wrapper
(function(){
    // The variable that would, normally, be global
    var msg = "Thanks for visiting!";

    // Binding a new function to a global object
    window.onunload = function(){
        // Which uses the 'hidden' variable
        alert( msg );
    };
// Close off the anonymous function and execute it
})();

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

(function (msg){alert(msg)})('SO');

Мой вопрос: что за магия здесь происходит? Я думал, что когда я написал:

(function (msg){alert(msg)})

тогда будет создана новая безымянная функция, подобная функции "" (msg) ...

но тогда почему это не работает?

(function (msg){alert(msg)});
('SO');

Почему это должно быть в одной строке?

Не могли бы вы указать мне несколько постов или дать объяснение?

Ответы [ 19 ]

375 голосов
/ 17 июля 2009

Удалите точку с запятой после определения функции.

(function (msg){alert(msg)})
('SO');

Выше должно работать.

ДЕМО-страница: https://jsfiddle.net/e7ooeq6m/

Я обсуждал этот тип паттерна в этом посте:

jQuery и $ questions

EDIT:

Если вы посмотрите на спецификацию сценария ECMA , есть 3 способа определить функцию. (Стр. 98, раздел 13 «Определение функций»)

1. Использование конструктора функций

var sum = new Function('a','b', 'return a + b;');
alert(sum(10, 20)); //alerts 30

2. Использование объявления функций.

function sum(a, b)
{
    return a + b;
}

alert(sum(10, 10)); //Alerts 20;

3. Выражение функции

var sum = function(a, b) { return a + b; }

alert(sum(5, 5)); // alerts 10

Итак, вы можете спросить, в чем разница между декларацией и выражением?

Из спецификации скрипта ECMA:

FunctionDeclaration: идентификатор функции (FormalParameterListopt) {FunctionBody }

FunctionExpression: function Identifieropt (FormalParameterListopt) {FunctionBody }

Если вы заметили, «идентификатор» равен необязательно для выражения функции. И когда вы не даете идентификатор, вы создаете анонимную функцию. Это не значит, что вы не можете указать идентификатор.

Это означает, что действует следующее.

var sum = function mySum(a, b) { return a + b; }

Важно отметить, что вы можете использовать 'mySum' только внутри тела функции mySum, а не снаружи. Смотрите следующий пример:

var test1 = function test2() { alert(typeof test2); }

alert(typeof(test2)); //alerts 'undefined', surprise! 

test1(); //alerts 'function' because test2 is a function.

Демонстрационная версия

Сравните это с

 function test1() { alert(typeof test1) };

 alert(typeof test1); //alerts 'function'

 test1(); //alerts 'function'

Вооружившись этими знаниями, давайте попробуем проанализировать ваш код.

Если у вас есть такой код,

    function(msg) { alert(msg); }

Вы создали выражение функции. И вы можете выполнить это выражение функции, заключив его в круглые скобки.

    (function(msg) { alert(msg); })('SO'); //alerts SO.
126 голосов
/ 17 июля 2009

Это называется функцией, вызываемой самим собой.

То, что вы делаете, когда вызываете (function(){}), возвращает функциональный объект. Когда вы добавляете () к нему, он вызывается, и все в теле выполняется. ; обозначает конец оператора, поэтому второй вызов завершается неудачно.

93 голосов
/ 21 апреля 2011

Одна вещь, которая меня смущает, это то, что "()" - это операторы группировки.

Вот ваша основная объявленная функция.

Ex. 1:

var message = 'SO';

function foo(msg) {
    alert(msg);
}

foo(message);

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

Ex. 2:

var message = 'SO';

function foo(msg) {  //declares foo
    alert(msg);
}

(foo)(message);     // calls foo

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

Ex. 3.

var message = 'SO';

(function foo(msg) {
    alert(msg);
})(message);          // declares & calls foo

Наконец, нам не нужен этот дополнительный foo, потому что мы не используем имя для его вызова! Функции могут быть анонимными.

Ex. 4.

var message = 'SO';

(function (msg) {   // remove unnecessary reference to foo
    alert(msg);
})(message);

Чтобы ответить на ваш вопрос, вернитесь к примеру 2. Ваша первая строка объявляет некоторую безымянную функцию и группирует ее, но не вызывает ее. Вторая строка группирует строку. Оба ничего не делают. (Первый пример Винсента.)

(function (msg){alert(msg)});  
('SO');                       // nothing.

(foo); 
(msg); //Still nothing.

Но

(foo)
(msg); //works
23 голосов
/ 17 июля 2009

Анонимная функция не является функцией с именем "". Это просто функция без имени.

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

Но, как и любое другое значение, иногда вы хотите использовать его без привязки к имени. Это самопризывающий паттерн.

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

function(){ alert("plop"); }
2;

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

var f = function(){ alert("plop"); }
var n = 2;

Вы также можете использовать синтетический сахар для привязки функции к переменной:

function f(){ alert("plop"); }
var n = 2;

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

(function(){ alert("plop"); })(); // will display "plop"
alert(2 + 3); // will display 5

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

Сказано, что это выглядит так, как будто вызывающая функция не имеет реального значения. Но вы должны иметь в виду, что разделитель области видимости JavaScript - это функция, а не блок ({}).

Таким образом, функция, вызывающая саму себя, на самом деле имеет то же значение, что и блок C ++, C # или Java. Это означает, что переменная, созданная внутри, не будет «просачиваться» за пределы области видимости. Это очень полезно в JavaScript, чтобы не загрязнять глобальную область.

19 голосов
/ 17 июля 2009

Это просто, как работает JavaScript. Вы можете объявить именованную функцию:

function foo(msg){
   alert(msg);
}

И назовите это:

foo("Hi!");

Или вы можете объявить анонимную функцию:

var foo = function (msg) {
    alert(msg);
}

И назовите это:

foo("Hi!");

Или, вы никогда не можете связать функцию с именем:

(function(msg){
   alert(msg);
 })("Hi!");

Функции также могут возвращать функции:

function make_foo() {
    return function(msg){ alert(msg) };
}

(make_foo())("Hi!");

Ничего не стоит, что любые переменные, определенные с помощью «var» в теле make_foo, будут закрываться каждой функцией, возвращаемой make_foo. Это закрытие, и это означает, что любые изменения, внесенные в значение одной функцией, будут видны другой.

Это позволяет вам инкапсулировать информацию, если вы хотите:

function make_greeter(msg){
    return function() { alert(msg) };
}

var hello = make_greeter("Hello!");

hello();

Так работает почти каждый язык программирования, кроме Java.

8 голосов
/ 17 июля 2009

код, который вы показываете,

(function (msg){alert(msg)});
('SO');

состоит из двух операторов. Первое - это выражение, которое возвращает функциональный объект (который затем будет собирать мусор, потому что он не сохраняется). Второе - это выражение, которое дает строку. Чтобы применить функцию к строке, вам нужно либо передать строку в качестве аргумента функции при ее создании (что вы также продемонстрировали выше), либо вам нужно будет фактически сохранить функцию в переменной, чтобы вы могли примените его позже, на досуге. Вот так:

var f = (function (msg){alert(msg)});
f('SO');

Обратите внимание, что, сохраняя анонимную функцию (лямбда-функцию) в переменной, вы фактически даете ей имя. Следовательно, вы также можете определить обычную функцию:

function f(msg) {alert(msg)};
f('SO');
7 голосов
/ 29 декабря 2011

В сумме предыдущих комментариев:

function() {
  alert("hello");
}();

если переменная не назначена, выдает синтаксическую ошибку. Код анализируется как оператор функции (или определение), что делает синтаксически некорректно закрывающие скобки. Добавление скобок вокруг части функции говорит интерпретатору (и программисту), что это выражение функции (или вызов), как в

(function() {
  alert("hello");
})();

Это функция, вызывающая себя, то есть она создается анонимно и запускается немедленно, потому что вызов происходит в той же строке, где она была объявлена. Эта функция, вызывающая себя, обозначена знакомым синтаксисом для вызова функции без аргументов, а также добавлены скобки вокруг имени функции: (myFunction)();.

Имеется хороший SO SO-обсуждение, синтаксис функции JavaScript .

3 голосов
/ 23 апреля 2015

примеров без скобок:

void function (msg) { alert(msg); }
('SO');

(это единственное реальное использование void, афаик)

или

var a = function (msg) { alert(msg); }
('SO');

или

!function (msg) { alert(msg); }
('SO');

тоже работает. void вызывает выражение для оценки, а также присваивания и взрыва. последний работает с ~, +, -, delete, typeof, с некоторыми унарными операторами (void также один). не работают, потому что ++, -- из-за требования переменной.

разрыв строки не требуется.

3 голосов
/ 01 мая 2013

Я понимаю вопрос аскера так:

Как работает эта магия:

(function(){}) ('input')   // Used in his example

Я могу ошибаться. Тем не менее, обычная практика, с которой люди знакомы:

(function(){}('input') )

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

Источник: запись в блоге Выражение с немедленным вызовом функции (IIFE)

3 голосов
/ 17 июля 2009

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

alert(
    {foo: "I am foo", bar: "I am bar"}.foo
); // alerts "I am foo"

Относится к функциям. Так как они являются объектами, которые наследуются от Function.prototype, мы можем делать такие вещи, как:

Function.prototype.foo = function () {
    return function () {
        alert("foo");
    };
};

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

bar(); // alerts foo

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

var x = function () {} (); // this function is executed but does nothing

function () {} (); // syntax error

Еще одна вещь, которую вы можете сделать с функциями, как только вы их объявите, - это вызвать оператор new над ними и получить объект. Следующее эквивалентно:

var obj = new function () {
    this.foo = "bar";
};

var obj = {
    foo : "bar"
};
...