Останнім часом я читав багато 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()
! Так досить схоже на визначення класу. Щоб краще зрозуміти це, я рекомендую наступні посилання для подальшого читання:
EDIT
Я забув згадати. У цьому фіналі ()
ви можете передати все, що завгодно, всередині. Наприклад, коли ви створюєте плагіни 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 =
?
Відповідь "ти можеш", і насправді існує багато способів, як ти це зробиш: додавання а +
, а !
, а -
чи, можливо, загортання в пару круглих дужок (як це зараз робиться за умовами), і багато іншого я вважаю. Наприклад:
(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
})();