web-dev-qa-db-fra.com

Quelle est la portée lexicale?

Quelqu'un pourrait-il me donner une brève introduction à la portée lexicale?

575
Subba Rao

Je les comprends à travers des exemples. :)

Tout d'abord, Lexical Scope (également appelé Static Scope), en 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é Dynamic Scope, utilisé par la première mise en œuvre de LISP, à nouveau dans la syntaxe en 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 dans dummy1 ou dummy2, soit à toute x dans une fonction qui appelle fun avec x déclaré.

dummy1();

imprimera 5,

dummy2();

imprimera 10.

Le premier est appelé statique car il peut être déduit lors de la compilation, le second est appelé dynamique car la portée externe est dynamique et dépend de l'appel en chaîne des fonctions.

Je trouve la portée statique plus facile pour les yeux. La plupart des langues sont allées de cette façon éventuellement même LISP (peut faire les deux, n'est-ce pas?). La portée dynamique est comme passer des références de toutes les variables à la fonction appelée.

Un exemple de la raison pour laquelle le compilateur ne peut pas déduire l'étendue 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'appels dépend d'une condition d'exécution. Si c'est vrai, la chaîne d'appels ressemble à ceci:

dummy1 --> fun()

Si la condition est fausse:

dummy2 --> fun()

La portée externe de fun dans les deux cas est l'appelant plus l'appelant de l'appelant et ainsi de suite .

Il suffit de mentionner que le langage C ne permet pas les fonctions imbriquées ni la portée dynamique.

599
AraK

Essayons la définition la plus courte possible:

Portée lexicale définit la façon dont les noms de variable sont résolus dans les fonctions imbriquées: les fonctions internes contiennent la portée des fonctions parent même si la fonction parent a renvoyé .

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

222
Pierre Spring
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 ne suis qu'un local". Il ne reviendra pas "je suis un global". Parce que la fonction func () compte où est initialement défini ce qui est dans la portée de la fonction whatismyscope.

Il ne s'embarrassera pas de quoi que ce soit appelé (la portée globale/même dans une autre fonction), c'est pourquoi la valeur de portée globale Je suis globale ne sera pas imprimée.

C'est ce que nous appelons la portée lexicale, où " les fonctions sont exécutées à l'aide de la chaîne d'étendue qui était en vigueur au moment où elles ont été définies " - conformément au guide de définition JavaScript.

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

J'espère que cela t'aides..:)

39
kta

La portée définit la zone, où les fonctions, les variables et autres sont disponibles. La disponibilité d'une variable, par exemple, est définie dans son contexte, par exemple la fonction, le fichier ou l'objet, ils sont définis dans. Nous appelons généralement ces variables locales.

La partie lexicale signifie que vous pouvez déduire l'étendue de la lecture du code source. 

La portée lexicale est également appelée portée statique.

La portée dynamique définit les variables globales pouvant être appelées ou référencées à partir de n’importe où après leur définition. Parfois, elles sont appelées variables globales, même si les variables globales dans la plupart des langages programmin ont une portée lexicale. Cela signifie que cela peut être déduit de la lecture du code indiquant 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 est au courant de la variable à cet endroit.

En dynamique, par contre, vous recherchez d'abord la fonction locale, puis la fonction qui a appelé la fonction locale, puis la fonction qui a appelé cette fonction, et ainsi de suite, jusqu'à la pile d'appels. "Dynamique" fait référence au changement, en ce sens que la pile d'appels peut être différente chaque fois qu'une fonction donnée est appelée, de sorte que la fonction peut frapper différentes variables en fonction de l'endroit d'où elle est appelée. (voir ici )

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

Pour plus de détails, voir ici et ici .

Quelques exemples dans 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.

Delphi se rapproche le plus de la portée dynamique de la paire de fonctions RegisterClass ()/GetClass (). Pour son utilisation, voir ici .

Supposons que l'heure à laquelle RegisterClass ([TmyClass]) est appelée pour enregistrer une certaine classe ne peut pas être prédite en lisant le 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 de RegisterClass () ne doit pas nécessairement figurer dans la portée lexicale de l'unité à l'aide de GetClass ();

Les méthodes anonymes (fermetures) de Delphi 2009 sont une autre possibilité pour la portée dynamique, car elles connaissent les variables de leur fonction d'appel. Il ne suit pas le chemin d'appel de manière récursive à partir de là et n'est donc pas totalement dynamique.

37

La portée lexicale (statique AKA) consiste à déterminer la portée d'une variable en se basant uniquement sur sa position dans le corpus de code. Une variable fait toujours référence à son environnement de niveau supérieur. Il est bon de le comprendre dans relation avec la portée dynamique.

36
Evan Meagher

J'adore les réponses très détaillées et dépourvues de langage, de gens comme @Arak. Cependant, depuis que cette question a été marquée JavaScript, j'aimerais ajouter des notes très spécifiques à ce langage.

En javascript, nos choix en matière de cadrage sont les suivants:

  • tel quel (pas d'ajustement de la portée)
  • lexical var _this = this; function callback(){ console.log(_this); }
  • callback.bind(this) lié

Je pense que cela vaut la peine de noter que JavaScript n’a pas vraiment de portée dynamique . .bind ajuste le mot clé this, et c'est proche, mais techniquement pas identique.

Voici un exemple illustrant les deux approches. Vous faites cela à chaque fois que vous prenez une décision sur la manière de définir l'étendue des rappels; cela s'applique donc aux promesses, aux gestionnaires d'événements, etc.

Lexical

Voici ce que vous pourriez appeler Lexical Scoping de rappels en JavaScript:

var downloadManager = {
  initialize: function() {
    var _this = this; // Set up `_this` for lexical access
    $('.downloadLink').on('click', function () {
      _this.startDownload();
    });
  },
  startDownload: function(){
    this.thinking = true;
    // request the file from the server and bind more callbacks for when it returns success or failure
  }
  //...
};

Lié

Une autre façon de faire est d’utiliser Function.prototype.bind :

var downloadManager = {
  initialize: function() {
    $('.downloadLink').on('click', function () {
      this.startDownload();
    }.bind(this)); // create a function object bound to `this`
  }
//...

Autant que je sache, ces méthodes ont un comportement équivalent.

31
SimplGy

IBM le définit comme suit:

La partie d’un programme ou d’un segment dans laquelle une déclaration s'applique. Un identifiant déclaré dans une routine est connu dans ce domaine routine et dans toutes les routines imbriquées. Si une routine imbriquée déclare un élément portant le même nom, l'élément externe n'est pas disponible dans le routine imbriquée.

Exemple 1:

function x() {
    /*
    Variable 'a' is only available to function 'x' and function 'y'.
    In other words the area defined by 'x' is the lexical scope of
    variable 'a'
    */
    var a = "I am a";

    function y() {
        console.log( a )
    }
    y();

}
// outputs 'I am a'
x();

Exemple 2:

function x() {

    var a = "I am a";

    function y() {
         /*
         If a nested routine declares an item with the same name,
         the outer item is not available in the nested routine.
         */
        var a = 'I am inner a';
        console.log( a )
    }
    y();

}
// outputs 'I am inner a'
x();
12
Robert Rocha

Portée lexicale: Les variables déclarées en dehors d'une fonction sont des variables globales et sont visibles partout dans un programme JavaScript. Les variables déclarées à l'intérieur d'une fonction ont la portée de la fonction et ne sont visibles que par le code qui apparaît à l'intérieur de cette fonction.

11
Stan

Il manque une partie importante de la conversation entourant Lexical et Dynamic Scoping: une explication simple de la durée de vie de la variable étendue - ou de quand la variable est accessible. 

La portée dynamique ne correspond que très vaguement à la portée "globale" dans la façon dont nous y pensons traditionnellement (la raison pour laquelle j'ai évoqué la comparaison entre les deux, c'est qu'elle a déjà été mentionnée - et je n'aime pas particulièrement l'explication de l'article lié ); il est probablement préférable de ne pas faire de comparaison entre global et dynamique - bien que, supposément, selon l'article lié, "... [soit] utile pour remplacer les variables globales".

Ainsi, en termes simples, quelle est la distinction importante entre les deux mécanismes de portée?

La portée lexicale a été très bien définie dans les réponses ci-dessus: les variables à portée lexicale sont disponibles - ou accessibles - au niveau local de la fonction dans laquelle elles ont été définies.

Cependant - comme ce n’est pas l’objet du PO - la portée dynamique n’a pas retenu beaucoup d’attention, ce qui signifie qu’elle a probablement besoin d’un peu plus (ce n’est pas une critique d’autres réponses, mais plutôt un "oh, cette réponse fait que nous souhaitons qu’il y en ait un peu plus "). Alors, voici un peu plus:

La portée dynamique signifie qu'une variable est accessible au programme le plus important pendant la durée de l'appel de la fonction - ou pendant l'exécution de la fonction. En réalité, Wikipedia fait du bon travail avec l'explication de la différence entre les deux. Pour ne pas l’obscurcir, voici le texte qui décrit la portée dynamique:

... [I] n portée dynamique (ou portée dynamique), si la portée d'un nom de variable est un certaine fonction, sa portée est la période pendant laquelle le la fonction est en cours d'exécution: pendant que la fonction est en cours d'exécution, la variable name existe et est lié à sa variable, mais après la fonction retourne, le nom de la variable n'existe pas.

4
Thomas

Une portée lexicale en Javascript signifie qu'une variable définie en dehors d'une fonction peut être accessible dans une autre fonction définie après la déclaration de la variable. Mais l'inverse n'est pas vrai, les variables définies dans une fonction ne seront pas accessibles en dehors de cette fonction.

Ce concept est très utilisé dans les fermetures en Javascript.

Disons que nous avons le code ci-dessous.

var x = 2;
var add = function() {
var y = 1;
return x + y;
};

Maintenant, lorsque vous appelez add () -> cela affichera 3.

Donc, la fonction add () accède à la variable globale x qui est définie avant la fonction de méthode add. Ceci est appelé en raison de la portée lexicale en javascript.

3
Praveen Kishor

Portée lexicale signifie qu'une fonction recherche les variables dans le contexte dans lequel elle a été définie, et non dans la portée qui l'entoure immédiatement. 

Regardez comment fonctionne la portée lexicale dans LISP si vous voulez plus de détails. La réponse choisie par Kyle Cronin dans Variables dynamiques et lexicales dans Common LISP est beaucoup plus claire que les réponses fournies ici. 

Par coïncidence, je n’ai appris cela que dans un cours LISP, et cela s’applique également dans JS.

J'ai couru ce code dans la console de chrome.

// javascript               equivalent LISP
var x = 5;                //(setf x 5)
console.debug(x);         //(print x)
function print_x(){       //(defun print-x () 
    console.debug(x);     //    (print x)
}                         //)
(function(){              //(let  
    var x = 10;           //    ((x 10))
    console.debug(x);     //    (print x)
    print_x();            //    (print-x)
})();                     //)

sortie:

5
10
5 
3
ratiotile

Voici un angle différent sur cette question que nous pouvons obtenir en prenant du recul et en examinant le rôle de la définition de la portée dans le cadre plus large de l’interprétation (gestion d’un programme). En d'autres termes, imaginez que vous construisiez un interprète (ou un compilateur) pour un langage et que vous étiez responsable du calcul du résultat, à partir d'un programme et de certaines entrées.

L'interprétation implique de garder trace de trois choses:

1) Etat - à savoir les variables et les emplacements mémoire référencés sur le tas et la pile.

2) Opérations sur cet état - à savoir, chaque ligne de code de votre programme

3) L'environnement dans lequel une opération donnée se déroule, à savoir la projection de l'état d'une opération. 

Un interprète commence à la première ligne de code d'un programme, calcule son environnement, exécute la ligne dans cet environnement et capture son effet sur l'état du programme. Il suit ensuite le flux de contrôle du programme pour exécuter la ligne de code suivante et répète le processus jusqu'à la fin du programme.

La manière dont vous calculez l'environnement pour toute opération passe par un ensemble de règles formelles définies par le langage de programmation. Le terme "liaison" est fréquemment utilisé pour décrire le mappage de l'état général du programme sur une valeur de l'environnement. Notez que par "état global", nous ne voulons pas dire état global, mais plutôt la somme totale de chaque définition accessible, à n'importe quel moment de l'exécution)

C'est le cadre dans lequel le problème de portée est défini. Passons maintenant à la partie suivante de nos options.

  • En tant qu'implémenteur de l'interpréteur, vous pouvez simplifier votre tâche en rendant l'environnement aussi proche que possible de l'état du programme. En conséquence, l’environnement d’une ligne de code serait simplement défini par l’environnement de la ligne de code précédente, auquel seront appliqués les effets de cette opération, que la ligne précédente soit une affectation, un appel de fonction, un retour de fonction, ou une structure de contrôle telle qu'une boucle while.

C’est l’essentiel de dynamic scoping , dans lequel l’environnement dans lequel tout code est exécuté est lié à l’état du programme, tel que défini par son contexte d’exécution.

  • Ou, vous pourriez penser à un programmeur utilisant votre langage et simplifier sa tâche de garder une trace des valeurs qu'une variable peut prendre. Il y a beaucoup trop de chemins et de complexité dans le raisonnement sur le résultat final de l'exécution passée. Portée lexicale facilite cette tâche en limitant l'environnement actuel à la partie de l'état définie dans le bloc, la fonction ou une autre unité de la portée actuels, ainsi qu'à son parent (le bloc contenant fonction qui a appelé la fonction actuelle).

En d'autres termes, avec Portée lexicale , l'environnement que tout code voit est lié à un état associé à une portée définie explicitement dans le langage, tel qu'un bloc ou une fonction.

0
er0

En langage simple, la portée lexicale est une variable définie en dehors de votre portée ou la portée supérieure est automatiquement disponible dans votre portée, ce qui vous évite de la transmettre.

Ex:

let str="JavaScript";

const myFun = () => {
    console.log(str);
}

myFun();

// Sortie: JavaScript

0
Sky

J'apprends normalement par exemple, voici un petit quelque chose:

const lives = 0;

function catCircus () {
    this.lives = 1;
    const lives = 2;

    const cat1 = {
        lives: 5,
        jumps: () => {
            console.log(this.lives);
        }
    };
    cat1.jumps(); // 1
    console.log(cat1); // { lives: 5, jumps: [Function: jumps] }

    const cat2 = {
        lives: 5,
        jumps: () => {
            console.log(lives);
        }
    };
    cat2.jumps(); // 2
    console.log(cat2); // { lives: 5, jumps: [Function: jumps] }

    const cat3 = {
        lives: 5,
        jumps: () => {
            const lives = 3;
            console.log(lives);
        }
    };
    cat3.jumps(); // 3
    console.log(cat3); // { lives: 5, jumps: [Function: jumps] }

    const cat4 = {
        lives: 5,
        jumps: function () {
            console.log(lives);
        }
    };
    cat4.jumps(); // 2
    console.log(cat4); // { lives: 5, jumps: [Function: jumps] }

    const cat5 = {
        lives: 5,
        jumps: function () {
            var lives = 4;
            console.log(lives);
        }
    };
    cat5.jumps(); // 4
    console.log(cat5); // { lives: 5, jumps: [Function: jumps] }

    const cat6 = {
        lives: 5,
        jumps: function () {
            console.log(this.lives);
        }
    };
    cat6.jumps(); // 5
    console.log(cat6); // { lives: 5, jumps: [Function: jumps] }

    const cat7 = {
        lives: 5,
        jumps: function thrownOutOfWindow () {
            console.log(this.lives);
        }
    };
    cat7.jumps(); // 5
    console.log(cat7); // { lives: 5, jumps: [Function: thrownOutOfWindow] }
}

catCircus();
0
Karl Morrison

Portée lexicale signifie que dans un groupe imbriqué de fonctions, le .__ intérieur. les fonctions ont accès aux variables et autres ressources de leur portée parent. Cela signifie que les fonctions enfants sont liées lexicalement au contexte d'exécution de leurs parents. La portée lexicale est parfois également appelé champ d'application statique.

function grandfather() {
    var name = 'Hammad';
    // likes is not accessible here
    function parent() {
        // name is accessible here
        // likes is not accessible here
        function child() {
            // Innermost level of the scope chain
            // name is also accessible here
            var likes = 'Coding';
        }
    }
}

Ce que vous remarquerez à propos de la portée lexicale est que cela fonctionne forward, le nom signifiant est accessible par l'exécution de ses enfants contextes. Mais cela ne fonctionne pas en arrière pour ses parents, ce qui signifie que la variable aime n'a pas accès aux parents. Cela dit aussi nous que les variables ayant le même nom dans différents contextes d’exécution obtenir la priorité de haut en bas de la pile d'exécution. Une variable, avoir un nom semblable à une autre variable, dans la fonction la plus profonde (le contexte le plus élevé de la pile d'exécution) aura une priorité plus élevée.

Notez que ceci est pris de ici

0
Shivendra Gupta