Visit Mozilla.org

Le principe de fermeture en JavaScript:Résolution d'identifiants, contextes d'exécution et visibilité des variables

Un article de MDC.

Sommaire

[modifier] Le contexte d'exécution

Le contexte d'exécution est un concept utilisé dans la spécification ECMAScript (ECMA 262 3e Edition) pour définir le comportement que doivent suivre les implémentations d'ECMAScript. La spécification ne précise en rien comment les contextes d'exécution doivent être implémentés, mais les contextes d'exécution disposent d'attributs qui font référence à des structures définies par la spécification qui laissent à penser qu'ils peuvent être conçus (et même implémentés) comme objets avec des propriétés, ces dernières cependant non publiques.

Tout code javascript est exécuté dans un contexte d'exécution. Le code global (c'est-à-dire exécuté au chargement d'un fichier JS ou d'une page HTML) est exécuté dans ce que j'appellerai un contexte d'exécution global, et chaque invocation de fonction (potentiellement comme constructeur) possède un contexte d'éxecution distinct associé. Le code exécuté avec la fonction eval obtient également un contexte d'exécution dédié, mais comme eval n'est normalement jamais employé par les programmeurs javascript je n'en parlerai pas ici. Les spécifications détaillées des contextes d'exécution peuvent être trouvées dans la section 10.2 de ECMA 262 (3e édition).

Lorsqu'une fonction est appelée elle entre dans un contexte d'exécution, si une autre fonction est appelée (ou la même fonction, de façon récursive) un nouveau contexte d'exécution est créé et l'exécution a lieu dans ce contexte pendant la durée de l'appel de fonction, retournant au contexte d'exécution initial lorsque la fonction retourne la main. Ainsi, le code javascript en cours d'exécution forme une pile de contextes d'exécution.

Lorsqu'un contexte d'exécution est créé un certain nombre de choses se passent dans un certain ordre. Tout d'abord, dans le contexte d'exécution d'une fonction, un objet "Activation" est créé. Cet objet activation constitue un autre mécanisme de la spécification. Il peut être considéré comme un objet car il offre au final des propriétés nommées accessibles, mais ce n'est pas un objet normal car il n'a pas de prototype (ou, tout du moins, pas de prototype défini) et ne peut être directement référencé par du code javascript.

L'étape suivante dans la création du contexte d'exécution pour un appel de fonction consiste à la création de l'objet arguments, une sorte d'objet stylé tableau avec des membres indexés par entiers positifs correspondant, dans l'ordre, aux arguments passés lors de l'appel de fonction. Cet objet possède des propriétés length et callee (qui ne sont pas pertinentes pour cette discussion, voir la spec pour les détails). Une propriété de l'objet Activation est créée avec le nom "arguments" et une référence vers l'objet arguments est assignée à cette propriété.

Ensuite, le contexte d'exécution reçoit une portée (scope). Une portée consiste en une liste (ou chaîne) d'objets. Chaque objet fonction possède une propriété interne scope (que nous allons discuter plus en détail plus bas) qui consiste également en une liste (ou chaîne) d'objets. La portée assignée au contexte d'exécution de l'appel de fonction est la liste référencée par la propriété scope de l'objet fonction correspondant, avec l'objet Activation inséré au début de la chaîne (ou en haut de la liste).

Ensuite le processus d'instantiation des variables entre en jeu, utilisant un objet appelé objet "Variable" par ECMA 262. Cependant, l'objet Activation est utilisé comme objet Variable (il est important de noter que ce sont le même objet). Des propriétés nommées de l'objet Variable sont créées pour chaque paramètre nommément identifié de la fonction, et si les arguments passés lors de l'appel de fonction correspondent à ces paramètres, les valeurs de ces arguments sont assignées aux propriétés (sinon la valeur assignée est undefined). Les définitions de fonctions imbriquées sont ensuite utilisées pour créer des objets fonction qui sont assignés à des propriétés de l'objet Variable avec des noms correspondant au nom utilisé dans la déclaration de la fonction. La dernière étape de l'instantiation des variables consiste à créer des propriétés nommées de l'objet Variable qui correspondent à toutes les variables locales déclarées dans la fonction.

Les propriétés créées sur l'objet Variable, correspondant aux variables locales déclarées, reçoivent tout d'abord des valeurs undefined pendant l'instantiation des variables, l'initialisation réelle des variables locales n'a lieu qu'avec l'évaluation des expressions d'assignation correspondantes pendant l'exécution du corps de la fonction.

C'est le fait que l'objet Activation, avec sa propriété arguments, et l'objet Variable, avec ses propriétés nommées correspondant aux variables locales, sont le même objet qui permet à l'identifiant arguments d'être traité comme une variable locale de la fonction.

Enfin une valeur est assignée pour utilisation avec le mot-clé this. Si la valeur assignée référence un objet alors les accesseurs de propriété préfixés avec le mot-clé this référencent les propriétés de cet objet. Si la valeur assignée (en interne) est null alors le mot-clé this référencera l'objet global.

Le contexte d'exécution global reçoit un traitement légèrement différent, n'ayant pas d'arguments il n'a pas besoin d'un objet Activation défini pour les référencer. Le contexte d'exécution global a cependant besoin d'une portée et sa chaîne de portée consiste en exactement un objet, l'objet global. Le contexte d'exécution global suit ensuite le processus d'instantiation des variables, ses fonctions imbriquées n'étant simplement que les déclarations des fonctions globales qui constitue le gros du code javascript. L'objet global est utilisé comme objet Variable, ce qui explique pourquoi les fonctions déclarées globalement sont devenues des propriétés de l'objet global, comme n'importe quelle variable globale.


Le contexte d'exécution utilise une référence vers l'objet global comme objet this.

[modifier] Visibilité des variables

[modifier] Résolution d'identifiants

< La résolution des noms de propriété sur les objets | Fermetures >