JavaScript Closure Use Posture Guide

Introduction

A closure is a function that can access variables in the scope of another function. A closure is a function that can access variables in the scope of other functions. js has a global object, under the browser is window, under the node is global. All functions under this object can also access variables under this object. That is to say, all functions in js are closures

Definition of Closure

A closure consists of a function and a reference to its state, the lexical environment.That is, closures allow you to access the scope of an external function from an internal function.In JavaScript, functions generate closures every time they are created.[[1]]

MDN's definition of closures explains the lexical context and references, as well as generating closures each time they are created.
Reference Code

const eg = ()=>{
    let a ='Test variables' // Local variables created by eg
    let inner = ()=>{ // eg's internal function, a closure
        console.log(a) // Variables declared in the parent function are used
    }
    return inner // inner is a closure function that has access to the scope of the eg function
}

An interesting example

function init() {
    var name = "Mozilla"; // name is a local variable created by init
    function displayName() { // displayName() is an internal function, a closure
        alert(name); // Variables declared in the parent function are used
    }
   displayName();
 }
 init();

There is no doubt that dispplayName has access to the parent scope init's variable name for js scope reasons

So look at this example again

function makeFunc() {
    var name = "Mozilla";
    function displayName() {
        alert(name);
    }
    return displayName;
  }
var myFunc = makeFunc();
myFunc();

This code is exactly the same as the previous code execution, and the difference - and interesting - is that the internal function displayName() is returned by an external function before execution.You probably think it won't work, so let's change the code a little more


var name2 = 123
function makeFunc() {
    var name = "Mozilla";
    function displayName() {
        alert(name2);
    }
    return displayName;
  }
var myFunc = makeFunc();
myFunc();

You hardly need to think about knowing that the result must be 123, so why can't you be sure that the code was executed before we returned?

The answer is that functions in JavaScript form closures.Closure is a combination of a function and the lexical context in which it was created.Please read this passage carefully. The closure of js is a combination of the function and the Lexical Environment in which it was created. There is this variable in the Lexical Environment in which it was created. All directly using this variable, if not, look up until it is not found in the global environment and return to undefind

So let's change the example again


var object = {
     name: ''object",
     getName:  function() {
        return function() {
             console.info(this.name)
        }
    }
}
object.getName()()    // underfined

Where does this point to at this point? The answer is global because the closure function inside is executed in windows scope, that is, this points to window s

Now let's take another example


function outer() {
     var  a = 'Variable 1'
     var  inner = function () {
            console.info(a)
     }
     return inner    // inner is a closing function because it has access to the scope of the outer function
}
var  inner = outer()   // Get inner closure function
inner()   //"Variable 1"

When the program finishes executing var inner = outer(), the execution environment of outer is not actually destroyed, because the variable a inside is still referenced by inner's function scope chain, and when the program finishes executing inner(), inner's and outer's execution environment are destroyed.Overuse of closures can result in an excessive memory footprint because it takes up more content than other functions.[[2]]

Let's take another interesting example


function makeAdder(x) {
  return function(y) {
    return x + y;
  };
 }

var add5 = makeAdder(5);
var add10 = makeAdder(10);

console.log(add5(2));  // 7
console.log(add10(2)); // 12

Both add5 and add10 are closures and share the definition of functions, but they preserve different lexical environments, where x=5 is in add5 and X is 10 in add10.

Memory leak problem

Closure functions refer to outer variables, which cannot be released when the outer function is executed


function  showId() {
    var el = document.getElementById("app")
    el.onclick = function(){
      aler(el.id)   // This causes the closure to reference the outer el, which cannot be released after showId is executed
    }
}

// Change to function showId () below {
    var el = document.getElementById("app")
    var id  = el.id
    el.onclick = function(){
      aler(id)   // This causes the closure to reference the outer el, which cannot be released after showId is executed
    }
    el = null    // Active release el
}

function  factorial(num) {
   if(num<= 1) {
       return 1;
   } else {
      return num * factorial(num-1)
   }}var anotherFactorial = factorial
factorial = nullanotherFactorial(4)   // Error, because it is best to return num* arguments.callee (num-1), arguments.callee points to the current executing function, but it will also error if it is not available in strict mode, so do it with closures


// Recursive function newFactorial = (function f(num){using closures
    if(num<1) {return 1}
    else {
       return num* f(num-1)
    }
}) //So there's no problem, it's actually the closure function f that works, not the outside function newFactorial

Solving recursive call problems with closures

Simulating private methods with closures

Programming languages, such as Java, support declaring methods private, that is, they can only be called by other methods in the same class.

JavaScript does not have this native support, but we can use closures to simulate private methods.Private methods are not only helpful in restricting access to code: they also provide a powerful ability to manage global namespaces and avoid non-core methods cluttering the public interface parts of code.

The following example shows how closures can be used to define public functions and make private functions and variables accessible.This is also known as the module pattern


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()); /* logs 0 */
Counter.increment();
Counter.increment();
console.log(Counter.value()); /* logs 2 */
Counter.decrement();
console.log(Counter.value()); /* logs 1 */

In the previous example, each closure had its own lexical environment; this time, we created only one lexical environment that was shared by three functions: Counter.increment, Counter.decrement, and Counter.value.

The shared environment is created within an immediately executed anonymous function body.This environment contains two private items: a variable named privateCounter and a function named changeBy.Neither of these can be accessed directly outside the anonymous function.It must be accessed through three common functions returned by anonymous functions.

These three common functions are closures that share the same environment.Thanks to the lexical scope of JavaScript, they can access both the privateCounter variable and the changeBy function.

You should notice that we have defined an anonymous function to create a counter.We immediately executed the anonymous function and assigned its value to the variable Counter.We can store this function in another variable, makeCounter, and use it to create multiple counters.


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();
console.log(Counter1.value()); /* logs 0 */
Counter1.increment();
Counter1.increment();
console.log(Counter1.value()); /* logs 2 */
Counter1.decrement();
console.log(Counter1.value()); /* logs 1 */
console.log(Counter2.value()); /* logs 0 */

Notice how the two counters Counter1 and Counter2 maintain their respective independence.Each closure refers to the variable privateCounter in its own lexical scope.

Each time one of these counters is called, changing the value of this variable changes the lexical context of this closure.However, modifications to variables within one closure do not affect variables in another closure.

Using closures in this way provides many benefits related to object-oriented programming -- especially data hiding and encapsulation.

Use closures in loops

<p id="help">Helpful notes will appear here</p>
<p>E-mail: <input type="text" id="email" name="email"></p>
<p>Name: <input type="text" id="name" name="name"></p>
<p>Age: <input type="text" id="age" name="age"></p>
function showHelp(help) {
  document.getElementById('help').innerHTML = help;
}

function setupHelp() {
  var helpText = [
      {'id': 'email', 'help': 'Your e-mail address'},
      {'id': 'name', 'help': 'Your full name'},
      {'id': 'age', 'help': 'Your age (you must be over 16)'}
    ];

  for (var i = 0; i < helpText.length; i++) {
    var item = helpText[i];
    document.getElementById(item.id).onfocus = function() {
      showHelp(item.help);
    }
  }
}

setupHelp(); 

As you can see here, all items.help point to the last item of helptext because they share the same lexical scope, either by using the let keyword or by using an anonymous closure

// Anonymous Closure
function showHelp(help) {
  document.getElementById('help').innerHTML = help;
}

function setupHelp() {
  var helpText = [
      {'id': 'email', 'help': 'Your e-mail address'},
      {'id': 'name', 'help': 'Your full name'},
      {'id': 'age', 'help': 'Your age (you must be over 16)'}
    ];

  for (var i = 0; i < helpText.length; i++) {
    (function() {
       var item = helpText[i];
       document.getElementById(item.id).onfocus = function() {
         showHelp(item.help);
       }
    })(); // Associate the item of the current loop item with the event callback immediately
  }
}
setupHelp();

// Use let keyword
function showHelp(help) {
  document.getElementById('help').innerHTML = help;
}

function setupHelp() {
  var helpText = [
      {'id': 'email', 'help': 'Your e-mail address'},
      {'id': 'name', 'help': 'Your full name'},
      {'id': 'age', 'help': 'Your age (you must be over 16)'}
    ];

  for (var i = 0; i < helpText.length; i++) {
    let item = helpText[i];
    document.getElementById(item.id).onfocus = function() {
      showHelp(item.help);
    }
  }
}

setupHelp();

Performance considerations

If closures are not required for certain tasks, it is unwise to create functions in other functions, because closures have a negative impact on script performance in terms of processing speed and memory consumption.

For example, when creating a new object or class, methods should usually be associated with the object's prototype, not defined in the object's constructor.The reason for this is that each time the constructor is called, the method is reassigned (that is, the creation of each object).


function MyObject(name, message) {
  this.name = name.toString();
  this.message = message.toString();
  this.getName = function() {
    return this.name;
  };

  this.getMessage = function() {
    return this.message;
  };
}

In the code above, we don't take advantage of closures, so we can avoid them.Modify to the following:


function MyObject(name, message) {
  this.name = name.toString();
  this.message = message.toString();}MyObject.prototype = {
  getName: function() {
    return this.name;
  },
  getMessage: function() {
    return this.message;
  }
};

This is also possible


function MyObject(name, message) {
  this.name = name.toString();
  this.message = message.toString();
}
MyObject.prototype.getName = function() {
  return this.name;};MyObject.prototype.getMessage = function() {
  return this.message;
};

Keywords: Javascript Programming Windows Java

Added by magic-eyes on Sat, 16 Nov 2019 04:08:07 +0200