Maison  >  Article  >  interface Web  >  Fermetures en JavaScript

Fermetures en JavaScript

韦小宝
韦小宝original
2018-03-07 14:08:281199parcourir

Tout le monde peut réciter la clôture en une ou deux phrases seulement. Mais la conclusion est le genre de question que les débutants peuvent rencontrer huit fois sur dix lors d'un entretien. S'ils ne peuvent pas y répondre, ils recevront une proposition, et s'ils peuvent y répondre, ils n'obtiendront aucun point. Afin d'éviter que notre développement front-end ne démarre et n'abandonne, permettez-moi de parler de ce que je pense être la fermeture en JS.

Qu'est-ce qu'une fermeture

Une fermeture crée une portée lexicale Une fois que les variables de cette portée sont référencées, elles peuvent être utilisées dans. cette portée lexicale. Elle est librement accessible en dehors du domaine et est une combinaison d'une fonction et de l'environnement lexical dans lequel la fonction est déclarée

Il existe une autre façon de dire qu'une fermeture est une fonction qui fait référence à un libre. variable. Cette variable libre existe avec la fonction, même si elle est séparée de l'environnement dans lequel elle a été créée. Vous voyez donc souvent qu'une fermeture est une fonction liée à un contexte, et c'est probablement ce que cela signifie. Une fois que vous l’aurez compris, vous sentirez que c’est en fait une chose très simple et pas très profonde.

Dans l'introduction suivante, je préfère l'explication selon laquelle la fermeture est une combinaison d'une fonction et de l'environnement lexical dans lequel la fonction est déclarée, j'élaborerai donc également sur la base de cette explication.

La fermeture est en fait un concept en informatique et n'est pas propre à JS. Le concept de fermeture est apparu dans les années 1960 et le premier langage de programmation à implémenter la fermeture était Scheme. (Ne me demandez pas ce qu'est Scheme, je ne sais pas si vous me le demandez. Ce paragraphe a été copié du Wiki.) Après cela, les fermetures ont été largement utilisées dans les langages de programmation fonctionnels .

Fermetures en JS

Maintenant, j'ai une astuce pour retirer une fermeture à mains nues.

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

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

Le code ci-dessus forme en fait une fermeture. La fonction say définie dans la fonction sayHello et l'environnement lexical dans lequel elle est déclarée forment une fermeture, car elle fait référence à une variable str définie dans sayHello, et la La fonction say est renvoyée, de sorte que la variable str définie dans sayHello soit également accessible en dehors de la fonction sayHello, comme si la fonction say était liée à cette variable.

En voyant cela, vous vous demandez peut-être pourquoi cette variable est toujours accessible en externe, car dans certains langages, on pense généralement que les variables locales d'une fonction ne sont accessibles que pendant l'exécution de la fonction. En parlant de ça, je dois parler de l'environnement d'exécution. Les amis qui n'y connaissent pas grand-chose peuvent d'abord lire mon article : Contexte d'exécution que vous ne connaissez pas. En fait, lorsque le code let myHello = sayHello('abby'); est exécuté, l'environnement d'exécution de sayHello() est censé être détruit, mais ce n'est pas le cas ici. La raison est que sayHello() renvoie une fonction In. cette fonction Le str fait référence à la variable externe str. Si elle est détruite, elle ne sera pas trouvée. Par conséquent, l'environnement d'exécution de la fonction sayHello() sera toujours en mémoire, il y aura donc des fermetures qui augmenteront la mémoire. frais généraux tels que balabala.

En fait, la clôture devrait être terminée ici, mais il peut y avoir beaucoup de choses qui vous rendent confus, alors continuons avec quelques exemples !

Par exemple

Exemple 1 : Les fermetures ne doivent pas nécessairement renvoyer une fonction

Bien que les fermetures courantes, les packages renvoient tous une fonction, mais les fermetures ne doivent pas nécessairement renvoyer. Renvoyer une fonction consiste simplement à accéder à une variable en dehors de la portée. Nous pouvons également le faire d'une autre manière, comme :

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

Dans cet exemple, say et l'environnement lexical dans lequel il est déclaré forment en fait une fermeture, et sa portée contient une référence à la variable str définie dans la fonction sayHello, elle peut donc également accéder à la variable str en dehors de la portée dans laquelle elle est définie. Déterminez simplement la nature des fermetures.

Mais en JS, la manière la plus courante de former une fermeture est d'imbriquer une autre fonction dans une fonction, et l'autre fonction contient les variables définies dans la portée parent.

Exemple 2 : La même fonction appelante génère le même environnement de fermeture, et toutes les fonctions qui y sont déclarées ont également des références aux variables libres de cet environnement.

Cette phrase est déroutante à dire, mais en fait je vais vous donner un exemple très simple.

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

Dans cet exemple, nous utilisons la fonction setUp pour générer un environnement de fermeture. Les trois fonctions de cet environnement partagent la référence à la variable number dans cet environnement, elles peuvent donc toutes accéder au numéro Effectuer des opérations.

Exemple 3 : Chaque fonction appelante créera un environnement de fermeture différent.

Donnons un exemple très simple.

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

Dans l'exemple ci-dessus, les instructions d'affectation de myClosure et yourClosure, c'est-à-dire que la fonction newClosure est appelée deux fois, créant ainsi deux environnements de fermeture différents, de sorte que les variables à l'intérieur s'excluent mutuellement.

Exemple 4 : Créer une fermeture à l'intérieur d'une boucle

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中作用域与函数闭包实例讲解

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn