786 votes

Qu'est-ce que la portée lexicale ?

Quelle est une brève introduction au scoping lexical ?

100 votes

Dans le podcast 58, Joel encourage les questions de ce genre car il souhaite que SO devienne LE lieu où l'on trouve des réponses, même si elles ont déjà été données ailleurs. Cette question est valable, même si on pourrait la formuler de manière un peu plus polie.

0 votes

Et puis surtout fr.wikipedia.org/wiki/Scope_(programmation) qui contient un bel exemple (Pascal/Delphi) de scopes imbriqués dans des procédures imbriquées.

7 votes

@rahul Je comprends que c'est une vieille question. Mais je suis sûr que même en 2009, SO s'attendait à ce que les demandeurs mettent en quelques efforts de base pour le résoudre. En l'état actuel des choses, il ne montre pas tout effort du tout. C'est peut-être pour cela qu'il a été descendu par beaucoup ?

767voto

AraK Points 38702

Je les comprends à travers des exemples. :)

D'abord, portée lexicale (également appelé champ statique ), dans une syntaxe de type C :

void fun()
{
    int x = 5;

    void fun2()
    {
        printf("%d", x);
    }
}

Chaque niveau intérieur peut accéder à ses niveaux extérieurs.

Il existe un autre moyen, appelé portée dynamique utilisé par la première implémentation de Lisp toujours dans une syntaxe de type C :

void fun()
{
    printf("%d", x);
}

void dummy1()
{
    int x = 5;

    fun();
}

void dummy2()
{
    int x = 10;

    fun();
}

Ici fun peut soit accéder x en dummy1 o dummy2 ou tout autre x dans toute fonction qui appelle fun con x qui y sont déclarés.

dummy1();

imprimera 5,

dummy2();

imprimera 10.

La première est dite statique car elle peut être déduite au moment de la compilation, et la seconde est dite dynamique car la portée externe est dynamique et dépend de l'appel en chaîne des fonctions.

Je trouve le cadrage statique plus facile pour l'œil. La plupart des langages ont fini par suivre cette voie, même Lisp (qui peut faire les deux, non ?). Le scoping dynamique consiste à passer les références de toutes les variables à la fonction appelée.

Pour illustrer pourquoi le compilateur ne peut pas déduire la portée dynamique externe d'une fonction, considérons notre dernier exemple. Si nous écrivons quelque chose comme ceci :

if(/* some condition */)
    dummy1();
else
    dummy2();

La chaîne d'appel dépend d'une condition d'exécution. Si elle est vraie, alors la chaîne d'appels ressemble à ceci :

dummy1 --> fun()

Si la condition est fausse :

dummy2 --> fun()

Le champ d'application extérieur de fun dans les deux cas est l'appelant plus l'appelant de l'appelant et ainsi de suite .

Il faut savoir que le langage C ne permet pas les fonctions imbriquées ni le scoping dynamique.

20 votes

Je voudrais également signaler un tutoriel très facile à comprendre que je viens de trouver. L'exemple d'Arak est sympa, mais peut être trop court pour quelqu'un qui a besoin de plus d'exemples (en fait, en comparant avec d'autres langages ). Jetez-y un coup d'œil. Il est important de comprendre este car ce mot-clé nous permettra de comprendre la portée lexicale. howtonode.org/qu'est-ce que c'est ?

0 votes

Il est bon de noter que gcc possède une extension permettant les fonctions imbriquées.

22 votes

C'est une bonne réponse. Mais la question est étiquetée avec JavaScript . Par conséquent, je pense que cela ne devrait pas être marqué comme la réponse acceptée. La portée lexicale en JS est différente

354voto

Pierre Spring Points 2398

Essayons la définition la plus courte possible :

Portée lexicale définit comment les noms de variables sont résolus dans les fonctions imbriquées : les fonctions internes contiennent la portée des fonctions parentes, même si la fonction parente a retourné l'information. .

C'est tout ce qu'il y a à faire !

43 votes

La dernière partie : "même si la fonction parentale est revenue" est appelée Closure.

8 votes

J'ai compris la portée lexicale et la fermeture en une seule phrase. Merci !

84voto

kta Points 4702
var scope = "I am global";
function whatismyscope(){
   var scope = "I am just a local";
   function func() {return scope;}
   return func;
}

whatismyscope()()

Le code ci-dessus renverra "Je suis juste un local". Il ne retournera pas "Je suis un global". Parce que la fonction func() compte là où elle a été définie à l'origine, c'est-à-dire sous la portée de la fonction whatismyscope.

Il ne se préoccupera pas de ce qui est appelé (la portée globale/à l'intérieur d'une autre fonction même), c'est pourquoi la valeur de la portée globale Je suis global ne sera pas imprimée.

C'est ce que l'on appelle lexical scoping où " Les fonctions sont exécutées en utilisant la chaîne de portée qui était en vigueur lorsqu'elles ont été définies. " - selon le guide de définition du JavaScript.

La portée lexicale est un concept très très puissant.

J'espère que cela vous aidera :)

5 votes

C'est une très bonne explication, je veux ajouter une chose si vous écrivez la fonction func() {return this.scope;} alors elle retournera "je suis global" utilisez juste ce mot-clé et votre scope sera changé.

48voto

Evan Meagher Points 3035

Le cadrage lexical (ou statique) consiste à déterminer la portée d'une variable en se basant uniquement sur sa position dans le corpus textuel du code. Une variable fait toujours référence à son environnement de premier niveau. Il est bon de le comprendre dans par rapport à la portée dynamique.

45voto

La portée définit la zone dans laquelle les fonctions, les variables et autres sont disponibles. La disponibilité d'une variable, par exemple, est définie dans son contexte, c'est-à-dire la fonction, le fichier ou l'objet dans lequel elle est définie. Nous appelons généralement ces variables locales.

La partie lexicale signifie que vous pouvez dériver la portée en lisant le code source.

La portée lexicale est également connue sous le nom de portée statique.

La portée dynamique définit les variables globales qui peuvent être appelées ou référencées de n'importe où après avoir été définies. Elles sont parfois appelées variables globales, même si les variables globales dans la plupart des langages de programmation ont une portée lexicale. Cela signifie que l'on peut déduire de la lecture du code que la variable est disponible dans ce contexte. Peut-être faut-il suivre une clause uses ou includes pour trouver l'instatiation ou la définition, mais le code/compilateur connaît la variable à cet endroit.

En revanche, dans le cas du scoping dynamique, vous cherchez d'abord dans la fonction locale, puis dans la fonction qui a appelé la fonction locale, puis dans la fonction qui a appelé cette fonction, et ainsi de suite, en remontant la pile d'appels. Le terme "dynamique" fait référence au changement, en ce sens que la pile d'appel peut être différente à chaque fois qu'une fonction donnée est appelée, et que la fonction peut donc toucher différentes variables selon l'endroit d'où elle est appelée. (voir aquí )

Pour voir un exemple intéressant de portée dynamique, voir aquí .

Pour plus de détails, voir aquí y aquí .

Quelques exemples en Delphi/Object Pascal

Delphi a une portée lexicale.

unit Main;
uses aUnit;  // makes available all variables in interface section of aUnit

interface

  var aGlobal: string; // global in the scope of all units that use Main;
  type 
    TmyClass = class
      strict private aPrivateVar: Integer; // only known by objects of this class type
                                    // lexical: within class definition, 
                                    // reserved word private   
      public aPublicVar: double;    // known to everyboday that has access to a 
                                    // object of this class type
    end;

implementation

  var aLocalGlobal: string; // known to all functions following 
                            // the definition in this unit    

end.

La paire de fonctions RegisterClass()/GetClass() est ce qui se rapproche le plus de la portée dynamique dans Delphi. Pour son utilisation, voir aquí .

Disons que le moment où RegisterClass([TmyClass]) est appelé pour enregistrer une certaine classe ne peut être prédit par la lecture du code (il est appelé dans une méthode de clic de bouton appelée par l'utilisateur), le code appelant GetClass('TmyClass') obtiendra un résultat ou non. L'appel à RegisterClass() ne doit pas nécessairement se trouver dans le champ lexical de l'unité qui utilise GetClass() ;

Une autre possibilité de portée dynamique est méthodes anonymes (fermetures) dans Delphi 2009, car ils connaissent les variables de leur fonction d'appel. Elles ne suivent pas le chemin d'appel de manière récursive et ne sont donc pas entièrement dynamiques.

2 votes

En fait, le privé est accessible dans toute l'unité où la classe est définie. C'est pourquoi "Strict private" a été introduit en D2006.

3 votes

+1 pour un langage simple (par opposition à un langage compliqué et à des exemples sans grande description)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X