Question Qu'est-ce que le champ d'application lexical?


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


528
2018-06-26 05:10


origine


Réponses:


Je les comprends à travers des exemples. :)

Premièrement, Lexical Scope (également appelé Static Scope), en syntaxe C-like:

void fun()
{
    int x = 5;

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

Chaque niveau interne peut accéder à ses niveaux externes.

Il y a une autre façon, appelée Dynamic Scope, utilisée par la première implémentation de Lisp, encore une fois dans la syntaxe C-like:

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, ou tout x dans toute fonction qui appelle fun avec x déclaré dedans.

dummy1();

va imprimer 5,

dummy2();

va imprimer 10.

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

Je trouve la portée statique plus facile pour l'œil. La plupart des langues sont allées par la suite même Lisp (peut faire les deux, non?). La portée dynamique est similaire à la transmission de références de toutes les variables à la fonction appelée.

Un exemple de 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 c'est vrai, alors la chaîne d'appel ressemble à:

dummy1 --> fun()

Si la condition est fausse:

dummy2 --> fun()

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

Juste pour mentionner que le langage C n'autorise pas les fonctions imbriquées ni la portée dynamique.


547
2018-06-26 05:26



Essayons la définition la plus courte possible:

Champ d'application lexical 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 est retournée.

C'est tout ce qu'on peut en dire! Pour m'aider à comprendre ce que cela signifie, j'ai écrit un article de blog approfondi sur Portée de la fonction et portée lexicale en JavaScript qui peut être trouvé ici. Peut-être que cela pourrait servir quelqu'un d'autre aussi.


197
2018-05-24 12:20



La portée lexicale (statique AKA) fait référence à la détermination de la portée d'une variable uniquement en fonction de sa position dans le corpus textuel du code. Une variable fait toujours référence à son environnement de niveau supérieur. C'est bon de le comprendre relation à la portée dynamique.


33
2018-06-26 05:14



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

La partie lexicale signifie que vous pouvez dériver la portée de la lecture du code source.

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

La portée dynamique définit les variables globales qui peuvent être appelées ou référencées depuis n'importe où après avoir été définies. Parfois, ils sont appelés variables globales, même si les variables globales dans la plupart des langages programmin sont de portée lexicale. Cela signifie qu'il peut être déduit 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, en recherche dynamique, vous effectuez d'abord une recherche dans la fonction locale, puis vous recherchez dans la fonction qui a appelé la fonction locale, puis vous recherchez dans la fonction qui a appelé cette fonction, et ainsi de suite, la pile des appels. "Dynamique" fait référence au changement, dans la mesure où la pile d'appels peut être différente chaque fois qu'une fonction donnée est appelée, et ainsi la fonction peut toucher 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.

Le Delphi le plus proche obtient la portée dynamique est la paire de fonctions RegisterClass () / GetClass (). Pour son utilisation voir ici.

Disons que le temps que RegisterClass ([TmyClass]) est appelé pour enregistrer une certaine classe ne peut pas être prédit 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') un résultat ou non. L'appel à RegisterClass () ne doit pas être dans la portée lexicale de l'unité en utilisant 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. Il ne suit pas le chemin d'appel de manière récursive et n'est donc pas totalement dynamique.


31
2018-06-26 05:22



J'adore les réponses aux questions et aux langues des gens comme @Arak. Depuis que cette question a été taguée JavaScript cependant, je voudrais insérer quelques notes très spécifiques à cette langue.

En javascript, nos choix pour la portée sont:

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

Il convient de noter, je pense, que JavaScript n'a pas vraiment de portée dynamique. .bind ajuste le this mot clé, et c'est proche, mais techniquement pas le même.

Voici un exemple démontrant les deux approches. Vous le faites chaque fois que vous prenez une décision sur la façon d'étendre les rappels, cela s'applique aux promesses, aux gestionnaires d'événements, etc.

Lexical

Voici ce que vous pourriez appeler Lexical Scoping des 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 portée 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`
  }
//...

Ces méthodes sont, autant que je sache, équivalentes sur le plan comportemental.


29
2017-08-27 18:51



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 retournera "Je suis juste un local". Il ne reviendra pas "je suis un global". Parce que la fonction func () compte où est défini à l'origine qui est sous la portée de la fonction whatismyscope.

Il ne dérangera pas de ce qu'il est appelé (la portée globale / à partir de l'intérieur d'une autre fonction même), c'est pourquoi la valeur de portée globale que je suis globale ne sera pas imprimée.

C'est ce qu'on appelle la portée lexicale 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 JavaScript Guide de définition.

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

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


28
2017-11-05 22:28



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 une portée de fonction et ne sont visibles que pour le code qui apparaît dans cette fonction.


9
2017-09-23 10:51



IBM le définit comme:

La partie d'un programme ou d'une unité de segment dans laquelle une déclaration   s'applique. Un identifiant déclaré dans une routine est connu dans ce   routine et dans toutes les routines imbriquées. Si une routine imbriquée déclare   un article avec le même nom, l'article 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();

7
2017-07-03 18:37



Il y a une partie importante de la conversation entourant Lexical et Dynamic Scoping qui manque: une explication simple de la durée de vie de la variable de portée - ou quand la variable peut être accédée.

Le cadrage dynamique ne correspond que très vaguement à la portée «globale» de la façon dont nous y pensons traditionnellement (la raison pour laquelle j'évoque la comparaison entre les deux est qu'il a déjà été mentionné - et je n'aime pas particulièrement le lié l'explication de l'article); il est probablement préférable de ne pas faire la comparaison entre le global et le dynamique - bien que soi-disant, selon l'article lié, "... [il] est utile comme un substitut aux variables à portée globale."

Donc, en anglais, quelle est la distinction importante entre les deux mécanismes de cadrage?

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

Cependant - comme ce n'est pas l'objectif de l'OP - la portée dynamique n'a pas reçu beaucoup d'attention et l'attention qu'elle a reçue signifie qu'elle a probablement besoin d'un peu plus (ce n'est pas une critique d'autres réponses, mais plutôt cette réponse fait nous souhaitons il y avait un peu plus "). Alors, voici un peu plus:

La portée dynamique signifie qu'une variable est accessible au programme plus grand pendant la durée de l'appel de fonction - ou pendant l'exécution de la fonction. Vraiment, Wikipedia fait un bon travail avec le 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 étendue dynamique), si la portée d'un nom de variable est   certaine fonction, alors sa portée est la période de temps au cours de laquelle la   La fonction est en cours d'exécution: lorsque la fonction est en cours d'exécution, la variable   nom existe, et est lié à sa variable, mais après la fonction   renvoie, le nom de la variable n'existe pas.


4
2018-02-22 13:22



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

Regardez comment la portée lexicale fonctionne en Lisp si vous voulez plus de détails. La réponse sélectionnée par Kyle Cronin dans Variables dynamiques et lexicales dans Common Lisp est beaucoup plus clair que les réponses ici.

Par coïncidence, je n'ai appris cela que dans une classe Lisp, et cela s'applique également à 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 

2
2017-09-15 14:38



Voici un angle différent sur cette question que nous pouvons obtenir en prenant du recul et en regardant le rôle de la portée dans le cadre plus large de l'interprétation (exécution d'un programme). En d'autres termes, imaginez que vous construisiez un interpréteur (ou un compilateur) pour une langue et que vous étiez responsable du calcul de la sortie, étant donné un programme et une entrée.

L'interprétation implique le suivi de trois choses:

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

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

3) L'environnement dans lequel une opération donnée s'exécute - à savoir, la projection de l'état sur une opération.

Un interprète démarre à 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 façon dont vous calculez l'environnement pour toute opération se fait à travers un ensemble formel de règles définies par le langage de programmation. Le terme "liaison" est fréquemment utilisé pour décrire la cartographie de l'état global du programme à une valeur dans l'environnement. Notez que par "état général" nous ne voulons pas dire l'état global, mais plutôt la somme totale de chaque définition accessible, à n'importe quel point de l'exécution.

C'est le cadre dans lequel le problème de portée est défini. Maintenant à la prochaine partie de ce que nos options sont.

  • En tant qu'interprète 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 avec les effets de cette opération appliquée, 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 le sens de portée dynamique, dans lequel l'environnement dans lequel s'exécute un code est lié à l'état du programme tel que défini par son contexte d'exécution.

  • Ou, vous pourriez penser à un programmeur utilisant votre langue et simplifier sa tâche de garder la trace des valeurs qu'une variable peut prendre. Il y a beaucoup trop de chemins et trop de complexité impliqués dans le raisonnement sur le résultat de la totalité de l'exécution passée. Champ d'application lexical aide à faire cela en limitant l'environnement actuel à la partie de l'état défini dans le bloc courant, la fonction ou autre unité de portée, et son parent (c'est-à-dire le bloc englobant l'horloge actuelle, ou la fonction qui a appelé la fonction actuelle).

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


0
2018-04-25 18:46