Keeping global JavaScript scope tidy
Keeping the global window namespace clean is crucial to prevent name conflicts and clearly distinguish official API from internal implementation details that may change.
JavaScript Scope Basics (for PHP Developers)
Unlike PHP, where functions and classes cannot be re-declared, JavaScript allows them to be overwritten (a feature). In JavaScript, nested scopes can access all constants, variables, and functions from their outer scopes. Conversely, outer scopes cannot access elements declared within a nested scope.
When do we need to declare functions?
Code often needs to be added to a page for tasks like initializing complex Alpine.js components, writing event subscriber callbacks, or providing generic functionality. Does all this code require global scope? No. As often in software development, "it depends".
- Alpine.js initialization functions: Must be global as they are referenced by name.
- Event subscribers: Can be private.
- Hyvä functionality for other code: Relies on global scope (unless called via custom events).
For this document, 'global scope' refers to universally accessible elements, while 'private scope' refers to hidden elements. Some details are omitted for clarity.
Making code private.
To keep code out of the global scope, declare it within a function scope.
function () {
// a private function
function thisIsNotInGlobalScope() {
// some code
}
// a private constant
const thisConstantIsPrivate;
// a private variable
let thisVariableIsPrivate;
}
// none of the three is visible here outside of the function scope
The anonymous function above isn't called. Naming it would place it in the global scope. The solution is an IIFE.
IIFE
IIFE stands for Immediately Invoked Function Expression. Use it by wrapping your code in parentheses and immediately invoking it: (function() { /* code */ })(). This immediately executes the anonymous function without polluting the global scope.
IIFE Example:
<script>
(
function() {
// inside of IIFE
const bar = 123;
console.log('in IIFE', bar);
}
)();
console.log('outside of IIFE', bar);
</script>
Loading a page with the above script yields:
This allows executing functions without global scope names or requiring an event callback. An IIFE can also be combined with event observers, allowing event callback logic to remain private.
<script>
(() => {
function myEventCallback() {
// the code
}
window.addEventListener('DOMContentLoaded', myEventCallback);
})();
</script>
Since myEventCallback is declared within the IIFE, it remains private.
Event Callbacks
For event subscribers, code can be placed directly within the callback function:
<script>
window.addEventListener('DOMContentLoaded', function (event) {
// my code here
});
</script>
This works for short code. For larger logic, splitting it into multiple functions within an IIFE improves manageability.