Зачем вам нужно вызывать анонимную функцию в той же строке?


374

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

// 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');

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

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


2
На других языках они называются указателями функций или делегатами, если вы хотите изучить структуры нижнего уровня.
Крис Москини

17
У тебя есть ; в первой строке
Оливер Ни

Теперь, когда вы знаете, как это работает ... Не используйте это. Мы должны перестать писать анонимные функции . С помощью всего лишь нескольких символов мы можем дать нашим функциям реальное имя и сделать отладку кода Javascript намного проще!
Стейн де Витт

1
Линия (function (msg){alert(msg)})('SO');работает полностью самостоятельно. Это не имеет никакого отношения к другой анонимной функции, которую вы разместили перед ней. Это две совершенно разные анонимные функции. Вы должны немедленно вызвать анонимную функцию, потому что она не имеет имени и на нее нельзя ссылаться впоследствии.
Осьминог

Ответы:


380

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

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

Над должно работать.

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

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

JQuery и $ вопросы

РЕДАКТИРОВАТЬ:

Если вы посмотрите на спецификацию скрипта ECMA , вы можете определить функцию тремя способами. (Страница 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 Script:

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

FunctionExpression: функция 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.

Live Demo

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

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

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

 test1(); //alerts 'function'

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

Когда у вас есть код, как,

    function(msg) { alert(msg); }

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

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

1
Да, но почему? Почему это должно быть как inline? Независимо от того, сколько пустого пространства я буду использовать.
Палиг

9
Как я уже писал, точка с запятой завершила определение анонимной функции. Поскольку у него нет имени (это анонимно!), Вы больше не сможете его называть. Если вы не поставите точку с запятой, функция все равно может быть выполнена.
Решение

Я думал, что автоматическая вставка точки с запятой в этом случае добавит точку с запятой, но это не так. Значит ты прав.
Носредна

1
Nosredna, JS ведет себя немного произвольно, когда дело доходит до добавления точки с запятой. Прочитайте эту подробную статью: blog.boyet.com/blog/javascriptlessons/…
SolutionYogi

Да, я вижу, что (function (msg) {alert (msg)}) ('SO'); работает. Я просто спрашивал, почему это работает. Где это указано или что это за функция JS. Поэтому, когда я просто вызываю: (function (msg) {alert (msg)}), что будет с функцией? Это будет GC'ed?
Палиг

129

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

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


Ах, хорошо, я понимаю, так что это просто особый синтаксис JS, верно? Как это объяснение больше всего! Простой и короткий :)
Palig

Я думаю, что неправильно говорить, что тело будет «изгнано». Он выполняется так же, как и любая другая функция. Поскольку он анонимный, вы должны либо сохранить ссылку где-либо, либо выполнить ее сразу же.
SolutionYogi

16
Лично мне даже не нравится термин «функция, вызывающая себя». Дело не в том, что функция вызывает себя. Программист написал эти скобки, чтобы вызвать его.
SolutionYogi

Это не «особый синтаксис» больше, чем что-либо особенное. На самом деле форма «имя функции (аргументы) {BLOCK}» гораздо более «особенная». Это на самом деле ненужный сахар; это то, что на самом деле делает вещи.
jrockway

2
хорошая ссылка на статью. В нем отмечается, почему кто-то использовал бы такую ​​цитату: «В целях защиты глобального объекта все приложения JavaScript должны быть написаны в самовозглашающейся функции. Это создаст область приложения, в которой можно создавать переменные, не опасаясь их столкновения. с другими приложениями. " А также отметил: «Как только функция завершается, переменные отбрасываются, а глобальный объект остается неизменным».
Yeahdixon

94

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

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

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

6
Спасибо. Ваши примеры были вполне понятны. Я не знал, что скобки в JavaScript могут изменить смысл кода таким образом. Я пришел из Java, поэтому почти каждый день узнаю что-то новое (и часто неожиданное) о JavaScript.
hotshot309

5
Спасибо, что сделали это шаг за шагом, это намного лучше, чем любое другое объяснение, которое я видел. +1
Wk_of_Angmar

2
Главный момент AHA здесь - и спасибо за иллюстрацию с заменой. +100
FredTheWebGuy

1
Одно из лучших объяснений, которые я читал об анонимных функциях. Большое спасибо!
Teknotica

23

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

Как и любое другое значение в 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, чтобы не загрязнять глобальную область видимости.


Хороший пост. Что будет с 'function () {alert ("plop"); } 'когда я это выполнил? Это будет GC'ed?
Палиг

2
Функция () {alert ("plop"); } инструкция просто выделяет функцию, но не выполняет ее и не привязывает к переменной. Поскольку созданная функция не связана с какой-либо переменной, она будет быстро GCed.
Винсент Роберт

Этот поток SO выходит за рамки того, о чем мы здесь говорим, но он объясняет способы разделения пространств имен JavaScript - и включает примеры, которые используют функции, вызывающие себя.
hotshot309

19

Это просто, как работает 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

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

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

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

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

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

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

7

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

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

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

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

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

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


3

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

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"
};

3

Есть еще одно свойство, которое имеет функция JavaScript. Если вы хотите вызвать ту же анонимную функцию рекурсивно.

(function forInternalOnly(){

  //you can use forInternalOnly to call this anonymous function
  /// forInternalOnly can be used inside function only, like
  var result = forInternalOnly();
})();

//this will not work
forInternalOnly();// no such a method exist

2
+1 Добавил небольшой пример, чтобы он был понятнее :-) При первом прочтении мне пришлось перечитать 4 раза.
xanatos

3

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

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

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

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

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

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

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


3

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

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

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

или

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

или

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

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

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


@ Bergi на ie11 deleteработает. даже с 'use strict';. это тоже работает:delete (3 + 4);
Нина Шольц

Упс, моя ошибка. « 2) Если Type (ref) не является Reference, вернуть true. » Он генерирует ошибки только для фактических ссылок, которые неразрешимы.
Берги

1

Это самоисполняющаяся анонимная функция. Первый набор скобок содержит выражения, которые должны быть выполнены, а второй набор скобок выполняет эти выражения.

(function () {
    return ( 10 + 20 );
})();

Питер Мишо обсуждает разницу в « Важной паре скобок» .

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

Видеть:

  1. Закрытие (информатика)
  2. Пространство имен JavaScript
  3. Важная пара скобок Javascript

0

Другая точка зрения

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

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

Тогда вы называете это:

foo ('Few');

Потому что foo = function (msg) {alert (msg);}, поэтому вы можете заменить foo следующим образом:

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

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

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

Таким образом, это легко понять для меня.


0

Когда вы сделали:

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

Вы закончили функцию раньше ('SO')из-за точки с запятой. Если вы просто напишите:

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

Это будет работать.

Рабочий пример: http://jsfiddle.net/oliverni/dbVjg/


0

Простая причина, по которой это не работает, не в том, что ;указывается конец анонимной функции. Это связано с тем, что без ()завершения вызова функции это не вызов функции. Это,

function help() {return true;}

Если вы вызываете result = help();это вызов функции и вернет true.

Если вы звоните, result = help;это не звонок. Это назначение, при котором помощь обрабатывается как данные, которые должны быть присвоены результату.

Вы сделали объявление / создание анонимной функции путем добавления точки с запятой,

(function (msg) { /* Code here */ });

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

('SO');

Интерпретатор видит круглые скобки во второй строке как новую инструкцию / инструкцию, и поэтому он не работает, даже если вы сделали это так:

(function (msg){/*code here*/});('SO');

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

(function (msg){/*code here*/})        // This space is ignored by the interpreter
('SO');

Вывод: вызов функции не является вызовом функции без ()конца, если только при определенных условиях, таких как вызов другой функцией, то есть onload = 'help', будет выполняться функция справки, даже если круглые скобки не были включены. Я полагаю, что setTimeout и setInterval также позволяют этот тип вызова функции, и я также полагаю, что интерпретатор в любом случае добавляет скобки за кулисы, что возвращает нас к «вызову функции не является вызовом функции без скобок».


Я не понимаю, почему это получило так много отрицательных отзывов. Я думаю, что это приемлемый ответ? : /
Даниэль Чунг

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

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

Эта функция вызывается автоматически при компиляции кода.

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

Это также можно записать как:

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

Для более подробной информации, посмотрите на JavaScript / анонимные функции .


Насколько я знаю, JavaScript не «компилируется»
Даниэль Чунг

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

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

    Вот типичный пример именованной функции:

    function flyToTheMoon () {alert ("Zoom! Zoom! Zoom!"); } flyToTheMoon (); Вот тот же пример, созданный как анонимная функция:

    var flyToTheMoon = function () {alert ("Zoom! Zoom! Zoom!"); } flyToTheMoon ();

    Для подробностей, пожалуйста, прочитайте здесь:

    http://helephant.com/2008/08/23/javascript-anonymous-functions/


0

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

var msg = "later dude";
window.onunload = function(msg){
  alert( msg );
};

Вы можете присвоить пространству имен свое msgсвойство, используя шаблон модуля «Выявление», например:

var myScript = (function() {
    var pub = {};
    //myscript.msg
    pub.msg = "later dude";
    window.onunload = function(msg) {
        alert(msg);
    };
    //API
    return pub;
}());

-1

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


Не на 100% правильно. Это анонимная функция , а также и предназначено для повторного использования: var foo = function() {};. Все остальное в порядке, хотя.
Феликс Клинг
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.