Я вижу это все время в javascript-источниках, но я никогда не обнаружил реальной причины, по которой эта конструкция используется. Почему это необходимо?
EDIT: я знаю, что это определение анонимной функции, а затем ее вызов, но почему?
Ответ 1
Это определяет замыкание функции
Это используется для создания закрытия функции с частными функциями и переменными, которые не отображаются глобально.
Рассмотрим следующий код:
(function(){
var test = true;
})();
variable test
не видна нигде, кроме встроенного закрытия функции, где она определена.
Что такое закрытие?
Закрытие функций позволяет различным сценариям не мешать друг другу, даже если они определяют одинаково названные переменные или частные функции. Эти рядовые люди видны и доступны только в самом закрытии, а не вне его.
Проверьте этот код и прочитайте комментарии вместе с ним:
// public part
var publicVar = 111;
var publicFunc = function(value) { alert(value); };
var publicObject = {
// no functions whatsoever
};
// closure part
(function(pubObj){
// private variables and functions
var closureVar = 222;
var closureFunc = function(value){
// call public func
publicFunc(value);
// alert private variable
alert(closureVar);
};
// add function to public object that accesses private functionality
pubObj.alertValues = closureFunc;
// mind the missing "var" which makes it a public variable
anotherPublic = 333;
})(publicObject);
// alert 111 & alert 222
publicObject.alertValues(publicVar);
// try to access varaibles
alert(publicVar); // alert 111
alert(anotherPublic); // alert 333
alert(typeof(closureVar)); // alert "undefined"
Здесь JSFiddle running code, который отображает данные, как указано комментариями в верхнем коде.
Что он на самом деле делает?
Как вы уже знаете это
Плагины jQuery обычно определяются таким образом, определяя функцию с одним параметром, который плагин манипулирует внутри:
(function(paramName){ ... })(jQuery);
Но основная идея остается прежней: определить закрытие функции с частными определениями, которые нельзя напрямую использовать вне нее.
Ответ 2
Эта конструкция известна как самоисполняющаяся анонимная функция, которая на самом деле не очень хорошее имя для нее, вот что происходит (и почему имя не является хорошим). Это:
function abc() {
//stuff
}
Определяет функцию, называемую abc
, если мы хотим анонимную функцию (которая является очень распространенным шаблоном в javascript), это будет нечто вроде следующих:
function() {
//stuff
}
Но, если у вас есть это, вам нужно либо связать его с переменной, чтобы вы могли ее вызвать (что сделало бы ее не-анонимным), или вам нужно немедленно ее выполнить. Мы можем попытаться выполнить его сразу, сделав это:
function() {
//stuff
}();
Но это не сработает, так как это даст вам синтаксическую ошибку. Причина, по которой вы получаете синтаксическую ошибку, выглядит следующим образом. Когда вы создаете функцию с именем (например, abc выше), это имя становится ссылкой на функцию выражение, вы можете затем выполнить выражение, поставив() после имя, например: abc()
. Акт объявления функции не создает выражение, объявление функции представляет собой инструкцию, а не выражение. По сути, выражение является исполняемым, а утверждения не являются (как вы могли догадаться). Поэтому, чтобы выполнить анонимную функцию, вам нужно сказать парсеру, что это выражение, а не оператор. Один способ сделать это (не единственный способ, но стал конвенцией), состоит в том, чтобы обернуть вашу анонимную функцию в набор ()
, и вы получите свою конструкцию:
(function() {
//stuff
})();
Анонимная функция, которая немедленно выполняется (вы можете видеть, как имя конструкции немного не работает, поскольку она не является анонимной функцией, которая выполняется сама по себе, а скорее анонимная функция, которая выполняется сразу).
Итак, почему все это полезно, одна причина заключается в том, что он позволяет вам остановить ваш код от загрязнения глобального пространства имен. Поскольку функции в javascript имеют свою собственную область видимости, любая переменная внутри функции не видна глобально, поэтому, если бы мы могли как-то написать весь наш код внутри функции, глобальная область была бы безопасной, то наша самоисполняющаяся анонимная функция позволяет нам делать именно это, Позвольте мне взять пример из старой книги Джона Ресига:
// 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
})();
Все наши переменные и функции написаны в нашей самоисполняющейся анонимной функции, наш код выполняется в первую очередь потому, что он находится внутри самопроизвольной анонимной функции. И из-за того, что javascript допускает закрытие, то есть, по сути, позволяет функциям обращаться к переменным, которые определены во внешней функции, мы можем в значительной степени написать любой код, который нам нравится, в самоисполняющейся анонимной функции, и все будет работать, как ожидалось.
Но подождите еще больше:). Эта конструкция позволяет решить проблему, которая иногда возникает при использовании закрытий в javascript. Я еще раз позволю Джону Ресигу объяснить, я цитирую:
Помните, что закрытие позволяет вам ссылаться на переменные, которые существуют внутри родительской функции. Однако он не дает значения переменная в момент ее создания; он дает последнее значение переменная в родительской функции. Наиболее распространенная проблема, которые вы увидите, это происходит во время цикла for. Есть один переменную, используемую в качестве итератора (например, i). Внутри цикла for, создаются новые функции, которые используют замыкание для ссылки итератор снова. Проблема в том, что к тому времени, когда новые закрытые вызываются функции, они будут ссылаться на последнее значение итератор (т.е. последняя позиция в массиве), а не значение, которое вы ожидал бы. В листинге 2-16 показан пример использования анонимного функции, чтобы вызвать область действия, создать экземпляр, в котором ожидается возможно закрытие.
// An element with an ID of main
var obj = document.getElementById("main");
// An array of items to bind to
var items = [ "click", "keypress" ];
// Iterate through each of the items
for ( var i = 0; i < items.length; i++ ) {
// Use a self-executed anonymous function to induce scope
(function(){
// Remember the value within this scope
var item = items[i];
// Bind a function to the element
obj[ "on" + item ] = function() {
// item refers to a parent variable that has been successfully
// scoped within the context of this for loop
alert( "Thanks for your " + item );
};
})();
}
По сути, все это означает, что люди часто пишут наивный код javascript, подобный этому (это наивная версия цикла сверху):
for ( var i = 0; i < items.length; i++ ) {
var item = items[i];
// Bind a function to the elment
obj[ "on" + item ] = function() {
alert( "Thanks for your " + items[i] );
};
}
Функции, которые мы создаем в цикле, являются закрытиями, но, к сожалению, они блокируют значение last i
из охватывающей области (в этом случае это, вероятно, будет 2, что вызовет беда). Скорее всего, мы хотим, чтобы каждая функция, которую мы создавали в цикле, блокировала значение я в момент его создания. Именно здесь приходит наша самопроизводящая анонимная функция, вот аналогичный, но, возможно, более простой способ переписать этот цикл:
for ( var i = 0; i < items.length; i++ ) {
(function(index){
obj[ "on" + item ] = function() {
alert( "Thanks for your " + items[index] );
};
})(i);
}
Поскольку мы вызываем нашу анонимную функцию на каждой итерации, параметр, который мы передаем, блокируется значением, которое было в момент его передачи, поэтому все функции, которые мы создаем в цикле, будут работать как ожидалось.
Вот вы, две хорошие причины, чтобы использовать самопроизвольную анонимную конструкцию функции и почему она на самом деле работает в первую очередь.