Computer Science

Javascript Closures

Javascript closures are functions that have access to variables in their outer scope, even after the outer function has returned. They allow for encapsulation and private variables in Javascript, and are commonly used in event handlers and callbacks. Closures can also be used to create factory functions and currying.

Written by Perlego with AI-assistance

5 Key excerpts on "Javascript Closures"

  • Mastering JavaScript
    Traditionally, closures have been a feature of purely functional programming languages. JavaScript shows its affinity with such functional programming languages by considering closures integral to the core language constructs. Closures are gaining popularity in mainstream JavaScript libraries and advanced production code because they let you simplify complex operations. You will hear experienced JavaScript programmers talking almost reverently about closures—as if they are some magical construct far beyond the reach of the intellect that common men possess. However, this is not so. When you study this concept, you will find closures to be very obvious, almost matter-of-fact. Till you reach closure enlightenment, I suggest you read and reread this chapter, research on the Internet, write code, and read JavaScript libraries to understand how closures behave—but do not give up.
    The first realization that you must have is that closure is everywhere in JavaScript. It is not a hidden special part of the language.
    Before we jump into the nitty-gritty, let's quickly refresh the lexical scope in JavaScript. We discussed in great detail how lexical scope is determined at the function level in JavaScript. Lexical scope essentially determines where and how all identifiers are declared and predicts how they will be looked up during execution.
    In a nutshell, closure is the scope created when a function is declared that allows the function to access and manipulate variables that are external to this function. In other words, closures allow a function to access all the variables, as well as other functions, that are in scope when the function itself is declared.
    Let's look at some example code to understand this definition: var outer = 'I am outer'; //Define a value in global scope function outerFn() { //Declare a a function in global scope console.log(outer); } outerFn(); //prints - I am outer
    Were you expecting something shiny? No, this is really the most ordinary case of a closure. We are declaring a variable in the global scope and declaring a function in the global scope. In the function, we are able to access the variable declared in the global scope—outer . So essentially, the outer scope for the outerFn() function is a closure and always available to outerFn()
  • Secrets of the JavaScript Ninja, Second Edition
    Closures are irrevocably tightly coupled with scopes. Closures are a straightforward side effect of the way scoping rules work in JavaScript. So in this section, we’ll revisit the closure examples from the beginning of the chapter. But this time you’ll take advantage of execution contexts and lexical environments that will enable you to grasp how closures work under the hood.
    5.6.1. Revisiting mimicking private variables with closures
    As you’ve already seen, closures can help us mimic private variables. Now that we have a solid understanding of how scoping rules work in JavaScript, let’s revisit the private variables example. This time, we’ll focus on execution contexts and lexical environments. Just to make things easier, let’s repeat the listing.
    Listing 5.11. Approximate private variables with closures
    Now we’ll analyze the state of the application after the first Ninja object has been created, as shown in figure 5.15 . We can use our knowledge of the intricacies of identifier resolution to better understand how closures come into play in this situation. JavaScript constructors are functions invoked with the keyword new. Therefore, every time we invoke a constructor function, we create a new lexical environment, which keeps track of variables local to the constructor. In this example, a new Ninja environment that keeps track of the feints variable is created.
    Figure 5.15. Private variables are realized as closures that are created by object methods defined in the constructor.
    In addition, whenever a function is created, it keeps a reference to the lexical environment in which it was created (through an internal [[Environment]] property). In this case, within the Ninja constructor function, we create two new functions: getFeints and feint, which get a reference to the Ninja environment, because this is the environment in which they were created.
  • The JavaScript Workshop
    eBook - ePub

    The JavaScript Workshop

    A New, Interactive Approach to Learning JavaScript

    • Joseph Labrecque, Jahred Love, Daniel Rosenbaum, Nick Turner, Gaurav Mehla, Alonzo L. Hosford, Florian Sloot, Philip Kirkbride(Authors)
    • 2019(Publication Date)
    • Packt Publishing
      (Publisher)
    var keyword:
    Figure 13.12: var keyword usage
    We see that using the var keyword gives the same undefined output for both blocks.

    Closures

    A closure is a feature in JavaScript where a function defined inside another function has access to the parent function's variables. A closure has three scope chains:
    • Own scope : Variables defined between its curly brackets
    • Parent function: Properties defined in the parent function
    • Global variables: Properties defined in the global scope
    Let's have a look at an example: function outer(arg) { let count = 1; function inner() { console.log(arg, '=', count++);     } return inner; } var funA = outer('A');  // outer() invoked the first time var funB = outer('B');  // outer() invoked the second time funA(); // function funA called for first time funA(); // function funA called for second time funA(); // function funA called for third time funB(); // function funB called for first time
    We have one main function, which is outer . Then, we have declared a count variable with a value of 1 . We have one more function, inner , which is using and incrementing the value of count . Then, we return the inner function. The output of the preceding code is as follows:
    Figure 13.13: Example of closures
    When we first call the outer('A') function , we are creating a scope for the count variable and the inner function. Then, we return the inner function and save its address in funA() . We did the same with funB() .
    When we called funA() first, it had access to the count variable because it was in its parent function. So, it printed the value of count and updated it by adding 1 . When we called it again, it again accessed the parent scope and got the updated value of count . JavaScript is able to achieve this because of closures.
    In this section, we have learned about a lot of the basic features of JavaScript. We started with prototypes and used them to implement inheritance. Then, we learned about anonymous and named functions and how to use them. We also learned about different types of data scopes in JavaScript. Finally, we learned about hoisting and closures, which are among the most complicated and important features provided by JavaScript.
  • JavaScript: Novice to Ninja
    • Darren Jones(Author)
    • 2017(Publication Date)
    • SitePoint
      (Publisher)
    inner() functions, as we can see when we invoke it:
    closure(); << Outside! Inside!
    This is important as the variable outside should only exist while the outer() function is running. The closure maintains access to this variable, however, even though the outer() has been invoked.
    A closure doesn't just have access to the value of a variable, it can also change the value of the variable long after the function in which it was originally declared has been invoked.

    A Practical Example

    A closure is formed when a function returns another function that then maintains access to any variables created in the original function’s scope. In the following example, two variables, a and b , are created in the scope of the closure() function. This then returns an anonymous arrow function that maintains access to the variables a and b even after the closure() function has been invoked:
    function closure() { const a = 1.8; const b = 32; return c => c * a + b; }
    Now we can create a new function by invoking the closure() function and assigning the return value to a variable called toFahrenheit :
    const toFahrenheit = closure();
    This new function can then be invoked with its own argument, but the values of a and b from the original function are still kept 'alive':
    toFahrenheit(30); << 86

    A Counter Example

    Closures not only have access to variables declared in a parent function's scope, they can also change the value of these variables. This allows us to do things like create a counter() function like the one in the example below:
    function counter(start){ let i = start; return function() { return i++; } }
    This function starts a count using the variable i . It then returns a function that uses a closure that traps and maintains access to the value of i . This function also has the ability to change the value of i , so it increments i by one every time it's invoked. The reference to the variable i
  • Learning TypeScript 2.x
    Closures are one of the most powerful features available at runtime, but they are also one of the most misunderstood. The Mozilla developer network defines closures as follows: "Closures are functions that refer to independent (free) variables. In other words, the function defined in the closure 'remembers' the environment in which it was created". We understand independent (free) variables as variables that persist beyond the lexical scope from which they were created. Let's look at an example:
    function makeArmy() { const shooters = []; for (let i = 0; i < 10; i++) { const shooter = () => { // a shooter is a function console.log(i); // which should display it's number }; shooters.push(shooter); } return shooters; }
    The preceding example is a JavaScript example, not a TypeScript example.
    We have declared a function named makeArmy. Inside the function, we have created an array of functions named shooters. Each function in the shooters array will display a number, the value of which was set from the variable i inside a for statement. We will now invoke the makeArmy function:
    const army = makeArmy();
    The variable army should now contain the array of the function's shooters. However, we will notice a problem if we execute the following piece of code:
    army[0](); // 10 (expected 0) army[5](); // 10 (expected 5)
    The preceding code snippet does not work as expected because we made one of the most common mistakes related to closures. When we declared the shooter function inside the makeArmy function, we created a closure without being aware of it.
    The reason for this is that the functions assigned to shooter are closures; they consist of the function definition and the captured environment from the makeArmy function's scope. We have created ten closures, but all of them the same environment. By the time the shooter functions are executed, the loop has run its course, and the i variable (shared by all the closures) has been left pointing to the last entry (10).
Index pages curate the most relevant extracts from our library of academic textbooks. They’ve been created using an in-house natural language model (NLM), each adding context and meaning to key research topics.