Closures and lexical scoping explained
Posted
Updated
Europe’s developer-focused job platform
Let companies apply to you
Developer-focused, salary and tech stack upfront.
Just one profile, no job applications!
When a function is created in Javascript, a closure is created with it at the same time, but what is a closure?
A closure is a combination of the function bundled together with the references to its surrounding state, or a bit more abstract, think of a closure as an invisible object that stores variables and parameters created in the function.
💰 The Pragmatic Programmer: journey to mastery. 💰 One of the best books in software development, sold over 200,000 times.
Lexical scoping describes how a parser resolves variable names when functions are nested. The word lexical refers to the fact that lexical scoping uses the location where a variable is declared within the source code to determine where that variable is available. Nested functions have access to variables declared in their outer scope.
Let's have a look at an example of lexical scoping:
function init() {
let name = 'Mario'; // name is a local variable created by init
function displayName() {
// displayName() is the inner function, a closure
alert(name); // use variable declared in the parent function
}
displayName();
}
init();
The function displayName()
has no local variable on their own.
It's an inner function and has access to the variables of the outer function.
A closure is the combination of a function, and the lexical environment within which that function was declared. Let's look at some code:
function alertDisplayName() {
let name = 'Mario';
function displayName() {
alert(name);
}
return displayName();
}
const myFunction = alertDisplayName();
myFunction();
Running this code would have the same effect as the init()
function from above.
The displayName()
inner function is returned from the outer function before being executed.
At first this might seem not correct, that the code run successfully. It depends on your programming background, in some languages, local variables within function only exist during the function's execution. In JavaScript functions form closures. A closure is the combination of a function, and the lexical environment within which that function was declared. This lexical environment or static context consists of any local variables that were in-scope at the time the closure was created. Closures are created at the same time functions are created.
In the code example above, myFunction
is a reference to the instance of the function displayName
that is created when alertDisplayName
runs.
The instance of displayName
maintains a reference to its lexical environment, within which the variable name exists. Hence, when myFunction
runs,
the variable name remains available for use, and Mario
is passed to alert
.
Let's look at another piece of code:
function addNumbers(num1) {
return function(num2) {
return num1 + num2;
};
}
const add3 = addNumbers(3);
console.log(add3(3)); // will log 6
The function addNumbers
is in essence a function factory. It creates a function, that can add a specific value to their argument.
The lexical environment stored for the constant add3
would be for num1=3
.
Closures are useful, because they let you associate data (the lexical environment) with a function that operates on that data.
Another example of encapsulating state using closure scope would be to enclose a secret:
unction createSigner (secret) {
const keypair = createKeypair(secret)
return function (content) {
return {
signed: cryptoSign(content, keypair.privateKey),
publicKey: keypair.publicKey
}
}
}
const sign = createSigner('super secret thing');
const signedContent = sign('sign me');
const moreSignedContent = sign('sign me as well');
In the code snippet above createKeypair
and cryptoSign
are purely for outlining the concept of the encapsulation of secrets.
Closure scope can also be used as an alternative to prototypal inheritance, but it doesn't use the prototype chain nor does it rely on the implicit this
keyword.
function animal(name) {
const eat = () => {
console.log(name + ' eats');
};
return { eat };
}
function dog(name) {
name = name + ' the dog';
const bark = () => {
console.log(name + ' barks');
};
return {
...animal(name),
bark,
};
}
const henry = dog('Henry');
henry.eat(); // prints "Henry the dog eats"
henry.bark(); // prints "Henry the dog barks"
The advantage of using closure scope to compose objects is that it eliminates the complexity of prototypes, context (this) and the need to call a function with new (which can have unintended side effects when omitted ). The downside is that where a prototype method is shared between multiple instances, an approach using closure scope requires that internal functions are created per instance. JavaScript's engines use increasingly sophisticated optimization techniques internally, so it's only important to be fast enough for any given use case.
It's recommended to use function composition over prototypal inheritance and optimize at a later point if required.
Thanks for reading and if you have any questions, use the comment function or send me a message @mariokandut.
If you want to know more about Javascript, have a look at these Javascript Tutorials.
Never miss an article.