What are scopes and scope chains

Let’s start with the following code:

function fn (name) { return function (object1, object2) { var val1 = object1[name]; var val2 = object2[name]; return val1 > val2; } } var f = fn('www'); f(); // Dereference anonymous functions (to free memory) f = null;Copy the code
  • Each function has its own execution environment.

  • Each execution environment has a variable object associated with it, and all variables and functions defined in the environment are stored in this object. If the environment is a function, treat its active object as a variable object.

  • The global execution environment is the most peripheral execution environment.

  • When the execution stream enters a function, the function’s environment is pushed onto an environment stack. After the function is executed, the stack pops up its environment, returning control to the previous execution environment.

  • When code is executed in an environment, a scope chain of variable objects is created.

  • The purpose of scope chains is to ensure orderly access to all variables and functions that the execution environment has access to.

  • At the front of the scope chain is always the variable object of the environment in which the code is currently executing. The next variable object in the scope chain comes from the external environment, and the next variable object comes from the external environment. The variable object of the global execution environment is always the last object in the scope chain.

  • When a function is called, an execution environment and the corresponding chain of scopes are created.

  • A scope chain is essentially a list of Pointers to variable objects that reference but do not actually contain them.

  • In general, when the function completes execution, the local active object is destroyed and only the global scope (the variable object of the global execution environment) is kept in memory. But closures are not consistent.

  • After the fn function completes execution, the scope chain of its execution environment is destroyed, but its active object remains in memory until the anonymous function is destroyed.

What is a closure

JS advanced programming gives the concept of closure: closure is a function that has access to variables in the scope of another function.

Var local = 'variable' function foo () {console.log(local); }Copy the code

The sum of the “function” and the “variables accessible within the function” (also known as the environment) is a closure.

function foo(){
  var local = 1
  function bar(){
    local++
    return local
  }
  return bar
}

var func = foo()
func()
Copy the code

Why do functions cover functions?

We put local in a function because we need local variables. If we don’t put local in a function, local is a global variable, failing the purpose of using closures to hide variables.

So the function covers the function just to make a local variable, independent of the closure.

Why return bar?

Because if you don’t return, you can’t use this closure. The same applies to return bar as window.bar = bar, as long as the bar function is accessible from outside.

So the return bar is only for the bar to be used, and has nothing to do with closures.

The role of closures

Closures are often used to “indirectly access a variable.” In other words, “hide a variable.”

Exposes an accessor (function) that can be accessed indirectly by other functions by means of a local variable.

! function(){ var lives = 50 function fn (){ lives += 1; } return fn }()Copy the code

What exactly is a closure?

Closures are byproducts of the scope of JS functions.

In other words, this code fits the definition of a closure precisely because JS functions can use variables outside the function. Instead of JS deliberately using closures.

Do closures cause memory leaks?

A memory leak is when variables that you don’t need (or can’t access) still occupy memory space and can’t be reused.

Because closures carry the scope of the function that contains them, they take up more memory than other functions. Overuse of closures can lead to excessive memory usage.

The use of closures in versions prior to IE9 caused some special problems:

function assignHandler() { var element = docment.getElementById('someEle'); element.onclick = function () { alert(element.id); }}Copy the code

The closure above creates a circular reference. Because anonymous functions hold an active object to assignHandler, there is no way to reduce the number of references to Elements. As long as the anonymous function exists, Element’s number of references will never be zero and its memory will not be reclaimed.

You can make some changes to the code:

function assignHandler() {
    var element = docment.getElementById('someEle');
    var id = element.id;
    element.onclick = function () {
        alert(id);
    }
    element = null;
}
Copy the code

This code sets the Element object to null, which unreferences the DOM object, reducing its number of references and ensuring that its memory is properly reclaimed.

How do I avoid memory leaks caused by closures?

  • Avoid variable loop assignments and references

Closure usage scenarios

  1. Function image stabilization
/** * @param {function} fn * @param {Number} interval * @return {function} * */ function debounce(fn, interval) {let timer = null; Timer return function() {// Clear the last timer clearTimeout(timer); // get the current function scope let _this = this; / / get the current function parameter arrays let args = Array. The prototype. Slice. The call (the arguments, 0); Timer = setTimeout(function() {fn.apply(_this, args); / / the default 300 ms}, the interval | | 300)}}Copy the code

If the event is triggered within n seconds, the function execution time is recalculated. More colloquially: a function can fire only once in a fixed period of time, and only once in multiple events.

When to use: The search function, which starts to send a search request after the user finishes typing, can be achieved by using the function anti-shake.

  1. Function of the throttle
/** * @function throttle * @param {function} fn * @param {Number} interval * @return {function} Function throttle(fn, interval) {let timer = null; // timer let firstTime = true; / / it is the first execution / / use closures return function () {/ / get a function parameter arrays let args = Array. Prototype. Slice. The call (the arguments, 0); // get the current function scope let _this = this; Fn.apply (_this, args); fn.apply(_this, args); fn.apply(_this, args); fn.apply(_this, args); FirstTime = null; } // If (timer) return if(timer) return; Function () {fn.apply(_this, args); fn.apply(_this, args); // Clear the previous timer timer = null; / / the default 300 ms performs a}, the interval | | 300)}}Copy the code

A function can only be executed once in a certain period of time.

Time of use:

  • To change the browser window size, you can use function throttling to avoid constant execution of functions.

  • Scroll event, which is throttled through the function to avoid continuous execution of the function.

Function throttling and function anti – shake difference:

Let’s use an example to illustrate the difference: Set an interval of one second, within one minute, move the mouse repeatedly and have it trigger a function that prints something.

Function anti – shake: will print once, in the mouse stopped moving after a second to print.

Function throttling: will print 60 times, because there are 60 seconds in a minute, will trigger once every second.

Summary: Throttling is to limit the number of times a function is executed, and buffeting is to limit the timing of the function’s execution.

  1. The function is currified

Function currification refers to the technique of converting a function that takes multiple arguments into a function that takes a single argument and returns a new function that takes the remaining arguments and returns the result.

Function add(x, x) Function curryingAdd(x) {return function (y) {return x + y}} add(1, 2) // 3 curryingAdd(1)(2) // 3Copy the code
  1. Loop binding events to a set of elements
function bindClick() {
    var allLi = document.getElementsByTagName('li');
    for (var i = 0; i < allLi.length; i++) {
      (function(i) {
        allLi[i].onclick = function () {
          console.log(i)
        }
      })(i);
    }
}
bindClick(); 
Copy the code

Click on the three elements to print 0,1,2 respectively.

  1. SetTimeout.

    The first function passed by a native setTimeout cannot take arguments, and can be passed by closures.

function f1(a) { function f2() { console.log(a); } return f2; } var fun = f1(1); setTimeout(fun, 1000); // Prints 1 after one secondCopy the code
  1. Encapsulating private variables
let Counter = (function() { let privateCount = 0; Function change(val) {privateCounter += val; } return { increment: function(val) { change(val); }, decrement: function(val) { change(-val); }, value: function() { return privateCounter; }}}) (); console.log(Counter.value()) console.log(Counter.increment(1)) console.log(Counter.decrement(1)) Console.log (counter.value ()) // Prints 0 and 2Copy the code
  1. The callback function
GetUserId (url) {return new Promise(function (resolve) {http.get(url, function (id) { resolve(id) }) }) } getUserId('some_url').then(function (id) { //do something return getNameById(id); }).then(function (name) { //do something return getCourseByName(name); }).then(function (course) { //do something return getCourseDetailByCourse(course); }).then(function (courseDetail) { //do something });Copy the code
  1. Self-executing function
(function () { var a = 0; setInterval(function() { console.log(a++); }, 1000); }) ();Copy the code

Refer to the article

This article refers to the following articles and JS advanced programming (3rd edition)

What are closures in JS?

What are closures, their pros and cons, and how do closures work

Detailed solution of JS function coriolization