The definition of closure in javascript, the operating principle of javascript closure

Hello everyone, the editor is here to answer the following questions for you, the definition of closure in javascript, and the operating principle of javascript closure. Now let us take a look together!

Closures are a tricky JavaScript concept to understand because it’s hard to see how they are actually used.

Unlike other concepts such as functions, variables, and objects, you don’t always use closures carefully and directly. All the operation symbols in Python are used. You won’t say: Oh! Here I’m going to use closures as a solution.

But at the same time, you’ve probably used this concept a hundred times. Understanding closures is more about determining when to use closures than learning a new concept.

What is a closure in JavaScript?

You have a closure when a function reads or modifies the value of a variable defined outside its context.

const value = 1
function doSomething() {
    let data = [1,2,3,4,5,6,7,8,9,10,11]
    return data.filter(item => item % value === 0)
}

This function doSomething uses the variable value. But the function item => item % value === 0 can also be written like this:

function(item){
    return item % value === 0
}

valueYou use the value of a variable defined outside the function itself.

Functions can access values out of context

As with the previous example, a function can access and use values defined outside of its “body” or context, for example:

let count = 1
function counter() {
    console.log(count)
}
counter() // print 1
count = 2
counter() // print 2

This allows us to count modify the value of the variable from anywhere in the module. Then when the counter function is called, it will know what to do with the current value.

Why do we use functions?

But why do we use functions in our programs? Of course, it’s possible to write a program without using the functions we defined – it’s difficult, but possible. So why do we create appropriate functions?

Imagine a piece of code that does something cool, no matter what, it consists of X lines.

/* My wonderful piece of code */

Now suppose you had to use thiswonderful code in various parts of your program, what would you do?

The “natural” choice is to put this code together into a reusable collection, and this reusable collection is what we call a function. Functions are the best way to reuse and share code within your program.

Now you can use your function as many times as you like. And, ignoring some special cases, calling your function N times is the same as writing thatwonderful codeN times. This is a simple replacement.

But where is the closure?

Using a counterexample, let’s think of it as apiece of wonderful code.

let count = 1
function counter() {
    console.log(count)
}
counter() // print 1

Now, we want to reuse it in many parts, so we “wrap” it in a function.

function wonderfulFunction() {
    let count = 1
    function counter() {
        console.log(count)
    }
    counter() // print 1
}

What do we have now? A function: counter which uses the value count declared outside it. There is also a value: count is declared within the wonderfulFunction function scope but uses counter inside the function.

That is, we have a function that uses a value declared outside of its context: a closure.

Pretty simple, isn’t it? Now, what happens when the function wonderfulFunction is executed? What happens to the variable count and the function after executing the parent function? counter

Variables and functions declared in their body “disappear” (garbage collector).

Now, let’s modify the example a little:

function wonderfulFunction() {
    let count = 1
    function counter() {
        count++
        console.log(count)
    }
   setInterval(counter, 2000)
}
wonderfulFunction()

Now what happens to the variables and functions declared inside wonderfulFunction?

In this example, we tell the browser to run counter every 2 seconds. Therefore, the JavaScript engine must keep a reference to the function and to the variables it uses. The function counter and value count will “live” even after the parent function wonderfulFunction completes its execution cycle.

This “effect” with closures occurs because JavaScript supports nesting of functions. Or in other words, functions are the first class citizens of the language, and you can use them just like any other object: nested, passed as parameters, returned as values, etc.

What can I do with closures in JavaScript?

Immediately invoked function expression (IIFE)

This is a technique that was heavily used in the ES5 era to implement the “module” design pattern (before native support). The idea is to “wrap” your module in a function that executes immediately.

(function(arg1, arg2){
...
...
})(arg1, arg2)

This allows you to use private variables in a function that can only be used by the module itself – that is, it can simulate access modifiers.

const module = (function(){
function privateMethod () {
}
const privateValue = "something"
return {
get: privateValue,
set: function(v) { privateValue = v }
}
})()

var x = module()
x.get() // "something"
x.set("Another value")
x.get() // "Another Value"
x.privateValue //Error
Function Factory

Another design pattern implemented because of closures is “Function Factory”. This is when a function creates a function or object, for example a function that allows you to create a user object.

const createUser = ({ userName, avatar }) => ({
      id: createID(),
      userName,
      avatar,
      changeUserName (userName) {
        this.userName = userName;
        return this;
      },
      changeAvatar (url) {
        // execute some logic to retrieve avatar image
        const newAvatar = fetchAvatarFromUrl(url)
        this.avatar = newAvatar
        return this
      }
    });
    
        console.log(createUser({ userName: 'Bender', avatar: 'bender.png' }));
    
    {
      "id":"17hakg9a7jas",
      "avatar": "bender.png",
      "userName": "Bender",
      "changeUsername": [Function changeUsername]
      "changeAvatar": [Function changeAvatar]
    
    }
    */c

Using this pattern, you can implement an idea calledcurryingfrom functional programming.

Currying

Currying is a design pattern (and a feature of some languages) in which one function is immediately evaluated and a second function is returned. This mode allows you to perform specialization and composition.

You create these “curried” functions using closures, defining and returning the closure’s inner functions.

function multiply(a) {

    return function (b) {
        return function (c) {
            return a * b * c
        }
    }
}
let mc1 = multiply(1);
let mc2 = mc1(2);
let res = mc2(3);
console.log(res);

let res2 = multiply(1)(2)(3);
console.log(res2);

These types of functions take a single value or parameter and return another function that also accepts parameters. This is part of the application of the argument. This example can also be rewritten using ES6.

let multiply = (a) => (b) => (c) => {

    return a * b * c;
}

let mc1 = multiply(1);
let mc2 = mc1(2);
let res = mc2(3);
console.log(res);

let res2 = multiply(1)(2)(3);
console.log(res2);

Where can we apply currying? In composition, let’s say you have a function that creates HTML elements.

unction createElement(element){
    const el = document.createElement(element)
    return function(content) {
        return el.textNode = content
    }
}

const bold = crearElement('b')
const italic = createElement('i')
const content = 'My content'
const myElement = bold(italic(content)) // <b><i>My content</i></b>
Event listeners

Another place where closures can be used and applied is with React’s event handlers.

Suppose you are using a third-party library to render items in a data collection. The library exposes a component called RenderItem which has only one available prop onClick. This prop does not receive any parameters and does not return a value.

Now, in your particular application, you require that when the user clicks on an item, the application displays an alert with the title of the item. But onClick your available event doesn’t accept parameters – so what can you do? Close rescue:

// Closure
// with es5
function onItemClick(title) {
    return function() {
      alert("Clicked " + title)
    }
}
// with es6
const onItemClick = title => () => alert(`Clcked ${title}`)

return (
  <Container>
{items.map(item => {
return (
   <RenderItem onClick={onItemClick(item.title)}>
    <Title>{item.title}</Title>
  </RenderItem>
)
})}
</Container>
)

In this simplified example, we create a function that receives the title you want to display and returns another function that satisfies the RenderItem function definition received as a prop.

Conclusion

You can even develop applications without knowing you are using closures. But understanding that they exist and how they actually work opens up new possibilities when you create solutions.

Closures are one of those concepts that can be difficult to understand at first. But once you know you’re using them and understand them, it allows you to increase your tools and advance your career.