Is closure a function? What is a nested function? What is a function scope? What is lexical environment and how does it work?
In this article, we’re going to answer all these questions and more – we are going to learn what functions and closures in JavaScript are, and most of all what is the main difference between functions and closures.
Functions – fundamental building blocks in JavaScript
In JavaScript, a function is some sort of a procedure or in other programming languages – a subroutine, which has an obvious relationship between an input and an output. A function is built from a set of statements that perform a task such as calculating a value. A function must be defined in the environment or scope from which you call it – more about that in a bit.
A function statement contains the function keyword and is followed by:
- The name of the function (if there is no name, we call it an anonymous function)
- Function parameters(arguments), which are listed in parentheses and separated by commas, such as (number1, number2)
- The function executes the code, in the example below return number1 * number2; and that is placed inside curly brackets {}
var result = multiply(2, 5); // Function is called, return value will end up in variable result
function multiply(number1, number2) {
// The function returns the multiplication of number1 and number2 and the result will be 10
return number1 * number2;
}
Defining a function (naming it and specifying what to do with it when it’s called) won’t execute that function. Calling the function or invoking the code inside the function will execute the function – function can be invoked or called:
- automatically or self-invoked (aka recursive functions),
- called from the code (for example with a return statement)
- through an event (such as a click of a button).
We mentioned above that functions must be in scope when they are called. However, the function declaration can be listed below the call in the code – this means the function declaration is hoisted. Be aware that function hoisting works only with function declarations and not with function expressions – you can’t use function expressions before you create them.
Let’s talk a bit more about function scope and nested functions
When you declare a function in the global scope, you can also access all variables that are defined in that global scope. However, when you declare a variable inside a function, you cannot access it from outside the function. This means the variable is declared in the function scope. Therefore, the function will be able to access that variable because it is defined in the function’s scope.
And if you define a function inside another function (nested functions), that inner function will also have access to all variables that are declared in its outer function, and even more – the inner function will have access to all variables and arguments to which the outer function has access. However, the outer function cannot use the arguments and variables of the inner function.
Let’s take a look at a few examples through a case of global scope and a nested function
// The variables defined below are declared in the global scope
var number1 = 2,
number2 = 3,
game = 'Chess';
// The function below is also defined in the global scope
function add() {
return number1 + number2;
}
add(); // the function returns 5
// And here is an example of a nested function - it can access a global variable (game)
function getNumberOfFields() {
var rows = 8,
columns = 8;
function multiply() {
return game + ' has ' + (rows * columns) + ' playing fields. ';
}
return multiply();
}
getNumberOfFields(); // Returns "Chess has 64 playing fields."
What is lexical environment?
Before we dive into closures and functions more deeply, we need to understand the concept of lexical environment. JavaScript engine stores the variable that is defined in a specific function in a “place” we call a lexical environment, and every time JavaScript engine creates a context for execution to perform the function, JavaScript engine also creates a new lexical environment. It is important to know that the lexical environment has two components:
- the environment record, which is the actual place where the variable and function declarations are stored
- a reference to the outer environment, which enables access to the outer lexical environment
This second component – a reference to the outer environment that enables access to the outer lexical environment – is crucial in understanding how closures work. Therefore, this is the right time to take a look at closures, how they work and how can we compare them to the (nested) functions.
What are closures?
In plain language, a closure is a function that remembers its outer variables and can access these variables. A closure remembers and accesses variables and arguments of its outer function even after that outer function has returned, even after it’s finished. In some programming languages, this is not possible but in JavaScript, all functions are naturally closures. A closure is created in a lexical environment at every function call, so whenever you’re using the closure, it references that same outer scope. So, in case a variable in that outer scope changes, the change of that variable is visible in the next call as well.
The closure serves as the entrance between the global context and the outer function scope. If the closure does not allow direct access to variables from the outer scope, you won’t be able to access it. In these terms these variables from the outer scope are private – an object with private property is a closure.
function outerFunction () {
var privateVar = 'some private text';
// this is our access to privateVar
function innerFunction () {
return privateVar;
}
return { getPrivateVar: innerFunction };
}
var closure = outerFunction();
console.log(closure.privateVar); // undefined; not able to access privateVar
closure.getPrivateVar(); // 'some private text'
At this point it makes sense to bring the nested function back because later on, we’re going to compare it with a closure – we already explained above what a nested function is – it’s a function (inner) inside another (outer) function. And here comes the connection with a closure: nested function – the inner one – is a closure. The inner function (the nested one) contains the scope of the outer function. The inner functions (the nested functions) share and inherit variables and arguments of the outer function environment. However, the outer function cannot use the arguments and variables of the inner function – the variables of the inner function are confined. Life’s not fair, right?
You should also know that functions can be multiply-nested – function 1 contains function 2, and function 2 contains function 3. In this case, functions 2 and 3 are closures. In the terms of scope – these closures contain multiple scopes aka scope chaining: 2 can access 1, 3 can access 2, and 3 can access 1.
To sum up. JavaScript allows function nesting. For this reason, the inner functions have full access to all the variables and functions that are defined inside the outer function, but the outer function does not have access to the variables and functions defined in the inner function. Consequently, the duration of the outer function execution is shorter compared to the variables and functions defined in the outer function, because the inner function has access to the scope of the outer function.
How are closures different from a nested function?
In JavaScript, all functions work like closures. At a conceptual level, functions and closures are almost the same and the difference is minimal, but let’s take a look at it.
A function represents a block of code that may use variable values from its surrounding scope – functions are traditionally invoked in the scope where they are defined – they are statical. In comparison – closure represents a piece of code that may encapsulate values of variables from their surrounding scope – closures could be called far away from their point of definition. Based on that comparison we could say that functions use parts of their surroundings while closures capture them – as we said in the previous chapter when we defined a closure – a closure remembers and accesses variables and arguments of its outer function even after that outer function has finished.
The inner function has access to the outer function’s scope and if the inner function manages to survive beyond the life of the outer function, all variables and functions that are defined in the outer function will live longer than the duration of the outer function execution. A closure is created when the inner function is made available to any scope outside the outer function. A closure is an expression that can reference variables within its scope, be assigned to a variable, be passed as an argument to a function, or be returned as a function result.