Vous êtes nouveau sur Developpez.com ? Créez votre compte ou connectez-vous afin de pouvoir participer !

Vous devez avoir un compte Developpez.com et être connecté pour pouvoir participer aux discussions.

Vous n'avez pas encore de compte Developpez.com ? Créez-en un en quelques instants, c'est entièrement gratuit !

Si vous disposez déjà d'un compte et qu'il est bien activé, connectez-vous à l'aide du formulaire ci-dessous.

Identifiez-vous
Identifiant
Mot de passe
Mot de passe oublié ?
Créer un compte

L'inscription est gratuite et ne vous prendra que quelques instants !

Je m'inscris !

Les principales propriétés des fermetures

Le , par yahiko

0PARTAGES

Cet article qui fait suite au précédent qui présentait une facette particulière des fermetures, tente de remettre certains concepts-clé au sujet des fermetures à leur place afin d'éviter les erreurs de compréhension les plus grossières.

Toute fonction en JavaScript crée une fermeture.

Il est courant de lire sur Internet que les fermetures ne concernent que les fonctions anonymes. C'est faux. Il s'agit d'une confusion manifeste entre le concept de lambda expression et celui de fermeture. Une fonction anonyme est une lambda expression dans le sens de la programmation fonctionnelle. En JavaScript, n'importe quelle fonction, anonyme ou explicitement nommée, créé une fermeture.

Toute fonction en JavaScript peut servir de paramètre.

Le fait que toute fonction en JavaScript crée une fermeture découle simplement du fait qu'une fonction en JavaScript est une expression de première classe, et pour être plus précis, c'est un objet { ... }, donc sur le même pied d'égalité par exemple qu'un nombre ou une chaîne de caractère, pouvant être passé en paramètre à d'autres fonctions ou pouvant être défini à l'intérieur d'autres fonctions. C'est d'ailleurs cette propriété qui fait de JavaScript un langage fonctionnel. Et tout objet de type fonction qui est créé, hérite des propriétés liées aux fonctions (apply, call, bind, etc).

Code javascript : Sélectionner tout
1
2
3
function fct() { 
    ... 
}
Exemple 1

Code javascript : Sélectionner tout
1
2
3
var fct = function () { 
    ... 
}
Exemple 2

Les deux constructions des deux exemples ci-dessus sont sémantiquement équivalentes. Il est possible de définir une fonction nommément ou bien, d'initialiser par une fonction anonyme une variable dont le nom pourra servir à appeler la fonction. Dans les deux cas, une fermeture est associée.

Les variables locales à une fonction ne sont pas directement accessibles en dehors de ladite fonction

Le fait qu'une variable définie à l'intérieur d'une fonction ne soit pas directement accessible à l'extérieur de cette fonction n'est pas propre à JavaScript. C'est l'usage dans la plupart des langages que je qualifierai de moderne. On parle d'encapsulation des variables locales.

Code javascript : Sélectionner tout
1
2
3
4
function() fct { 
    var x = 13; 
} 
alert(x); // affiche undefined
Exemple 3

Dans l'exemple ci-dessus, la variable locale x n'est pas directement accessible par le contexte englobant, d'où l'affichage de undefined.

Cependant, ce mécanisme d'encapsulation des variables, couplé à celui de la fermeture autorisant la fonction à accéder aux variables du contexte englobant (cf. propriété suivante), engendre une notion de hiérarchie, de niveaux, dans la visibilité qu'ont les variables au sein des fonctions. On parle de hiérarchie de la portée lexicale.

Code javascript : Sélectionner tout
1
2
3
4
5
6
7
var x = 7; 
function exterieure() { 
    var y = 10; 
    function interieure() { 
    var z = 24; 
    } 
}
Exemple 4

Dans l'exemple ci-dessus, la variable x est globale et sa portée lexicale (sa visibilité) est également globale. Elle est visible aussi bien dans la fonction exterieure() que dans la fonction interieure().
La variable y est visible dans la fonction exterieure() évidemment puisqu'elle y est définie, elle y est visible dans la fonction interieure() puisque celle-ci est imbriquée dans exterieure(), mais la variable y n'est pas visible dans le contexte global.
Enfin la variable z n'est visible, accessible, uniquement dans la fonction interieure().

Une fermeture peut rattacher des variables libres (externes) à son contexte d'exécution.

Le fait qu'une fermeture puisse capturer des variables externes (dans la limite de sa portée lexicale comme vu précédemment), est la propriété la plus importante d'une fermeture.

Code javascript : Sélectionner tout
1
2
3
4
5
6
function modify() { 
    x = 127; 
} 
var x = 314; 
modify(); 
alert(x); // affiche 127
Exemple 5

Dans l'exemple ci-dessus, dans la fonction modify() la variable x en l'absence du mot-clé var est une variable libre ou externe. Bien qu'initialisée postérieurement, la variable globale x est donc rattachée à la fermeture ce qui permet à celle-ci de modifier la valeur de la variable globale x.

Si nous avions fait précéder du mot-clé var la variable x dans la fonction modify(), cette variable serait devenue locale et l'appel à la fonction modify() n'aurait pas modifié la variable globale x.

Code javascript : Sélectionner tout
1
2
3
4
5
6
function modify() { 
    var x = 127; 
} 
var x = 314; 
modify(); 
alert(x); // affiche 314
Exemple 6

Cependant, il faut avoir à l'esprit que la fonction modify() a toujours accès aux variables globales puisque rattachées à l'objet window, accessible à tout moment.

Code javascript : Sélectionner tout
1
2
3
4
5
6
7
function modify() { 
    var x = 127; 
    window.x = 504; 
} 
var x = 314; 
modify(); 
alert(x); // affiche 504
Exemple 7

Le lien créé par une fermeture avec une variable externe est persistant.

Lorsqu'une fermeture capture une variable externe définie dans une fonction englobante, cette variable reste accessible à l'intérieur de la fermeture, même lorsque le contexte d'exécution de la fonction englobante concernée est détruit. Autrement dit, même lorsque la fonction englobante se termine (par exemple via un return), ses variables locales capturées par une fermeture restent présentes en mémoire et accessibles à l'intérieur de la fermeture.

Code javascript : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
function exterieure() { 
    var x = 10; 
    function interieure() { 
        alert(x); 
    } 
  
    return interieure; 
} 
var fct = exterieure(); 
fct(); // affiche 10
Exemple 8

Dans l'exemple ci-dessus, malgré le fait que l'appel à la fonction interieure(), par le biais de la variable fct, a lieu après que la fonction englobante exterieure() ait terminé son exécution, la variable x, locale à la fonction exterieure(), existe toujours avec la valeur attendue, ici 10.

Ce mécanisme découle du mode de fonctionnement du ramasse-miettes (garbage collector). En l'absence de référence sur la variable x, celle-ci est normalement détruite (libération de la mémoire allouée) par le ramasse-miette une fois la fonction exterieure() terminée. Or, puisque que la fermeture de la fonction interieure() crée un lien persistant avec cette variable x, celle-ci ne sera pas détruite par le ramasse-miette, en tout cas, pas tant que la fonction interieure() sera susceptible d'être appelée. D'où d'ailleurs la mauvaise réputation des fermetures en terme de fuite mémoire selon les navigateurs et leur ramasse-miettes. C'est le revers de la médaille en quelque sorte.

Une fermeture capture des variables et non des valeurs

Comme ce point a déjà été illustré dans un post précédent, j'invite le lecteur à s'y reporter.

Conclusion

Les fermetures sont un mécanisme relativement basique et ne sont finalement que la conséquence de la possibilité d'imbriquer une fonction dans une autre.

Les fonctions en JavaScript, selon la norme actuelle ECMAScript 5 en tout cas, sont le seul moyen de créer dynamiquement des contextes d'exécution. Et du fait de leurs liens étroits avec les fermetures en JavaScript, beaucoup de constructions syntaxiques plus ou moins sophistiquées reposent sur elles. Notamment, les fonctions auxiliaires (ou callback en anglais), les Immediately-Invoked Function Expressions (IIFEs), les espaces de nommage ou module ou encore la programmation orientée objet qui ne sont que des conséquences directes des propriétés des fonctions et des fermetures.

Une erreur dans cette actualité ? Signalez-le nous !