Что означают круглые скобки, связанные с объявлением объекта/функции/класса?

Я новичок в JavaScript и YUI. В примерах библиотеки YUI вы можете найти много применений этой конструкции:

(function() {
    var Dom = YAHOO.util.Dom,
    Event = YAHOO.util.Event,
    layout = null,
        ...
})();

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

... Но как насчет предыдущего набора круглых скобок, окружающих объявление функции?

Я думаю, что это вопрос масштаба; чтобы скрыть внутренние переменные внешним функциям и возможно глобальным объектам. Это? В более общем плане, какова механика этих круглых скобок?

Ответ 1

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

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

См:

http://en.wikipedia.org/wiki/Closure_%28computer_science%29

http://peter.michaux.ca/articles/javascript-namespacing

Ответ 2

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

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

Использование ключевого слова var очень важно, потому что в JavaScript каждая переменная по умолчанию глобальна, но с ключевым словом вы создаете новую, лексически ограниченную переменную, то есть это видно по коду между две фигурные скобки. В вашем примере вы по существу создаете короткие псевдонимы для объектов в библиотеке YUI, но у них более мощные средства.

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

var add_gen = function(n) {
  return function(x) {
    return n + x;
  };
};
var add2 = add_gen(2);
add2(3); // result is 5

Что здесь происходит? В функции add_gen вы создаете другую функцию, которая просто добавит число n к его аргументу. Фокус в том, что в переменных, определенных в списке параметров функции, действуют как лексически ограниченные переменные, такие как те, которые определены с помощью var.

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

С помощью функциональных параметров, лексически ограниченных, вы можете обойти "проблемы", возникающие из-за использования переменных цикла в анонимных функциях. Возьмем простой пример:

for(var i=0; i<5; i++) {
  setTimeout(function(){alert(i)}, 10);
}

"Ожидаемый" результат может быть числом от нуля до четырех, но вместо этого вы получаете четыре экземпляра пяти. Это происходит из-за того, что анонимная функция в setTimeout и в цикле for использует переменную очень же i, поэтому к моменту вычисления функций я буду равен 5.

Вы можете получить наивно ожидаемый результат, используя технику в своем вопросе и тот факт, что параметры функции лексически ограничены. (Я использовал этот подход в другом ответе)

for(var i=0; i<5; i++) {
  setTimeout(
     (function(j) {
       return function(){alert(j)};
     })(i), 10);
}

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

Я предлагаю вам попытаться понять отличный учебник в http://ejohn.org/apps/learn/, чтобы лучше понять замыкания, вот где я узнал очень-очень.

Ответ 3

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

В частности, это заставляет JavaScript интерпретировать конструкцию функции() {...} 'как выражение встроенной анонимной функции. Если вы опустили скобки:

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

Вы получите синтаксическую ошибку, потому что JS-анализатор увидит ключевое слово 'function' и предположим, что вы начинаете инструкцию функции формы:

function doSomething() {
}

... и вы не можете иметь инструкцию функции без имени функции.

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

Ответ 4

Прыжки, чтобы следить за тем, что сказали Энди Юм и другие:

"()", окружающая анонимную функцию, является "оператором группировки", как определено в разделе 11.1.6 спецификации ECMA: http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf.

Получено дословно из документов:

11.1.6 Оператор группировки

Производное PrimaryExpression: (Выражение) оценивается следующим образом:

  • Возвращает результат оценки Expression. Это может быть тип Reference.

В этом контексте функция рассматривается как выражение.

Ответ 5

Несколько соображений по этому вопросу:

  • Скобка:

    Браузер (движок/парсер) связывает функцию ключевого слова с

    [optional name]([optional parameters]){...code...}
    

    Итак, в выражении типа function() {}() последняя скобка не имеет смысла.

    Теперь подумайте

    name=function(){} ; name() !?
    

Да, первая пара скобок заставляет анонимную функцию превращаться в переменную (хранимое выражение), а вторая запускает оценку/выполнение, поэтому ( функция() {} ) ( ) имеет смысл.

  • Утилита:?

    • Для выполнения некоторого кода при загрузке и изоляции использованных переменных от остальной части страницы, особенно при конфликтах имен,

    • Замените eval ( "string" ) на

      (новая функция ( "строка" ))()

    • Заверните длинный код для оператора =?::

      result = exp_to_test? (function() {... long_code...})(): (function() {...})();

Ответ 6

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

Что касается того, почему это полезно, мне не хватает мастера JavaScript, чтобы иметь какие-либо идеи.: P

Ответ 7

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