Как работает конструкция (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. Я не видел, чтобы функция на верхнем уровне имела оператор возврата ... Я читаю dojo.util._doh ._browserRunner.js Спасибо!
Пол
1) Как я уже сказал, он выполняется сам, last () сообщает ему о выполнении. 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, даже если у вас загружены другие фреймворки.

tvanfosson
источник
Это более конкретный вариант исходного вопроса, но это все равно хорошая информация! :)
Бобби Джек
Вовсе не ответ, но я повысил его за вашу великолепную идею!
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 создаются с помощью замыканий, это мощная техника.
Грег Хьюгилл,
Да, хотя если вы хотите мыслить в терминах фреймов стека, то закрытие представляет собой цепочку фреймов стека, по одному фрейму для каждой включающей функции в лексической области видимости вместе с глобальной областью видимости. О лексической области легче думать (особенно при объяснении кому-то, кто плохо знаком с замыканиями), потому что вы не попадаете в бессмысленное (и вводящее в заблуждение) обсуждение того, какой фрейм стека, когда у вас есть несколько уровней вложенных функций. Лексическая область видимости - это просто то, что вы видите в исходном коде.
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
Spoulson
источник
5
Это не закрытие, это просто демонстрация того, что функция имеет собственную область видимости. Замыкание формируется, когда функция определяется внутри другой функции и становится доступной вне этой функции , таким образом сохраняя доступ к переменным, функциям и параметрам во внешней функции даже после того, как эта внешняя функция вернулась.
Тим Даун
Просто измените "(function () {..." на "var func = function () {...}; func ();". Вот и завершение.
Споулсон
Я не хочу вдаваться в подробности, но ... до сих пор нет закрытия: нет внутренней функции, выполняемой вне внешней функции. Простой способ создать замыкание в вашем примере - это вернуть функцию, которая предупредила i: var f = (function () {var i = 99; return function () {alert (i);};}) (); f ();
Тим Даун
Пункт отмечен. Все сводится к специфике реализации.
spoulson
2

Они часто используются в плагинах jQuery. Как объясняется в Руководстве по созданию плагинов jQuery, все переменные, объявленные внутри, { }являются частными и не видны снаружи, что обеспечивает лучшую инкапсуляцию.

Дарин Димитров
источник
3
{}сами по себе не создают новую область видимости в JavaScript. Область видимости создается объявлением функции.
Аннабель
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.

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

В ()конце означает самовызов. Вы также можете добавить туда аргумент, который станет аргументом вашей анонимной функции. Я часто делаю это с помощью 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

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

Wsanville
источник
пожалуйста, посмотрите мой первый вопрос в комментариях к Эвану ... Спасибо!
Пол
//here, the anonymous function is invoked, просто, но я этого раньше не видел :)
Стефан
1

Потому что хорошие ответы на код уже приняты :) Я предлагаю посмотреть несколько видеороликов Джона Ресига, видео 1 , видео 2 (изобретатель jQuery и мастер в JavaScript).

Некоторые действительно хорошие идеи и ответы представлены в видео.

Именно этим я и занимался в тот момент, когда увидел ваш вопрос.

Джон К
источник
0
function(){ // some code here }

- это способ определить анонимную функцию в javascript. Они могут дать вам возможность выполнять функцию в контексте другой функции (в противном случае у вас может не быть этой возможности).

Джастин Нисснер
источник