Чому потрібно викликати анонімну функцію в одному рядку?


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

Вище слід попрацювати.

Сторінка DEMO: 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:

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.

Демонстраційна демонстрація

Порівняйте це з

 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
Так, але чому? Чому він повинен бути як вбудований? Незалежно від того, скільки білого простору я буду використовувати.
palig

9
Як я писав, напівкрапка припиняє визначення анонімної функції. Оскільки у нього немає імені (це анонімний дух!), Ви більше не зможете його зателефонувати. Якщо ви не ставите крапку з комою, функція все одно може бути виконана.
SolutionYogi

Я думав, що автоматичне вставлення крапки з комою в цьому випадку поставить крапку з комою, але це не так. Тож ти маєш рацію.
Носредна

1
Носредна, JS поводиться мало довільно, коли йдеться про додавання напівколонок. Прочитайте цю детальну статтю: blog.boyet.com/blog/javascriptlessons/…
SolutionYogi

Так, я бачу, що (функція (msg) {сигнал (повідомлення)}) ("ТАК"); працює. Я просто запитував, чому це працює. Де це вказано або що це за JS. Отже, як тільки я зателефоную: (функція (msg) {сигнал (повідомлення)}) що буде з функцією? Це буде GC'ed?
palig

129

Це називається функцією самовикликання.

Те, що ви робите під час дзвінка (function(){}), повертає об’єкт функції. Коли ви додаєте ()його, він викликається, і все в тілі виконується. ;Чи означає кінець заяви, саме тому другий виклик зазнає невдачі.


Ну добре, я бачу, тож це просто якийсь особливий синтаксис JS, правда? Найбільше подобається це пояснення! Просте і коротке :)
palig

Я думаю, що неправильно говорити про те, що тіло буде «випароване». Він виконує так само, як і будь-яку іншу функцію. Оскільки це анонімно, ви або збережіть посилання десь або АБО виконайте його відразу.
SolutionYogi

16
Особисто мені навіть не подобається термін "функція самозакликання". Це не те, що функція викликає себе. Програміст написав ці дужки, щоб викликати його.
SolutionYogi

Це не "особливий синтаксис" більше, ніж будь-що інше. Насправді форма "ім'я функції (args) {BLOCK}" набагато "особливіша". Це насправді непотрібний цукор; це, однак, те, що насправді змушує щось статися.
jrockway

2
приємне посилання на статтю. Він зазначає, чому хтось використовував би це цитуючи: "Прагнучи захистити глобальний об'єкт, усі програми JavaScript повинні писатися в межах функції самозакликання. Це створить область застосування, в якій можна створювати змінні, не боячись їх зіткнення. з іншими програмами. " А також зауважив: "Після завершення функції змінні відкидаються, а глобальний об'єкт залишається незмінним".
yeahdixon

94

Одне, що мене заплутало, - це те, що "()" групують операторів.

Ось ваша основна заявлена ​​функція.

Вих. 1:

var message = 'SO';

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

foo(message);

Функції є об'єктами, і їх можна групувати. Тож давайте кинемо паролі навколо функції.

Вих. 2:

var message = 'SO';

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

(foo)(message);     // calls foo

Тепер замість того, щоб оголосити та відразу викликати ту саму функцію, ми можемо використовувати базову заміну, щоб оголосити її так, як ми її називаємо.

Вих. 3.

var message = 'SO';

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

Нарешті, у нас немає необхідності в цьому додатковому фоні, тому що ми не використовуємо ім'я для його виклику! Функції можуть бути анонімними.

Вих. 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?
palig

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)();.

Є гарний синтаксис функцій JavaScript для обговорення SO .


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

(це єдине реальне використання пустоти, afaik)

або

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

або

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

працювати також. the 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();це дзвінок до функції і поверне справжній.

Якщо ви телефонуєте 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 / Anonymous .


Наскільки я знаю, JavaScript не "компілюється"
Daniel Cheung

0
  1. Анонімні функції - це функції, які динамічно оголошуються під час виконання. Їх називають анонімними функціями, оскільки їм не присвоюється ім’я так само, як звичайним функціям.

    Анонімні функції оголошуються за допомогою оператора функції замість декларації функції. Ви можете використовувати оператор функції, щоб створити нову функцію там, де це дійсно, щоб поставити вираз. Наприклад, ви можете оголосити нову функцію як параметр виклику функції або призначити властивість іншого об'єкта.

    Ось типовий приклад названої функції:

    функція flyToTheMoon () {сигнал ("Збільшити! Збільшити! Збільшити!"); } flyToTheMoon (); Ось такий же приклад, створений як анонімна функція:

    var flyToTheMoon = function () {alert ("Збільшити! Збільшити! Збільшити!"); } 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

Анонімні функції мають на увазі однократну угоду, коли ви визначаєте функцію на льоту, щоб вона генерувала у вас вихід із вводу, який ви надаєте. За винятком того, що ви не надали дані. Натомість ви щось написали у другому рядку ("ТАК"); - незалежне твердження, яке не має нічого спільного з функцією. Що ви очікували? :)


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