Як працює конструкція (function () {}) () і чому люди використовують її?


79

(function() {})()і його двоюрідний брат, який відповідає jQuery (function($) {})(jQuery), весь час з’являється у коді Javascript.

Як працюють ці конструкції і які проблеми вони вирішують?

Приклади оцінені



2
Це не точна копія. Відповіді на інші запитання насправді не говорять, чому хтось подає jQueryяк аргумент.
Георг Шеллі,

1
Я завжди дивувався, навіщо потрібні додаткові дужки. Я вважаю, що це пов’язано з двозначністю між блоками та літералами об’єктів із фігурними дужками. Зверніть увагу, що вони не потрібні, якщо ви щось присвоюєте, наприклад var x = function () {} () чудово працює.
jpsimons

1
Чому це "закрито як не справжнє питання"? Питання чітке та актуальне.
Кріс,

Відповіді:


73

Зі зростанням популярності фреймворків JavaScript, $знак використовувався в багатьох різних випадках. Отже, для полегшення можливих зіткнень ви можете використовувати такі конструкції:

(function ($){
  // Your code using $ here.
})(jQuery);

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


4
Коротка і змістовна відповідь. +1!
pestaa

5
Технічно це вираз функції (! Оголошення). IIFE
Джон Стріклер

Чи можете ви пояснити, що ви маєте на увазі під словами: "... не турбуючись про те, що інші рамки також входять до сфери застосування."?
Redtopia

@Redtopia. Обсяг Javascript визначається на рівні функції. Все, що не є функцією, є глобальним. Подумайте, скільки Javascript включено на веб-сторінку. Кожна змінна поза сферою функцій небезпечна для зіткнення.
Альберто Де Каро,

Дякую за роз'яснення ... мені було незрозуміло, що ви мали на увазі локальну область дії анонімної функції, яку не видно ні з глобальної сфери, ні з області будь-якої іншої функції.
Redtopia,

41

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

var bar = 1; // bar is now part of the global namespace
alert(bar);

(function () {
   var foo = 1; // foo has function scope
   alert(foo); 
   // code to be executed goes here
})();

7
... і нехай вони залишаться "глобальними" щодо вашого коду.
tvanfosson

Тож посилання на Opera у прикладі коду не має значення / неправильно?
Боббі Джек,

1
@Bobby: у цьому блоці можуть бути додаткові змінні / функції, які використовуються лише для Opera.
geowa4

19

1) Він визначає анонімну функцію і виконує її відразу.

2) Зазвичай це робиться, щоб не забруднювати глобальний простір імен небажаним кодом.

3) Вам потрібно виставити з нього деякі методи, все, що оголошено всередині, буде "приватним", наприклад:

MyLib = (function(){
    // other private stuff here
    return {
        init: function(){
        }
    };

})();

Або, як варіант:

MyLib = {};
(function({
    MyLib.foo = function(){
    }
}));

Справа в тому, що існує безліч способів його використання, але результат залишається незмінним.


1. Така функція визначена у відокремленому файлі, як вона виконується? (здається, ніхто не викликає цю функцію, так як функція запускається?) 2. Я не бачив, що функція на верхньому рівні має оператор "return" ... Я читаю dojo.util._doh ._browserRunner.js Дякую!
Пол

1) Як я вже сказав, він виконується сам, останній () каже йому виконати. 2) Це не обов'язково повинно мати повернення, воно також може встановлювати значення для певної властивості. Це був лише приклад, я доповнив відповідь іншою можливістю.
Еван Тримболі,

9

Це просто анонімна функція, яка викликається негайно. Ви можете спочатку створити функцію, а потім викликати її, і ви отримаєте той самий ефект:

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

працює як:

temp = function(){ ... };
temp();

Ви також можете зробити те ж саме з названою функцією:

function temp() { ... }
temp();

Код, який ви називаєте специфічним для jQuery, - це лише той, що означає, що ви використовуєте в ньому об’єкт jQuery. Це просто анонімна функція з параметром, яка викликається негайно.

Ви можете зробити те саме в два етапи, і ви можете зробити це з будь-якими параметрами, які вам подобаються:

temp = function(answer){ ... };
temp(42);

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

У конкретному випадку для jQuery ви використовуєте його в режимі сумісності, де він не оголошує ім'я $ як псевдонім для jQuery. Надсилаючи об'єкт jQuery до закриття та називаючи параметр $, ви все одно можете використовувати той самий синтаксис, що і без режиму сумісності.


Як я коментував відповідь @ spoulson, анонімна функція - це не те саме, що закриття.
Тім Даун

7

Це пояснює тут , що ваша перша конструкція забезпечує простір для змінних.

Змінні масштабуються на рівні функції в javascript. Це відрізняється від того, до чого ви могли б звикнути в такій мові, як C # або Java, де змінні дістаються до блоку. Що це означає, якщо ви оголосите змінну всередині циклу або оператора if, вона буде доступна для всієї функції.

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

(function() {
  var myProperty = "hello world";
  alert(myProperty);
})();
alert(typeof(myProperty)); // undefined

6

Ще однією причиною цього є усунення будь-якої плутанини щодо того, який $оператор фреймворку ви використовуєте. Наприклад, щоб примусити jQuery, ви можете зробити:

;(function($){
   ... your jQuery code here...
})(jQuery);

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


Це більш конкретна варіація вихідного питання, але це все одно гарна інформація! :)
Боббі Джек,

Зовсім не відповідь, але я підтримав її завдяки чудовій ідеї, яку ви мали!
Spidey

5

Іншим використанням цієї конструкції є "захоплення" значень локальних змінних, які будуть використовуватися в закритті. Наприклад:

for (var i = 0; i < 3; i++) {
    $("#button"+i).click(function() {
        alert(i);
    });
}

Наведений вище код змусить усі три кнопки спливати "3". З іншої сторони:

for (var i = 0; i < 3; i++) {
    (function(i) {
        $("#button"+i).click(function() {
            alert(i);
        });
    })(i);
}

Це призведе до появи трьох кнопок "0", "1" і "2", як очікувалося.

Причиною цього є те, що замикання зберігає посилання на свій рамковий стек , що охоплює , який містить поточні значення своїх змінних. Якщо ці змінні змінюються до виконання закриття, тоді закриття буде бачити лише останні значення, а не ті значення, які були на момент створення закриття. Обертаючи створення закриття всередині іншої функції, як у другому прикладі вище, поточне значення змінної iзберігається у кадрі стека анонімної функції.


Невеликий нітпік, але закритий не кадр стека, це навколишні лексичні сфери. Хороші реалізації закриття будуть захоплювати лише символи, які насправді згадуються всередині закриття, деякі реалізації просто фіксують все (Ruby це робить, тому будьте обережні, що у вас біля закриття). Один із способів думати про об’єкт в ООП - це як закриття, де захоплені символи явно визначені (атрибути члена об’єкта).
KayEss

Це правда, але це насправді лише проблема реалізації, і концептуально можна вважати, що фрейм стека - це те, на що посилається закриття. "Об'єкти" в стилі ООП на мовах сімейства Lisp виконуються із закриттям, це потужна техніка.
Greg Hewgill

Так, хоча, якщо ви хочете думати з точки зору стекових кадрів, тоді закриття - це ланцюжок стекових кадрів, по одному кадру для кожної функції, що включає в лексичну область, разом із глобальною сферою. Про лексичну сферу легше продумати (особливо, коли пояснюєте комусь, хто новачок у закритті), оскільки ви не вступаєте в безглузду (і оманливу) дискусію про те, який фрейм стека, коли у вас є кілька рівнів вкладених функцій. Лексичний обсяг - це просто те, що ви бачите у вихідному коді.
KayEss

4

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

var i = 0;
alert("The magic number is " + i);

(function() {
   var i = 99;
   alert("The magic number inside the closure is " + i);
})();

alert("The magic number is still " + i);

Це створить три спливаючі вікна, демонструючи, що iзакриття не змінює вже існуючу змінну з тим же ім'ям:

  • Чарівне число дорівнює 0
  • Чарівне число всередині закриття - 99
  • Чарівне число все ще 0

5
Це не закриття, це просто демонстрація того, що функція має власний обсяг. Закриття формується, коли функція визначена всередині іншої функції і стає доступною поза цією функцією , таким чином зберігаючи свій доступ до змінних, функцій та параметрів у зовнішній функції навіть після повернення цієї зовнішньої функції.
Тім Даун

Просто змініть "(function () {..." на "var func = function () {...}; func ();". Там, закриття.
spoulson

Я не хочу робити це, але ... все одно закриття: внутрішня функція не виконується поза зовнішньою функцією. Простим способом створити закриття у вашому прикладі було б повернення функції, яка попереджала i: var f = (function () {var i = 99; return function () {alert (i);};}) (); f ();
Тім Даун

Точка зазначена. Це зводиться до особливостей реалізації.
spoulson

2

Вони часто використовуються в плагінах jQuery. Як пояснено в Посібнику з розробки плагінів jQuery, усі змінні, декларовані всередині, { }є приватними та не видно зовні, що дозволяє краще інкапсулювати.


3
{}самі по собі не створюють нову сферу в JavaScript. Область дії створюється за допомогою оголошення функції.
Annabelle

2

Як сказали інші, вони обидва визначають анонімні функції, які запускаються негайно. Як правило, я обгортаю свої оголошення JavaScript-класу в цю структуру, щоб створити статичну приватну область для класу. Потім я можу помістити в цю область постійні дані, статичні методи, обробники подій або що-небудь ще, і це буде видно лише екземплярам класу:

// Declare a namespace object.
window.MyLibrary = {};

// Wrap class declaration to create a private static scope.
(function() {
  var incrementingID = 0;

  function somePrivateStaticMethod() {
    // ...
  }

  // Declare the MyObject class under the MyLibrary namespace.
  MyLibrary.MyObject = function() {
    this.id = incrementingID++;
  };

  // ...MyObject's prototype declaration goes here, etc...
  MyLibrary.MyObject.prototype = {
    memberMethod: function() {
      // Do some stuff
      // Maybe call a static private method!
      somePrivateStaticMethod();
    }
  };
})();

У цьому прикладі MyObjectклас присвоюється MyLibraryпростору імен, тому він доступний. incrementingIDі somePrivateStaticMethod()не мають безпосереднього доступу поза межами анонімної функції.


2

Це в основному для простору імен вашого коду JavaScript.

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

()На кінцях коштів для самостійного Invoke. Ви також можете додати аргумент, який стане аргументом вашої анонімної функції. Я часто це роблю з jQuery, і ви можете зрозуміти, чому ...

(function($) {

    // Now I can use $, but it won't affect any other library like Prototype
})(jQuery);

Еван Тримболі висвітлює решту у своїй відповіді .


1

Це функція самовиклику. Начебто скорочення для письма

function DoSomeStuff($)
{
}

DoSomeStuff(jQuery);

1

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

Це хороша практика, і міркування за нею - уникнути забруднення глобального простору імен змінними та функціями, які можуть бути заблоковані іншими частинами Javascript на всьому сайті.

Щоб пояснити, як називається функція, розглянемо простий приклад:

Якщо у вас включений цей єдиний рядок Javascript, він автоматично запуститься без явного виклику:

alert('hello');

Отже, візьміть цю ідею та застосуйте її до цього прикладу:

(function() {
    alert('hello')
    //anything I define in here is scoped to this function only
}) (); //here, the anonymous function is invoked

Кінцевий результат подібний, оскільки анонімна функція викликається так само, як і попередній приклад.


будь ласка, перегляньте моє перше запитання до коментарів до Евана ... Дякую!
Пол

//here, the anonymous function is invoked, просто, але я цього раніше не бачив :)
Стефан

1

Оскільки хороші відповіді на коди вже прийняті :) Я запропоную подивитися кілька відеороликів John Resig, відео 1 , відео 2 (винахідник jQuery & master у JavaScript).

Деякі справді добрі ідеї та відповіді, надані у відео.

Саме цим я випадково займався в той момент, коли побачив ваше запитання.


0
function(){ // some code here }

це спосіб визначити анонімну функцію в javascript. Вони можуть дати вам можливість виконувати функцію в контексті іншої функції (де ви, можливо, не маєте такої можливості).

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.