Home  >  Article  >  Web Front-end  >  Closures in JavaScript

Closures in JavaScript

韦小宝
韦小宝Original
2018-03-07 14:08:281133browse

Closure is just one or two sentences that anyone can recite. But closure is the kind of question that beginners may encounter eight times out of ten interviews. If they can't answer it, they will be given a proposition, and if they can answer it, they won't get any points. In order to prevent our front-end development from getting started and giving up, let me talk about what I think is closure in JS.

What is a closure

A closure creates a lexical scope. After the variables in this scope are referenced, they can be used in this lexical scope. Free access outside the domain is a combination of a function and the lexical environment in which the function is declared.

There is another way of saying that a closure is a function that references a free variable. This free variable exists with the function, even if it is separated. The environment in which it was created. So you often see that a closure is a function bound to a context, and that's probably what it means. Once you understand it, you will realize that it is actually a very simple thing and not very profound.

In the following introduction, I prefer the explanation that closure is a combination of a function and the lexical environment in which the function is declared, so I will also elaborate based on this explanation.

Closure is actually a concept in computer science and is not unique to JS. The concept of closure appeared in the 1960s, and the earliest programming language to implement closure was Scheme. (Don’t ask me what Scheme is, I don’t know if you ask me. This paragraph was copied from Wiki.) After that, closures were widely used in functional programming languages.

Closures in JS

Now, I’m going to use my ultimate trick, to create a closure with my bare hands.

function sayHello(name) {
  let str = `Hello,${name}`;
  function say() {
    console.log(str);
  }
  return say;
}

let myHello = sayHello('abby');
myHello(); // Hello,abby

The above code actually forms a closure. The function say defined in the sayHello function and the lexical environment in which it is declared form a closure because it references sayHello. Define a variable str, and return the say function, so that the variable str defined in it can be accessed outside the sayHello function, as if the say function is bound to this variable.

Seeing this, you may wonder why this variable can still be accessed externally, because in some languages, it is generally believed that the local variables of a function can only be accessed during the execution of the function. Speaking of which, I have to talk about the execution environment. Friends who don’t know much about it may first read my article: Execution Context You Don’t Know. In fact, when the code let myHello = sayHello('abby'); is executed, the execution environment of sayHello() is supposed to be destroyed, but it is not here. The reason is because sayHello() returns a function. In this function The str refers to the external variable str. If it is destroyed, it will not be found. Therefore, the execution environment of the sayHello() function will always be in the memory, so there will be closures that will increase the memory overhead such as balabala.

In fact, when it comes to this, closure should be finished, but there may be many things that make you confused, so let’s continue to talk about a few examples!

For example

Example 1: Closures do not necessarily need to return a function

Although common closures Packages all return a function, but closures do not necessarily have to return. Returning a function is just to access a variable outside the scope. We can also do it in another way, such as:

let say;
function sayHello(name) {
  let str = `Hello,${name}`;
  say = function() {
    console.log(str);
  }
}
let myHello = sayHello('abby');
say(); // Hello,abby

In this example, say and the lexical environment in which it is declared actually form a closure, and its scope holds a reference to the str variable defined in the sayHello function, so it can also be used in str Access a variable outside the scope in which it is defined. Just figure out the nature of closures.

But in JS, the most common way to form a closure is to nest another function inside a function, and the other function holds the variables defined in the parent scope.

Example 2: The same calling function generates the same closure environment, and all functions declared in it also have references to the free variables in this environment.

This sentence sounds confusing, but in fact, I will give you a very simple example.

let get, up, down
function setUp() {
  let number = 20
  get = function() {
    console.log(number);
  }
  up = function() {
    number += 3
  }
  down = function() {
    number -=2;
  }
}
setUp();
get(); // 20
up();
down();
get(); // 21

In this example, we use the setUp function to generate a closure environment. The three functions in this environment share the reference to the number variable in this environment, so they can all operate on number. .

Example 3: Each calling function will create a different closure environment.

Let’s give a very simple example.

function newClosure() {
  let array = [1, 2];
  return function(num) {
    array.push(num);
    console.log(`array:${array}`);
  }
}
let myClosure = newClosure();
let yourClosure = newClosure();
myClosure(3); // array:1,2,3
yourClosure(4); // array:1,2,4
myClosure(5); // array:1,2,3,5

In the above example, the assignment statement of myClosure and yourClosure, that is, the newClosure function is called twice, thus creating two different closure environments, so the variables inside do not affect each other. .

Example 4: Creating a closure inside a loop

function newClosure() {
  for(var i = 0; i < 5; i++) {
    setTimeout(function() {
      console.log(i);
      })
  }
}
newClosure(); // 5个5

打印的结果大家也知道是5个5,因为 setTimeout 里面的函数保持对 i 的引用,在setTimeout的回调函数被执行的时候这个循环早已经执行完成,这里我之前在另一篇文章里面做过更深入的介绍:深入浅出Javascript事件循环机制(上)。

这里我要说的是我们如何才能得到我们想要的01234,在这里有两种做法。

一种是 创建一个新的闭包对象,这样每个闭包对象里面的变量就互不影响。例如下面的代码种每次 log(i)都会创建不同的闭包对象,所有的回调函数不会指向同一个环境。

function log(i) {
  return function() {
    console.log(i);
  }
}
function newClosure() {
  for(var i = 0; i < 5; i++) {
    setTimeout(log(i));
  }
}
newClosure(); // 0 1 2 3 4

另一种做法就是使用自执行函数,外部的匿名函数会立即执行,并把 i 作为它的参数,此时函数内 e 变量就拥有了 i 的一个拷贝。当传递给 setTimeout 的匿名函数执行时,它就拥有了对 e 的引用,而这个值是不会被循环改变的。写法如下:

function newClosure() {
  for(var i = 0; i < 5; i++) {
    (function(e) {
      setTimeout(function() {
        console.log(e);
      })
    })(i)  
  }
}
newClosure(); // 0 1 2 3 4

看看,写这么多,多累是不是,还是let省事,所以赶紧拥抱 es6 吧。。。

好了,这次是真的结束了,我所理解的闭包大概就是这样了,如果理解有所偏差,欢迎指出,谁当初不是从颗白菜做起的呢,学习前端的小伙伴们可以看看哦!

关于闭包:

js中作用域与函数闭包实例讲解

The above is the detailed content of Closures in JavaScript. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn