Understanding JavaScript Closures: Scope Chain and Emulating Private Methods wit...
source link: https://hackernoon.com/understanding-javascript-closures-scope-chain-and-emulating-private-methods-with-closures
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Understanding JavaScript Closures: Scope Chain and Emulating Private Methods with Closures
Queen Nnakwue is a software engineer et technical writer based in Lagos, Nigeria.
Quick Summary
Closures are one of the most asked interview questions in JavaScript and a lot of people who have worked with JavaScript for a while also don’t understand closures. So, knowing closure is very important as a JavaScript developer. To prepare you for your next big interview, I have put together this curated article, dissected to help broaden your horizons and understanding of JavaScript closures in depth.
Introduction
Understanding closures and how they work can reveal so much about how we work with JavaScript and other programming languages like C# and Python since they all rely on this concept. In JavaScript, closures are not a new construct, but they are a concept that is used to describe what is possible within JavaScript and, understanding it opens up possibilities for you that you may not have thought of in the past. Also, it helps you understand the JavaScript code that is being written out there.
This indeed explains why so much emphasis is placed on the need to understand closures. In this article, we will be introducing JavaScript Closures from the ground up. We will start by looking at closures from a high level - what are they, how they work, how to use them in our code, and how they are structured. We will also take a look at the scope chain and its use cases. Additionally, we will explore how to emulate private methods with closures. A basic understanding of the JavaScript function scope and familiarity with the lexical environment is necessary to follow through with this tutorial. On the flip side, we will also get to cover these basics, so readers of all background levels can follow suit.
What Is Closure?
A Closure is a function (an inner function) that has access to another function (an outside function) outside of its scope. It is a function combination created by calling a function inside another function.
Quoting Kyle Simpson, Closure is when a function is able to remember and access its lexical scope even when that function is executed outside its lexical scope. One common feature peculiar to these definitions is that a code can execute outside of scope and yet still have access to "that scope".
Closure is a widely used concept not only in JavaScript but also in Nodes’ single-threaded, event-driven, and non-blocking architecture.
Let’s take a look at a basic example:
function greeting(){
let message = "Hello World";
function displayMessage(){
alert(message);
}
return displayMessage
}
let newMessage = greeting();
newMessage();
In the above example, greeting() creates a local variable called message and an inner function called displayMessage(), defined inside greeting() and is available only within the scope of the greeting() function. But, it will interest you to note that displayMessage() can access the variable message declared in its parent function, greeting().
Let’s try this in real-time. Go to your dev tools in your browser and run the above lines of code. Notice that “Hello World” is passed to alert? The reason isn’t far-fetched. Recall that we mentioned at the beginning that closures are a combination of functions that have access to each other within the same lexical environment. Following that definition, newMessage is a reference to the instance of the function displayMessage that is created when the greeting() function is run.
Note: The instance of displayMessage keeps a reference to its lexical environment, within which the variable message exists.
For this reason, when newMessage is invoked, the variable message remains available for use, and "Hello World" is passed to the alert.
Using closures in your code
To reiterate further, one of the most important features of closures is that the inner function still has access to and is able to use the outer function’s variables even after the outer function has run or returned.
In JavaScript, functions execute using the same scope chain that was in place when they were created. What this means is that the inner function has access to the outer function’s variables even after the outer function has returned.
In this section, we will look at three different ways we can implement closures in our program/code.
Example 1: In this first example, we will assign a var me the value James and declare a function called greetMe which console logs (‘Hello, ‘ + me + ‘!’) and then we call the greetMe function like so:
var me = 'James';
function greetMe() {
console.log('Hello, ' + me + '!');
}
greetMe(); //This outputs 'Hello, James!' when called
Notice in line 5 that we did not pass any arguments in our greetMe function. That is, we called our function without any argument. What this means is that we are referring to me in line 1 that is assigned outside of the function scope. The reason that we can do this is that JavaScript functions like greetMe are closures. We cannot do this in languages that do not have support for closures. For languages without support for closures, we would pass the name James as an argument to the function greetMe in line 5. This is because such a language does not have access to the outer scope of itself.
I would like to stress here that greetMe does have access to the outer variable scope. It does not snapshot the value of me at the time it is declared. For instance, if I reassigned me to be Matt , what gets outputted will be: Hello, Matt! like so:
var me = 'James';
function greetMe() {
console.log('Hello, ' + me + '!');
}
me = 'Matt';
greetMe(); //This outputs 'Hello, Matt!' when called
What this tells you is that greetMe does not copy the value of me, instead it reads whatever the value of me is at that time from the outer scope, and it will do this even if greetMe was an asynchronous function being called from an AJAX callback or something like that. The function greetMe will remember this outer context that it declares even if greetMe is called from a completely different part of the application or a different module, it will still refer to this specific context in line 1.
Example 2:
In this second example, we will define an inner function, firstName (outer function) and another function lastName(a closure) inside our firstName function and show you how our lastName function (inner function) still has access to our outer function even after our outer function, firstName has returned.
Let me show you what I mean:
function firstName (firstName) {
var Intro = "My name is ";
// this is an inner function which as we will get to see, has access to the outer function's variables
//this function, lastName is a closure because it is a function inside another function)
function lastName (lastName) {
return Intro + firstName + " " + lastName;
}
return lastName;
}
var fullName = firstName("Queen");
//The firstName outer function has returned which we passed to this var fullName
fullName ("Nnakwue"); // My name is Queen Nnakwue
// Here, the closure (lastName) is called after the outer function has returned above
// Yet, the closure still has access to the outer function's //variables and parameter
Example 3:
In this example, we will see how we can use closure to define a function (an inner function) which we will call n inside another function, which we will call m (an outer function) and expose n
function multiply (m) {
return function (n) {
return m * n
};
}
var multiply9 = multiply(9)
var multiply5 = multiply(5)
console.log (multiply9(8)); // 72
console.log (multiply5(12)); // 60
In the code snippet above, we defined a multiply(m) function, which takes a single argument, m, and returns a new function, n. The function it returns, in turn, takes a single argument, n, and returns m * n. multiply is used to create two new closures (functions) — multiply9 and multiply5 which although share the same function body definition, have different lexical scopes. For instance, In the lexical scope of multiply9, m is 9 while in multiply5, m is 5.
Closure is one of those things in JavaScript that permeates the entire language. It is all around you in JavaScript. You have to use it and embrace it. There are more use cases for closures than the ones I have shown you; more than would fit into this article. Closure is one of those things that you have to use a lot, on your own and in your code, for you to internalize it. However, MDN, has good JavaScript documentation alongside a super good page on closures on their site. You should check out some of the examples there and start using closures in your code to get a feel for them.
Closures in Callbacks
One very common use case where closures are essential is in callbacks. JavaScript has a feature called callback where you can send a function to another function and have that function execute whatever code you have in your function.
As you might already know, JavaScript execution is single-threaded. That is, whenever you have a page open in a browser, there is one thread assigned to execute the code that executes on that page, so you don’t really have the option of creating multiple threads. Because it’s single-threaded, the JavaScript language doesn’t have features like wait or pause, but instead, what you have is the setTimeOut feature.
Let’s take a look at a setTimeOut example. So let’s say I want to have like var b = 20 and I want to wait for one second and then console log b like so:
b = 20;
//wait for one second
console.log(b);
Question: how do I make the browser wait for one second since there is no feature like wait or pause in JavaScript? The way to do this is by using a function called setTimeOut(). The setTimeOut() function comes with JavaScript to enable things like having an execution happen after some time. This function takes two parameters: the first is the function that it needs to execute and the second is the time it needs to wait before executing. Let’s go back to our code and add the following lines of code:
b = 20;
//wait for one second
var fn = function(){
// the function we want to execute after 1000 milliseconds console.log(b);
};
setTimeOut(fn, 1000);
From the above snippet, we defined a setTimeOut() function and we passed some arguments to it. The first argument, fn is the function we want it to execute after 1000 milliseconds, which we declared above in line 5 while the second argument, 1000 milliseconds is the time we want it to wait before executing our function. When you console log this on your terminal, you will notice that it waited for a second before outputting 10 on your console.
Again, you might be wondering: What do closures have to do with this? I’ll show you. In line 6, we console log b even when b is nowhere around our setTimeOut function. This is where closures come into play. Let me explain better. When lines 5-7 are executed, it makes a snapshot of all the variables in the scope. So there is var a in the scope which contains a value of 10. It doesn’t matter because it knows this variable here, in line 1, was created at this point. This function object, lines 5-7, then created, knows that there is this variable a in line 1 at this point, in this place in memory and it remembers it in this function itself. What then happens is that when you pass this function a in line 1 at this point, in this place in memory, and it remembers it in this function itself. What then happens is that when you pass this function to setTimeOut, you’re actually also passing information about the scope information and about where this variable a is. This clearly explains how we can use closures in callbacks.
The Module pattern
Another practical use of closures is in what’s called the module pattern in JavaScript. In JavaScript, there is no concept of private and public methods. You can create functions as properties of objects and then those functions are accessible like any other properties and can be accessed by using a reference. For example, let’s create a person object and inside this object, we have properties, firstName, and lastName. Let’s say I also want to create getters and setters for my firstName and lastName properties and return them both, I can do that by using a couple of more properties called functions. For example, getFirstName and getLastName are both going to be functions. Let’s write some code:
var person = {
"firstName" = "John",
"lastName" = "Doe",
"getFirstName" : function(){
return this.firstName;
},
"getLastName" : function(){
return this.lastName;
},
};
To use the getter method from the code above, we can run person.getFirstName() which will return John. Beyond this concept of getter (used to access properties using a function), the concept is also to prevent access to the property itself. In other languages, we can use either private or public methods. Functions/methods created as public are accessible everywhere, while methods/functions created as private are only accessible inside that class. For instance, if we don’t want the two variables in line 2 and 3 to be accessible outside, all we need to do is to create them as private, but this concept is unfortunately not available in JS.
This is one use case where the use of closures actually helps. The good news is that we can use closures to make our variables private using a pattern called the ‘module pattern’. The module pattern basically helps you create private data and public APIs. To use closures in our code using the module pattern, we are going to transform our code so that the variables become private and inaccessible from the outside but have methods which work on the variables. We can achieve this by using scope.
Let me show you how:
function createPerson() {
"firstName" = "John";
"lastName" = "Doe";
var returnObj = {
"getFirstName" : function(){
return firstName;
},
"getLastName" : function(){
return lastName;
},
"setFirstName" : function(name){
firstName = name;
},
"setLastName" : function(name){
lastName = name;
};
return returnObj
};
var =createPerson();
console.log(person.getFirstName());
person.setFirstName("Jane"));
console.log(person.getFirstName());// this outputs 'Jane' on the console
Here, we prevented the access of variables firstName and lastName anywhere else by enclosing them in a scope. First, we created a function called createPerson, then we created our variables firstName and lastName(both closures) inside our createPerson scope. These two properties are only accessible by these two functions, getFirstName and getLastName that were created. All thanks to closures. However, the object itself, returnObj, does not remember the two variables and so does not have access to them. Following that, we created setFirstName and setLastName to a function and then returned both first and last names because both setters were created with the scope.
Please note that all four functions works on the closure variables firstName and lastName. On line 23, when you run that console.log, you get ‘Jane’ printed out on your console.
Here, we removed the property-firstName and lastName from the object because there’s no way we can make a property private in JavaScript other than using the module pattern. Using the module pattern, we moved both closure variables-firstName and lastName, so that the functions remember them and using the function is the only way to access those variables.
Why do we need closures?
Closures are very important as they can control what is and isn’t in the scope of a particular function. They also control which variables are shared between siblings functions in the same lexical scope. Additionally, they also help simplify our code and make our lives as programmers easier.
One of the most important reasons why we need closures is that they help with ‘Data Hiding and Encapsulation’. With Closure, we can store data in a separate scope and share it only when we deem fit. Closure also helps prevent leaking or exposing data where it is not needed. When we use closures for data encapsulation, the enclosed variables are only within the scope of the containing (outer) function. What this means is that we can’t access the data from an outside environment except through the methods defined in the object.
To demonstrate, consider the following example code:
(function () {
var p; p = 0;
function bar() {
p += 1;
}
Count.prototype = {
CountInc: function () {
return p;
}
};
window.Count = Count;}());
From the example above, the p variable is accessed by the Count constructor and the CountInc method. p's value is still retained even after the Immediately Invoked Function Expression (IIFE) method is executed and exits the constructor and the method. IIFE are functions that run as soon as they are defined. This is a very popular use case that we will get to see in practice and real world use cases without even knowing it’s closure in place.
Additionally, closures are also useful for associating data with a function that operates on that data. This is similar to object-oriented programming, where an object allows us to effectively associate the same object's properties with one or more methods.
The code is attached as a single function that is executed in response to the event.
Understanding the scope chain
In JavaScript, every closure has three scopes, namely:
- Global
- Function scope/local scope
- Block
Now let's explore each of them below.
Global Scope:
When a variable defined is not inside a function or a pair of curly braces, { } then we can say that such a variable exists in the global scope and therefore can be accessed from anywhere in the program.
For example:
var greeting = 'Welcome Home';
//greeting is a global variable that can be accessed from anywhere
function greet() {
console.log(Welcome Home);
}
// Prints 'Welcome Home
greet(); //We get "Welcome Home" printed on the console because the greet function could access the variable.
Function/local scope:
When variables are declared inside a function, they can only be accessed from within that function. That means they can’t be accessed from outside the function scope.
For example,
function greet() {
var greeting = 'Welcome Home';
console.log(greeting);
}
// Prints 'Welcome Home'
greet(); //try calling the function outside of its scope==>Uncaught ReferenceError: greeting is not definedconsole.log(greeting);
Block Scope:
With ECMAScript 2015 (or ES6), let and const keywords, variables can only be scoped to the nearest pair of curly braces. Unlike var variables, they can’t be accessed from outside that pair of curly braces.
For example,
{ let greeting = 'Welcome Home';
var course = 'JavaScript';
console.log(greeting); // Prints 'Welcome Home'
}
// Prints 'JavaScript'
console.log(course);
// Uncaught ReferenceError: greeting is not defined
console.log(greeting);
Emulating private methods with closures
One way to use a closure is to emulate private methods. Private methods can only be called by other methods in the same class. These methods/functions in JavaScript are accessible within their scope. These variables also allow the functions defined within their scope to have total control over how they are manipulated. JavaScript does not give us the luxury of specifying whether variables and methods associated with objects should be public or private in their declaration, but it does give us a way to implement them, as seen below:
var counter = (function() {
var privateCounter = 0;
function changeBy(val) {
privateCounter += val;
}
return {
increment: function() {
changeBy(1);
},
decrement: function() {
changeBy(-1);
},
value: function() {
return privateCounter;
}
};
})();
console.log(counter.value()); // 0.
counter.increment();
counter.increment();
console.log(counter.value()); // 2.
counter.decrement();
console.log(counter.value()); // 1.
Source: MDN
Here, though, there is a single lexical environment- counter that is shared by the three functions: counter.increment, counter.decrement, and counter.value. The lexical environment contains two private items: a variable called privateCounter, and a function called changeBy. These private members cannot be accessed from outside the anonymous function, counter. Instead, you can access them using the three public functions that are returned from the anonymous wrapper. The three public functions are closures that share the same lexical environment and can each access the privateCounter variable and the change By function through scoping.
In summary,
- Whenever you have a function inside another function, then, you have a closure.
- Closures, whether inner functions or functions returned by another function, have a reference to their lexical environment during the time of their creation.
- You can use closures to create private data, whether it’s a private method or a private variable.
- Every closure has three scopes: global, local and block scope
Conclusion
In this tutorial, we have looked at closures and how we can use them in our code with real world examples. We also covered why you should use closures alongside the scope chain, and of course, how to emulate private methods with closures.
JavaScript closures are one of those concepts that may seem difficult to understand at first glance, but once you understand them, you will gain a better insight into how they work, and most importantly, the kinds of problems you can use them to solve.
Pull Quotes
Closure is one of those things in JavaScript that permeates the entire language. It is all around you in JavaScript. You just have to use it and embrace it.
Closure is when a function can remember and access its lexical scope even when it’s invoked outside its lexical scope.
Further resources
These are some useful links you can check out if you want to dive deeper into some of the things we discussed above.
You don’t know JS: Scope and Closures by Oreilly
Mozilla Developer Network
Author Bio
Queen is a front-end developer and a tech enthusiast. She’s passionate about learning and sharing what she learns with others.
Author Links
This article was also published here.
Create your free account to unlock your custom reading experience.
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK