Closures and Currying in Javascript

Publikováno: 6.6.2019

What are Closures?

If you write code in Javascript it's quite likely you have come across the term closure, which is a useful yet often confusing concept. But just what is a c...

Celý článek

What are Closures?

If you write code in Javascript it's quite likely you have come across the term closure, which is a useful yet often confusing concept. But just what is a closure?

A closure may be described as a combination of a function and the lexical environment in which it was declared.

But what exactly does this mean? The lexical environment consists of any local variables in the function's scope when the function is created. A closure enables one to refer to all local variables of a function in the state they were found. This is essentially achieved by defining a function inside another function, this function within a function is technically the closure. Each time the parent function is called, a new context of execution is created holding a fresh copy of all local variables. These local variables can be referred to in the global scope by either linking them to variables declared globally or returning the closure from the parent function.

A simple example will take on a format similar to this:

function closuredFunc (){
    function closure(){
    // some logic
    }
}

It is also possible to have a closure that returns several methods as shown below:


function closure(){
    function first() { console.log('I was declared first')}
    function second() { console.log('I was declared second')}
    function first() { console.log('I was declared third')}
    return [first, second, third]
}

To reference each of these methods, we'll assign our closure to a global variable which will then point to an array of exposed methods. Each method can then be assigned to unique variable names to bring them into the global scope as shown below. At this point, they can now be called.

let f = closure()

let one = f[0]
let two = f[1]
let three = f[2]

one() // logs I was declared first
two() // logs I was declared second
three() // logs I was declared third

Why use closures?

You may be wondering why would one go through the trouble of making closures. Well, closures have a number of uses and advantages.

Prior to the introduction of Classes in ES6, closures provided a means of creating class-like privacy similar to that used in Object Oriented Programming, allowing us to emulate private methods. This is known as the module pattern and it allows us to right easily maintainable code with reduced namespace pollution and more reusability.

Let's look at a case where this is done.

var makeCounter = function() {
  var privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }
  return {
    increment: function() {
      changeBy(1);
    },
    decrement: function() {
      changeBy(-1);
    },
    value: function() {
      return privateCounter;
    }
  }
};

var counter1 = makeCounter();
var counter2 = makeCounter();

counter1.value(); // returns 0
counter1.increment(); // adds 1
counter1.increment(); // adds 1
counter1.value(); // returns 2
counter1.decrement(); //subtracts 1
counter1.value(); // returns 1
counter2.value(); // returns 0

In the above example, we have declared a function makeCounter which is a public function that has access to some private variables within it such as privateCounter and the functions that manipulate it. This mimics the behaviour of creating makeCounter as a class with it's own built in fuctionality and variables. This can be seen when we create two different counters, counter1 and counter2 . Each counter is independent to the other and references a different version of variables.

Closures also allow us to use functions to create other functions that add a specific value to their argument. In this case, the parent function allowing this behaviour is known as a function factory as it essentially creates other functions.

Using function factories, we are able to achieve a behaviour known as Currying .which we'll cover in the next section.

Currying

Currying is the pattern of functions that immediately evaluate and return other functions. This is made possible by the fact that Javascript functions are expressions that can return other functions.

Curried functions are constructed by chaining closures by defining and immediately returning their inner functions simultaneously.

Here's an example of currying:

let greeting = function (a) {
    return function (b) {
        return a + ' ' + b
}

let hello = greeting('Hello')
let morning = greeting('Good morning')

hello('Austin') // returns Hello Austin
hello('Roy') // returns Hello Roy
morning('Austin') // returns Good morning Austin
morning('Roy') //returns Good Morning Roy

The two functions created from greeting ( hello and morning )each return functions that process the provided inputs to generate a greeting statement. They also take an argument which is the name of the person to be greeted.

In the above case, greeting is also used as a function factory with the two functions hello and morning generated from it.

The inner function may also be called invoked after the first call as follows:


greeting('Hello There')('General Kenobi') 
//returns Hello There General Kenobi

Currying is considered to be part of functional programming and as such curried functions may be easily written using the arrow function syntax in ES6 and newer versions of Javascript for cleaner, more elegant code.


let greeting = (a) = (b) => a + ' ' + b 

greeting('Hello There')('General Kenobi') 
//returns Hello There General Kenobi

Conclusion

While closures may not be as commonly used since Javascript incorporated classes in ES6, they still have their place when it comes to writing clean reusable code. Closures and Currying are also important concepts to understand when it comes to functional programming where they essentially serve a similar purpose to private methods in Object Oriented Programming.

Nahoru
Tento web používá k poskytování služeb a analýze návštěvnosti soubory cookie. Používáním tohoto webu s tímto souhlasíte. Další informace