Le grand camarade Einstein a dit un jour : "Si vous ne pouvez pas expliquer clairement quelque chose à un enfant de 6 ans, alors vous ne le comprenez pas vous-même." Cependant, lorsque j’ai expliqué ce qu’étaient les fermetures à un ami de 27 ans, j’ai complètement échoué.
Il s'agissait à l'origine d'une question soulevée par un ami étranger sur Stack Overflow à propos des fermetures JavaScript. Cependant, puisque cette question a été posée sur Stack Overflow, bien sûr de nombreux experts viendront y répondre. Certaines réponses sont effectivement des classiques, comme la suivante :
.Si vous définissez une fonction interne dans une fonction externe, c'est-à-dire une fonction imbriquée, alors la fonction interne peut également accéder aux variables de la fonction externe :
function foo(x) { var tmp = 3; function bar(y) { alert(x + y + (++tmp)); } bar(10); } foo(2); // alert 16 foo(2); // alert 16 foo(2); // alert 16
Ce code peut être exécuté correctement et renvoie le résultat : 16, car bar peut accéder à la variable tmp de la fonction externe et peut également accéder au paramètre x de la fonction externe foo. Mais l’exemple ci-dessus n’est pas une clôture !
Pour implémenter la fermeture, la fonction interne doit être renvoyée comme valeur de retour de la fonction externe. Avant de revenir, la fonction interne verrouillera toutes les variables de la fonction externe qui ont été accédées dans la mémoire. ces variables Il résidera dans la mémoire du bar et ne sera pas recyclé par le garbage collector, comme suit :
function foo(x) { var tmp = 3; return function (y) { alert(x + y + (++tmp)); } } var bar = foo(2); // bar 现在是个闭包了 bar(10); // alert 16 bar(10); // alert 17 bar(10); // alert 18
Dans le code ci-dessus, lorsque bar est exécuté pour la première fois, le résultat sera toujours renvoyé : 16, car bar peut toujours accéder à x et tmp, bien qu'il n'existe plus directement dans la portée de foo. Ainsi, puisque tmp est verrouillé lors de la fermeture de bar, tmp sera incrémenté à chaque fois que bar est exécuté, donc lorsque bar est exécuté pour la deuxième et la troisième fois, 17 et 18 sont renvoyés respectivement.
Dans cet exemple, x est juste une valeur pure Lorsque foo est appelé, la valeur x sera copiée dans foo en tant que paramètre.
Mais lorsque JavaScript gère des objets, il utilise toujours des références. Si vous appelez foo avec un objet comme paramètre, alors ce qui est passé dans foo est en fait une référence à l'objet d'origine, donc l'objet d'origine équivaut également à être fermé. . , comme suit :
function foo(x) { var tmp = 3; return function (y) { alert(x + y + tmp++); x.memb = x.memb ? x.memb + 1 : 1; alert(x.memb); } } var age = new Number(2); var bar = foo(age); // bar 现在是个闭包了 bar(10); // alert 15 1 bar(10); // alert 16 2 bar(10); // alert 17 3
Comme prévu, chaque fois que bar(10) est exécuté, non seulement tmp est incrémenté, mais x.memb est également incrémenté, car x dans le corps de la fonction et age en dehors de la fonction font référence au même objet.
via http://stackoverflow.com/questions/111102/how-do-javascript-closures-work
Supplément : Grâce aux exemples ci-dessus, vous devriez être en mesure de comprendre plus clairement les fermetures. Si vous pensez l'avoir compris, vous pouvez tenter de deviner le résultat de l'exécution du code suivant :
function foo(x) { var tmp = 3; return function (y) { alert(x + y + tmp++); x.memb = x.memb ? x.memb + 1 : 1; alert(x.memb); } } var age = new Number(2); var bar1 = foo(age); // bar1 现在是个闭包了 bar1(10); // alert 15 1 bar1(10); // alert 16 2 bar1(10); // alert 17 3 var bar2 = foo(age); // bar2 现在也是个闭包了 bar2(10); // alert ? ? bar2(10); // alert ? ? bar2(10); // alert ? ? bar1(10); // alert ? ? bar1(10); // alert ? ? bar1(10); // alert ? ?
Lorsqu'elles sont utilisées dans la pratique, les fermetures peuvent créer des designs très élégants, permettant de personnaliser les différentes méthodes de calcul définies sur funarg. Voici un exemple de tri par tableau, qui accepte une fonction de condition de tri comme paramètre :
[1, 2, 3].sort(function (a, b) { ... // 排序条件 });
Le même exemple est que la méthode map d'un tableau mappe le tableau d'origine à un nouveau tableau en fonction des conditions définies dans la fonction :
[1, 2, 3].map(function (element) { return element * 2; }); // [2, 4, 6]
À l'aide de paramètres fonctionnels, vous pouvez facilement implémenter une méthode de recherche et prendre en charge des conditions de recherche illimitées :
someCollection.find(function (element) { return element.someProperty == 'searchCondition'; });
Il existe également des fonctions d'application, telles que la méthode commune forEach, qui applique la fonction à chaque élément du tableau :
[1, 2, 3].forEach(function (element) { if (element % 2 != 0) { alert(element); } }); // 1, 3
À propos, les méthodes apply et call des objets fonction peuvent également être utilisées comme fonctions d'application dans la programmation fonctionnelle. Ici, nous les considérons comme des fonctions d'application - des fonctions appliquées aux paramètres (dans apply c'est la liste des paramètres, dans call c'est un paramètre indépendant) :
(function () { alert([].join.call(arguments, ';')); // 1;2;3 }).apply(this, [1, 2, 3]);
Les fermetures ont une autre application très importante : les appels différés :
var a = 10; setTimeout(function () { alert(a); // 10, after one second }, 1000); 还有回调函数: //... var x = 10; // only for example xmlHttpRequestObject.onreadystatechange = function () { // 当数据就绪的时候,才会调用; // 这里,不论是在哪个上下文中创建 // 此时变量“x”的值已经存在了 alert(x); // 10 }; //...
Vous pouvez également créer des étendues d'encapsulation pour masquer les objets d'assistance :
var foo = {}; // 初始化 (function (object) { var x = 10; object.getX = function _getX() { return x; }; })(foo); alert(foo.getX()); // 获得闭包 "x" – 10