📜 ⬆️ ⬇️

Why do you have to give another chance to closure

Hi, Habr! I present to you the translation of the article “Why you should give the Closure function another chance” by Cristi Salcescu.

In JavaScript, functions can be nested inside other functions.

A closure is when an internal function has access to the variables of the parent function, even after the parent function is executed.

As you can see, it becomes interesting when the internal function continues to exist in the call to the parent function. This will happen in the following situations:


Closing and Timers


In the following example, we expect that the local variable x will be immediately destroyed after executing the autorun () function, but it will be alive for 10 minutes. This is due to the fact that the variable x is used by the internal log () function. The log () function is a closure.


(function autorun(){ var x = 1; setTimeout(function log(){ console.log(x); }, 6000); })(); 

When setInterval () is used , the variable referenced by the closure function will only be destroyed after calling clearInterval () .


Closure and events


We create closures every time variables from external functions are used in event handlers. The event handler increment () is a closure in the following example.


 (function initEvents(){ var state = 0; $("#add").on("click", function increment(){ state += 1; console.log(state); }); })(); 

Closing and asynchronous tasks


When variables from an external function are used in an asynchronous call, the call becomes a closure, and the variables will remain active until the asynchronous task is completed.


Timers, events, and AJAX calls are probably the most common asynchronous tasks, but there are other examples: HTML5 Geolocation API, WebSockets API, and requestAnimationFrame ().


In the following example, the AJAX call updateList () is a closure.


 (function init(){ var list; $.ajax({ url: "https://jsonplaceholder.typicode.com/users"}) .done(function updateList(data){ list = data; console.log(list); }) })(); 

Closure and Encapsulation


Another way to see a closure is a private state function. A closure encapsulates a state.


For example, let's create a count () function with a private state. Each time she is called, she remembers her previous state and returns the next consecutive number. The state variable is private, there is no access to it from outside.


 function createCount(){ var state = 0; return function count(){ state += 1; return state; } } var count = createCount(); console.log(count()); //1 console.log(count()); //2 

We can create multiple closures that share the same private state. In the following example, increment () and decrement () are two closures separating the same private state variable. Thus, we can create objects with private state.


 function Counter(){ var state = 0; function increment(){ state += 1; return state; } function decrement(){ state -= 1; return state; } return { increment, decrement } } var counter = Counter(); console.log(counter.increment());//1 console.log(counter.decrement());//0 

Short circuit vs pure functions


The closure uses variables from outer scope.


Pure functions do not use variables from outer scope. Pure functions must return a value calculated using only the values ​​passed to it, and there can be no side effects.


Asynchronous tasks: closure and cycles


In the following example, I will create five closures for five asynchronous tasks, all using the same variable i . Since the variable i changes during the loop, all console.log () display the same value — the last.


 (function run(){ var i=0; for(i=0; i< 5; i++){ setTimeout(function logValue(){ console.log(i); //5 }, 100); } })(); 

One way to fix this problem is to use IIFE (Immediately Invoked Function Expression). In the following example, there are five more closures, but more than five different variables i .


 (function run(){ var i=0; for(i=0; i<5; i++){ (function autorunInANewContext(i){ setTimeout(function logValue(){ console.log(i); //0 1 2 3 4 }, 100); })(i); } })(); 

Another option is to use a new way of declaring variables: via let , available as part of ECMAScript 6. This will create a variable locally for the block scope at each iteration.


 (function run(){ for(let i=0; i<5; i++){ setTimeout(function logValue(){ console.log(i); //0 1 2 3 4 }, 100); } })(); 

I think this is the best option for this problem in terms of readability.


Closure and Garbage Collector


In JavaScript, local variables of a function will be destroyed after the function returns if there are no references to them. The private state of the closure becomes suitable for garbage collection after the closure itself has been removed. To make this possible, the closure should not have any more references to it.


In the following example, I first create an add () closure


 function createAddClosure(){ var arr = []; return function add(obj){ arr.push(obj); } } var add = createAddClosure(); 

Then I define two functions: one to add a large number of addALotOfObjects () objects and another clearAllObjects () to set the reference to null . Then both functions are used as event handlers.


 function addALotOfObjects(){ for(let i=0; i<50000;i++) { add({fname : i, lname : i}); } } function clearAllObjects(){ if(add){ add = null; } } $("#add").click(addALotOfObjects); $("#clear").click(clearAllObjects); 

Clicking "Add" will add 50,000 items to the private state of the circuit.



')

I clicked “Add” three times and then clicked “Clear” to set the link to null . After that, the private state is cleared.



Conclusion


Closure is the best tool in our encapsulation toolbox. It also simplifies our work with calls for asynchronous tasks. We just use the variables we want, and they will come to life by the time of the call.


On the other hand, it is very helpful to understand how closures work to make sure that closures are removed when garbage is collected when we no longer need them.


This is probably the best feature ever put into a programming language.

Douglas Crockford on closure.

Source: https://habr.com/ru/post/352882/


All Articles