В последнее время я много читал Javascript и заметил, что весь файл упакован, как показано ниже, в импортируемые файлы .js.
(function() {
...
code
...
})();
В чем причина этого, а не простой набор функций конструктора?
В последнее время я много читал Javascript и заметил, что весь файл упакован, как показано ниже, в импортируемые файлы .js.
(function() {
...
code
...
})();
В чем причина этого, а не простой набор функций конструктора?
Ответы:
Обычно это пространство имен (см. Далее) и управление видимостью функций-членов и / или переменных. Думайте об этом как об определении объекта. Техническое название для него - выражение немедленного вызова функции (IIFE). Плагины jQuery обычно пишутся так.
В Javascript вы можете вкладывать функции. Итак, законно следующее:
function outerFunction() {
function innerFunction() {
// code
}
}
Теперь вы можете звонить outerFunction()
, но видимость innerFunction()
ограничена рамками outerFunction()
, то есть частной outerFunction()
. Он в основном следует тому же принципу, что и переменные в Javascript:
var globalVariable;
function someFunction() {
var localVariable;
}
Соответственно:
function globalFunction() {
var localFunction1 = function() {
//I'm anonymous! But localFunction1 is a reference to me!
};
function localFunction2() {
//I'm named!
}
}
В приведенном выше сценарии вы можете звонить globalFunction()
откуда угодно, но не можете звонить localFunction1
или localFunction2
.
Когда вы пишете (function() { ... })()
, вы делаете код внутри первого набора скобок литералом функции (т.е. весь «объект» на самом деле является функцией). После этого вы вызываете функцию (финал ()
), которую вы только что определили. Итак, основное преимущество этого, как я уже упоминал ранее, заключается в том, что вы можете иметь частные методы / функции и свойства:
(function() {
var private_var;
function private_function() {
//code
}
})();
В первом примере вы явно вызываете globalFunction
по имени для его запуска. То есть вы бы просто globalFunction()
запустили его. Но в приведенном выше примере вы не просто определяете функцию; Вы определяете и вызываете это за один раз. Это означает, что когда ваш файл JavaScript загружен, он сразу же исполняется. Конечно, вы можете сделать:
function globalFunction() {
// code
}
globalFunction();
Поведение во многом будет таким же, за исключением одного существенного различия: вы избегаете загрязнения глобальной области при использовании IIFE (как следствие, это также означает, что вы не можете вызывать функцию несколько раз, так как у нее нет имени, но так как эта функция должна выполняться только тогда, когда она действительно не является проблемой).
Отличная вещь с IIFE заключается в том, что вы также можете определять вещи внутри и выставлять только те части, которые вы хотите, внешнему миру (пример пространства имен, так что вы можете создать свою собственную библиотеку / плагин):
var myPlugin = (function() {
var private_var;
function private_function() {
}
return {
public_function1: function() {
},
public_function2: function() {
}
}
})()
Теперь вы можете позвонить myPlugin.public_function1()
, но вы не можете получить доступ private_function()
! Очень похоже на определение класса. Чтобы лучше это понять, я рекомендую следующие ссылки для дальнейшего чтения:
РЕДАКТИРОВАТЬ
Я забыл упомянуть. В этом финале ()
вы можете передать все, что хотите внутри. Например, когда вы создаете плагины jQuery, вы передаете jQuery
или $
вот так:
(function(jQ) { ... code ... })(jQuery)
Итак, что вы делаете здесь - это определение функции, которая принимает один параметр (вызываемый jQ
, локальная переменная и известный только этой функции). Затем вы вызываете функцию самостоятельно и передаете параметр (также вызываемый jQuery
, но этот - из внешнего мира и является ссылкой на сам jQuery). В этом нет необходимости, но есть некоторые преимущества:
Ранее я описывал, как эти функции запускаются автоматически при запуске, но если они запускаются автоматически, кто передает аргументы? Этот метод предполагает, что все необходимые параметры уже определены как глобальные переменные. Так что, если бы jQuery еще не был определен как глобальная переменная, этот пример не работал бы. Как вы можете догадаться, во время своей инициализации jquery.js определяет глобальную переменную 'jQuery', а также ее более известную глобальную переменную '$', которая позволяет этому коду работать после включения jQuery.
;(function(jQ) { ... code ... })(jQuery);
таким образом, если кто-то пропустил точку с запятой в своем сценарии, это не сломало бы ваш, особенно если вы планируете минимизировать и объединить свой сценарий с другим.
(function (context) { ..... })(this)
что затем позволит вам присоединить все, что вам нравится, к родительскому контексту, тем самым раскрывая его.
В своей простейшей форме этот метод стремится заключить код в область действия функции .
Это помогает уменьшить шансы на:
Она не определяет , когда документ будет готов - это не какое - то document.onload
ниwindow.onload
Это обычно известно как Immediately Invoked Function Expression (IIFE)
илиSelf Executing Anonymous Function
.
var someFunction = function(){ console.log('wagwan!'); };
(function() { /* function scope starts here */
console.log('start of IIFE');
var myNumber = 4; /* number variable declaration */
var myFunction = function(){ /* function variable declaration */
console.log('formidable!');
};
var myObject = { /* object variable declaration */
anotherNumber : 1001,
anotherFunc : function(){ console.log('formidable!'); }
};
console.log('end of IIFE');
})(); /* function scope ends */
someFunction(); // reachable, hence works: see in the console
myFunction(); // unreachable, will throw an error, see in the console
myObject.anotherFunc(); // unreachable, will throw an error, see in the console
В приведенном выше примере любая переменная, определенная в функции (т. var
Е. Объявленная с использованием ), будет «закрытой» и будет доступна ТОЛЬКО в области действия функции (как говорит Вивин Палиат). Другими словами, эти переменные не видны / недоступны вне функции. Смотрите живую демонстрацию .
Javascript имеет функцию определения области видимости. «Параметры и переменные, определенные в функции, не видны вне функции, и что переменная, определенная где-либо внутри функции, видна везде внутри функции». (из "Javascript: хорошие части").
В конце код, размещенный ранее, также можно сделать следующим образом:
var someFunction = function(){ console.log('wagwan!'); };
var myMainFunction = function() {
console.log('start of IIFE');
var myNumber = 4;
var myFunction = function(){ console.log('formidable!'); };
var myObject = {
anotherNumber : 1001,
anotherFunc : function(){ console.log('formidable!'); }
};
console.log('end of IIFE');
};
myMainFunction(); // I CALL "myMainFunction" FUNCTION HERE
someFunction(); // reachable, hence works: see in the console
myFunction(); // unreachable, will throw an error, see in the console
myObject.anotherFunc(); // unreachable, will throw an error, see in the console
Однажды кто-то, вероятно, подумал: «Должен быть способ избежать именования« myMainFunction », поскольку все, что нам нужно, - это выполнить его немедленно».
Если вы вернетесь к основам, вы обнаружите, что:
expression
: что-то, оценивающее значение. т.е.3+11/x
statement
: строка (и) кода, делающая что-то, НО это не оценивает к значению. т.е.if(){}
Аналогично, функциональные выражения оцениваются как значения. И одно из следствий (я полагаю?) Заключается в том, что они могут быть немедленно вызваны:
var italianSayinSomething = function(){ console.log('mamamia!'); }();
Таким образом, наш более сложный пример становится:
var someFunction = function(){ console.log('wagwan!'); };
var myMainFunction = function() {
console.log('start of IIFE');
var myNumber = 4;
var myFunction = function(){ console.log('formidable!'); };
var myObject = {
anotherNumber : 1001,
anotherFunc : function(){ console.log('formidable!'); }
};
console.log('end of IIFE');
}();
someFunction(); // reachable, hence works: see in the console
myFunction(); // unreachable, will throw an error, see in the console
myObject.anotherFunc(); // unreachable, will throw an error, see in the console
Следующим шагом является мысль «почему есть var myMainFunction =
если мы даже не используем это !?».
Ответ прост: попробуйте удалить это, например, ниже:
function(){ console.log('mamamia!'); }();
Это не будет работать, потому что «объявления функций не могут быть вызваны» .
Хитрость заключается в том, что путем удаления var myMainFunction =
мы преобразовали выражение функции в объявлении функции . Смотрите ссылки в разделе «Ресурсы» для более подробной информации об этом.
Следующий вопрос: «Почему я не могу сохранить это как выражение функции с чем-то другим var myMainFunction =
?
Ответ: «Вы можете», и на самом деле есть много способов сделать это: добавив a +
, a !
, a -
или, возможно, заключив в скобки пару (как это теперь делается по соглашению), и многое другое, я верю. Как пример:
(function(){ console.log('mamamia!'); })(); // live demo: jsbin.com/zokuwodoco/1/edit?js,console.
или
+function(){ console.log('mamamia!'); }(); // live demo: jsbin.com/wuwipiyazi/1/edit?js,console
или
-function(){ console.log('mamamia!'); }(); // live demo: jsbin.com/wejupaheva/1/edit?js,console
Поэтому, когда соответствующая модификация добавлена к тому, что когда-то было нашим «Альтернативным кодом», мы возвращаемся к тому же коду, который использовался в примере «Код объяснен»
var someFunction = function(){ console.log('wagwan!'); };
(function() {
console.log('start of IIFE');
var myNumber = 4;
var myFunction = function(){ console.log('formidable!'); };
var myObject = {
anotherNumber : 1001,
anotherFunc : function(){ console.log('formidable!'); }
};
console.log('end of IIFE');
})();
someFunction(); // reachable, hence works: see in the console
myFunction(); // unreachable, will throw an error, see in the console
myObject.anotherFunc(); // unreachable, will throw an error, see in the console
Узнайте больше о Expressions vs Statements
:
Одна вещь, которая может задаться вопросом: «что происходит, когда вы НЕ определяете переменную« должным образом »внутри функции - т.е. вместо этого делаете простое присваивание?»
(function() {
var myNumber = 4; /* number variable declaration */
var myFunction = function(){ /* function variable declaration */
console.log('formidable!');
};
var myObject = { /* object variable declaration */
anotherNumber : 1001,
anotherFunc : function(){ console.log('formidable!'); }
};
myOtherFunction = function(){ /* oops, an assignment instead of a declaration */
console.log('haha. got ya!');
};
})();
myOtherFunction(); // reachable, hence works: see in the console
window.myOtherFunction(); // works in the browser, myOtherFunction is then in the global scope
myFunction(); // unreachable, will throw an error, see in the console
По сути, если переменной, которая не была объявлена в ее текущей области действия, присваивается значение, то «выполняется поиск цепочки области действия до тех пор, пока она не найдет переменную или не достигнет глобальной области (в которой она будет создана).
В среде браузера (по сравнению с серверной средой, такой как nodejs) глобальная область определяется window
объектом. Следовательно, мы можем сделать window.myOtherFunction()
.
Мой совет «Хорошей практики» по этой теме - всегда использовать var
при определении чего-либо : будь то число, объект или функция и даже в глобальной области видимости. Это делает код намного проще.
Замечания:
block scope
(Обновление: локальные переменные области видимости добавлены в ES6 .)function scope
& global scope
( window
область видимости в среде браузера)Узнайте больше о Javascript Scopes
:
Как только вы получите эту IIFE
концепцию, она приведет к тому module pattern
, что обычно делается путем использования этого шаблона IIFE. Радоваться, веселиться :)
Javascript в браузере действительно имеет только несколько эффективных областей действия: область действия функции и глобальная область действия.
Если переменная не находится в области видимости функции, она находится в глобальной области видимости. А глобальные переменные, как правило, плохие, так что это конструкция для хранения переменных библиотеки при себе.
Это называется закрытием. Он в основном запечатывает код внутри функции, чтобы другие библиотеки не мешали ему. Это похоже на создание пространства имен в скомпилированных языках.
Пример. Предположим, я пишу:
(function() {
var x = 2;
// do stuff with x
})();
Теперь другие библиотеки не могут получить доступ к переменной, которую x
я создал для использования в моей библиотеке.
(function(){ ... return { publicProp1: 'blah' }; })();
. Очевидно, что не совсем параллельно пространству имен, но это может помочь думать об этом таким образом.
Вы также можете использовать замыкания функций в качестве данных в больших выражениях, как в этом методе определения поддержки браузером некоторых объектов html5.
navigator.html5={
canvas: (function(){
var dc= document.createElement('canvas');
if(!dc.getContext) return 0;
var c= dc.getContext('2d');
return typeof c.fillText== 'function'? 2: 1;
})(),
localStorage: (function(){
return !!window.localStorage;
})(),
webworkers: (function(){
return !!window.Worker;
})(),
offline: (function(){
return !!window.applicationCache;
})()
}
Помимо сохранения локальных переменных, очень удобно использовать при написании библиотеки с использованием глобальной переменной, вы можете дать ей более короткое имя переменной для использования в библиотеке. Он часто используется при написании плагинов jQuery, поскольку jQuery позволяет отключить переменную $, указывающую на jQuery, с помощью jQuery.noConflict (). Если он отключен, ваш код все еще может использовать $ и не прерываться, если вы просто сделаете:
(function($) { ...code...})(jQuery);
Мы также должны использовать «использовать строгий» в функции объема, чтобы убедиться, что код должен выполняться в «строгом режиме». Пример кода показан ниже
(function() {
'use strict';
//Your code from here
})();