One interesting concept in JavaScript is lexical scoping, where scopes determine the visibility of variables within a specific context.
- This means that scopes not only define which variables are visible but also determine which variables can be safely garbage collected when they are no longer needed.
- Each time the keyword
function
is used, a new scope is created.
- Scopes can be nested because functions themselves can be nested.
- When a function is called, it can access all variables in every scope that was available when the function was created.
For example:
// +- global scope
// |
function setHandler() { // |+- scope for setHandler()
// || sees: own, global
var elem = document.getElementById('id'); // ||
// ||
elem.onclick = function() { // ||+- Scope for anonymous function
// ... // ||| sees: own, setHandler, global
} // |||
} // ||
// |
In this scenario, the anonymous function assigned to onclick
has access to the variables elem
, setHandler
, and everything from the global scope.
If an element is removed from the DOM, the garbage collector identifies which variables have gone out of scope. However, in cases where there are circular references like the one between elem
and the click handler function, the memory might not be properly released by older browsers.
Due to the persistent nature of scopes, the "lexical environment" of setHandler()
persists even after the function has finished executing, thanks to closures - a fundamental aspect of JavaScript.
While modern garbage collectors can handle these situations efficiently, outdated versions of Internet Explorer were known to struggle with memory leaks caused by such circular references.