preface

Many students have had, when the interview was asked about closure, do not know where to start the situation. In fact, closures are just a phenomenon (or feature) of JS, not as terrible as imagined.

What is a closure

A function and its reference to its lexical environment together constitute a closure. Closures let us access the scope of an external function from an internal function.

So what is a lexical environment?

Lexical environment

Lexical context is understood as the scope context in which a function is defined. Here’s an example:

function a() {
  var i = 0;
  return function inner() {
    i++;
    return i;
  };
}
// Or so
function a() {
  var i = 0;
  function inner() {
    returni; }}Copy the code

The lexical context of the inner function is the context in which the variable I resides.

A closure is understood to be “closed” in the sense that the scope that the inner function can access is determined when it is declared, not when it is called, and its lexical scope is closed externally.

The “package” of a closure is understood to mean that an inner function keeps references to its lexical environment, as if it were carrying a small backpack containing variables referenced to its lexical environment. When an inner function accesses I and cannot find it in the local scope, it will look in the backpack.

What closures do

  • Closures protect the private variables of a function from external interference

  • Is the variable stored in memory and not destroyed

Here’s an example:

function a() {
  var i = 0;
  return function () {
    i++;
    return i;
  };
}
var y = a();
y(); / / 1
y(); / / 2
y(); / / 3
Copy the code

The variable I in function A cannot be accessed outside of function A.

  • Where is the variable I stored in the closure? Each time you access function y, the variable I is not found in function y, and it is searched up the scope chain until it is not found in the global scope.

Closure application scenarios

Closures are often seen in implementing singleton patterns, currying, shaking, throttling, and modularization

2-0 Singleton mode

The instance is created only once to avoid memory consumption caused by repeated creation

function singleIns(name) {
  this.name = name;
}
singleIns.getInstance = (function () {
  var instance = null;
  return function (name) {
    if (!this.instance) {
      this.instance = new singleIns(name);
    }
    return this.instance; }; }) ();var a = singleIns.getInstance("a");
var b = singleIns.getInstance("b");
a === b; // true
Copy the code

2-1 currie

Input parameters can be disassembled and passed in

function curry(func) {
  return function curried(. args) {
    if (args.length >= func.length) {
      func.apply(this, args);
    } else {
      return function (. args2) {
        curried.apply(this, args.concat(args2)); }; }}; }// example
function a(x, y, z) {
  console.log(x + y + z);
}
var b = curry(a);
b(1.2.3); / / 6
b(1.2) (3); / / 6
b(1) (2.3); / / 6
b(1) (2) (3); / / 6
Copy the code

2-2 image stabilization

Triggers execution only once for a period of time

// Execute only for the last time
function debounce(func, time) {
  let timer = null;
  return function (. args) {
    timer && clearTimeout(timer);
    setTimeout(function () {
      func.apply(this, args);
    }, time);
  };
}
// Execute only the first time
function debounce(func, time){
  let timer = null;
  func.id = 0;
  return function(. args){
    if(func.id ! = =0) {clearTimeout(timer;)
    }
    timer = setTimeout(function () {
      func.id = 0;
      func.apply(this, args);
    }, time);
    func.id = func.id + 1; }}Copy the code

2-3 throttling

It fires over a period of time, at regular intervals

// Timestamp mode
function throttle(func, time) {
  let start = new Date(a);return function (. args) {
    if (new Date() - start >= time) {
      func.apply(this, args);
      start = new Date();
    }
  };
}
Copy the code

2-4 modular

Modular encapsulation encapsulates internal logic inside a module and exposes methods outside the module.

function module() {
  var pool = [];
  function add(item) {
    pool.push(item);
    return pool;
  }
  function remove(num) {
    pool.forEach((item, index) = > {
      if (item === num) {
        pool.splice(index, 1); }});return pool;
  }
  return {
    add: add,
    remove: remove,
  };
}
var foo = module(a); foo.add(); foo.remove();Copy the code

Is it easy to understand closures by looking closely at the above example?

The downside of closures

Overuse of closures can cause memory leaks (where useless variables are stored in memory and cannot be reclaimed, constantly occupying memory). The solution to this problem is to clear the variable (set to null).

function a() {
  var i = 0;
  return function () {
    i++;
    return i;
  };
}
var y = a();
y();
y = null; // After the variable is cleared, the reference disappears and the closure ceases to exist
Copy the code

Frequent meeting questions

    1. Look at the output
for (var i = 0; i < 5; i++) {
  setTimeout(function () {
    console.log(i);
  }, 0);
}
Copy the code

The output is: Five consecutive fives. Modify the preceding code to output 0, 1, 2, 3, and 4

Method 1: Use the third argument of setTimeout

for (let i = 0; i < 5; i++) {
  setTimeout(
    function () {
      console.log(i);
    },
    0,
    i
  );
}
Copy the code

Method two: Use let

for (let i = 0; i < 5; i++) {
  setTimeout(function () {
    console.log(i);
  }, 0);
}
Copy the code

Let creates a block-level scope.

Method three: Use self-executing functions

for (var i = 0; i < 5; i++) {
  (function (i) {
    setTimeout(function () {
      console.log(i);
    }, 0);
  })(i);
}
Copy the code

The self-executing function passes I into a new execution context.

Method four: Use function arguments

function output(i) {
  setTimeout(function () {
    console.log(i);
  }, 0);
}
for (var i = 0; i < 5; i++) {
  output(i);
}
Copy the code

Function parameters are passed by value.

Method five: borrow async

const timeout = function (time) {
  return new Promise((resolve, reject) = > {
    setTimeout(function () {
      resolve();
    }, time);
  });
};
async function print() {
  for (var i = 0; i < 5; i++) {
    await timeout(1000);
    console.log(i); }}await print();
Copy the code
  1. Look at the output
var a = 100;
function create() {
  var a = 200;
  return function () {
    console.log(a);
  };
}
var fn = new create();
fn();
Copy the code

This problem is very simple, is the concept of closure, output 200, I won’t explain here.

  1. Implement a sum(x)(y) function that does the same thing as sum(x,y)
function sum(a) {
  return function (b) {
    return a + b;
  };
}
sum(1) (2); / / 3
Copy the code

We’re looking at the concept of Curryization

tips

Do all closures need to be manually cleared?

Closures that are useful do not need to be cleared, and closures that are not useful need to be cleared. If it is a closure in a DOM event, components in vue and React have a life cycle. When uninstalled, events bound to the DOM will be automatically removed, and memory will be automatically reclaimed.

Do not use closures during development?

Closures are everywhere, and have been created every time we simulate private variables. Don’t keep long objects and arrays in memory; use closures wisely.

How do I check for memory leaks?

Closures are not the only cause of memory leaks, and the Perfomance and Memory tools of The Chrome Developer Tools (devTools) allow you to look at memory in detail. This article is not about memory leaks, so I don’t want to go into details about how to use devTools.

conclusion

Closure as one of the front-end eight-part essay, baffled a lot of students are looking for a job. Little sister is also in a learning process, there may be inaccurate, incorrect place, welcome everyone to like, discuss, progress together ah. I hope that’s helpful to some of you.

This article is a series of [JS Foundation] articles, pay attention to the little sister, learn a study together.