Qu'est-ce qu'une brève introduction à la portée lexicale?
javascript
scoping
lexical-scope
Subba Rao
la source
la source
Réponses:
Je les comprends à travers des exemples. :)
Tout d'abord, la portée lexicale (également appelée portée statique ), dans une syntaxe de type C:
Chaque niveau intérieur peut accéder à ses niveaux extérieurs.
Il existe une autre façon, appelée portée dynamique utilisée par la première implémentation de Lisp , toujours dans une syntaxe de type C:
Ici, vous
fun
pouvez accéderx
àdummy1
oudummy2
, ou à n'importex
quelle fonction qui appellefun
avecx
déclaré en elle.imprimera 5,
imprimera 10.
Le premier est appelé statique car il peut être déduit au moment de la compilation, et 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 l'œil. La plupart des langues ont fini par suivre cette voie, même Lisp (peut faire les deux, non?). La portée dynamique revient à transmettre 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 ça:
La chaîne d'appel dépend d'une condition d'exécution. Si c'est vrai, alors la chaîne d'appel ressemble à:
Si la condition est fausse:
La portée externe de
fun
dans les deux cas est l'appelant plus l'appelant de l'appelant et ainsi de suite .Juste pour mentionner que le langage C ne permet pas les fonctions imbriquées ni la portée dynamique.
la source
JavaScript
. Par conséquent, je pense que cela ne devrait pas être marqué comme la réponse acceptée. La portée lexicale spécifiquement dans JS est différentefor
boucle est le problème typique. La portée lexicale de JavaScript est uniquement au niveau de la fonction, sauf si l'ES6let
ouconst
est utilisé.Essayons la définition la plus courte possible:
La 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 est retournée .
C'est tout ce qu'il y a à faire!
la source
Le code ci-dessus renverra "Je suis juste 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 le cadre de la fonction whatismyscope.
Cela ne dérangera pas quel que soit son nom (la portée globale / même dans une autre fonction), c'est pourquoi la valeur de la portée globale Je suis global 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 au moment de leur définition » - selon le guide de définition JavaScript.
La portée lexicale est un concept très très puissant.
J'espère que cela t'aides..:)
la source
La portée lexicale (AKA statique) 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. Il est bon de le comprendre par rapport à la portée dynamique.
la source
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, 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 portée statique.
La portée dynamique définit des variables globales qui peuvent être appelées ou référencées de n'importe où après avoir été définies. Parfois, elles sont appelées variables globales, même si les variables globales dans la plupart des langages de programmation sont de portée lexicale. Cela signifie, il peut être dérivé de la lecture du code que la variable est disponible dans ce contexte. Peut-être qu'il faut suivre une clause uses ou includes pour trouver l'instatiation ou la définition, mais le code / compilateur connaît la variable à cet endroit.
Dans la portée dynamique, en revanche, vous recherchez d'abord 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, dans 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 donc la fonction peut frapper différentes variables selon 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 en Delphi / Object Pascal
Delphi a une portée lexicale.
Delphi se rapproche le plus de la portée dynamique est la paire de fonctions RegisterClass () / GetClass (). Pour son utilisation voir ici .
Disons que l'heure à laquelle RegisterClass ([TmyClass]) est appelé 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 à RegisterClass () ne doit pas nécessairement être dans la portée lexicale de l'unité utilisant GetClass ();
Une autre possibilité pour la portée dynamique sont les méthodes anonymes (fermetures) dans Delphi 2009, car elles connaissent les variables de leur fonction d'appel. Il ne suit pas le chemin d'appel à partir de là de manière récursive et n'est donc pas entièrement dynamique.
la source
J'adore les réponses complètes et indépendantes de la langue de personnes comme @Arak. Puisque cette question a été taguée JavaScript , je voudrais ajouter quelques notes très spécifiques à cette langue.
En JavaScript, nos choix de cadrage sont les suivants:
var _this = this; function callback(){ console.log(_this); }
callback.bind(this)
Il convient de noter, je pense, que JavaScript n'a pas vraiment de portée dynamique .
.bind
ajuste lethis
mot clé, et c'est proche, mais pas techniquement le même.Voici un exemple illustrant les deux approches. Vous effectuez cette opération chaque fois que vous décidez de la façon d'étendre les rappels, cela s'applique donc aux promesses, aux gestionnaires d'événements, etc.
Lexical
Voici ce que vous pourriez appeler
Lexical Scoping
des rappels en JavaScript:Lié
Une autre façon d'étendre est d'utiliser
Function.prototype.bind
:Pour autant que je sache, ces méthodes sont équivalentes sur le plan du comportement.
la source
bind
n'affecte pas la portée.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 par le code qui apparaît à l'intérieur de cette fonction.
la source
IBM le définit comme:
Exemple 1:
Exemple 2:
la source
La portée lexicale signifie que dans un groupe de fonctions imbriquées, les fonctions internes ont accès aux variables et autres ressources de leur portée parent . Cela signifie que les fonctions enfants sont lexicalement liées au contexte d'exécution de leurs parents. La portée lexicale est parfois également appelée portée statique .
La chose que vous remarquerez à propos de la portée lexicale est qu'elle fonctionne en avant, ce qui signifie que le nom est accessible par les contextes d'exécution de ses enfants. Mais il ne fonctionne pas en arrière pour ses parents, ce qui signifie que la variable
likes
n'est pas accessible à ses parents.Cela nous indique également que les variables portant le même nom dans des contextes d'exécution différents gagnent la priorité du haut vers le bas de la pile d'exécution. Une variable, ayant un nom similaire à une autre variable, dans la fonction la plus interne (contexte le plus haut de la pile d'exécution) aura une priorité plus élevée.
Notez que cela est tiré d' ici .
la source
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 à l'intérieur de votre portée, ce qui signifie que vous n'avez pas besoin de la passer là-bas.
Exemple:
// Sortie: JavaScript
la source
bind
. Avec eux, lebind
n'est plus nécessaire. Pour plus d'informations sur cette modification, consultez stackoverflow.com/a/34361380/11127383Il y a une partie importante de la conversation entourant la portée lexicale et dynamique qui manque: une explication claire de la durée de vie de la variable portée - ou quand la variable est accessible.
Le cadrage dynamique ne correspond que très vaguement au cadrage "global" dans 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 l'explication de l'article lié ); il est probablement préférable que nous ne fassions pas la comparaison entre global et dynamique - bien que soi-disant, selon l'article lié, "... [il] est utile comme substitut aux variables de portée globale."
Donc, en langage simple, 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: des 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 du PO - 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 des autres réponses, mais plutôt un "oh, cette réponse nous a fait souhaiter qu'il y en ait un peu plus "). Alors, voici un peu plus:
La portée dynamique signifie qu'une variable est accessible au plus grand programme pendant la durée de vie de l'appel de fonction - ou, pendant l'exécution de la fonction. Vraiment, Wikipedia fait vraiment du bon travail avec l' explication de la différence entre les deux. Afin de ne pas l'obscurcir, voici le texte qui décrit la portée dynamique:
la source
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 immédiatement autour d'elle.
Regardez comment fonctionne la portée lexicale en Lisp si vous voulez plus de détails. La réponse choisie par Kyle Cronin dans les variables dynamiques et lexicales en Common Lisp est beaucoup plus claire que les réponses ici.
Par coïncidence, je ne l'ai appris que dans une classe Lisp, et cela s'applique également à JavaScript.
J'ai exécuté ce code dans la console de Chrome.
Production:
la source
Une portée lexicale en JavaScript signifie qu'une variable définie en dehors d'une fonction peut être accessible à l'intérieur d'une autre fonction définie après la déclaration de variable. Mais l'inverse n'est pas vrai; les variables définies à l'intérieur d'une fonction ne seront pas accessibles en dehors de cette fonction.
Ce concept est largement utilisé dans les fermetures en JavaScript.
Disons que nous avons le code ci-dessous.
Maintenant, lorsque vous appelez add () -> cela affichera 3.
Ainsi, 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.la source
add()
fonction était appelée immédiatement après l'extrait de code donné, elle afficherait également 3. La portée lexicale ne signifie pas simplement qu'une fonction peut accéder à des variables globales en dehors du contexte local. Ainsi, l'exemple de code n'aide vraiment pas à montrer ce que signifie la portée lexicale. L'affichage de la portée lexicale dans le code a vraiment besoin d'un contre-exemple ou au moins d'une explication d'autres interprétations possibles du code.La portée lexicale fait référence au lexique des identifiants (par exemple, variables, fonctions, etc.) visibles depuis la position actuelle dans la pile d'exécution.
foo
etbar
sont toujours dans le lexique des identifiants disponibles car ils sont globaux.Quand
function1
est exécuté, il a accès à un lexiquefoo2
,bar2
,foo
etbar
.Quand
function2
est exécuté, il a accès à un lexiquefoo3
,bar3
,foo2
,bar2
,foo
etbar
.La raison pour laquelle les fonctions globales et / ou externes n'ont pas accès aux identifiants de fonctions internes est que l'exécution de cette fonction n'a pas encore eu lieu et, par conséquent, aucun de ses identifiants n'a été alloué à la mémoire. De plus, une fois ce contexte interne exécuté, il est supprimé de la pile d'exécution, ce qui signifie que tous ses identifiants ont été récupérés et ne sont plus disponibles.
Enfin, c'est pourquoi un contexte d'exécution imbriqué peut TOUJOURS accéder au contexte d'exécution de ses ancêtres et donc pourquoi il a accès à un plus grand lexique d'identifiants.
Voir:
Un merci spécial à @ robr3rd pour son aide à simplifier la définition ci-dessus.
la source
Voici un angle différent sur cette question que nous pouvons obtenir en prenant du recul et en examinant 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 compilateur) pour une langue et que vous étiez responsable du calcul de la sortie, compte tenu d'un programme et de certaines entrées.
L'interprétation implique le suivi de trois choses:
État - à savoir, les variables et les emplacements de mémoire référencés sur le tas et la pile.
Opérations sur cet état - à savoir, chaque ligne de code de votre programme
L' environnement dans lequel une opération donnée s'exécute, à savoir la projection d' état sur une opération.
Un interpréteur 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 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 mise en correspondance de l'état global du programme avec une valeur dans l'environnement. Notez que par «état global», nous ne voulons pas dire l'état global, mais plutôt la somme totale de chaque définition accessible, à tout moment de l'exécution).
C'est le cadre dans lequel le problème de délimitation est défini. Passons maintenant à la partie suivante de nos options.
C'est l'essentiel de la portée dynamique , dans laquelle l'environnement dans lequel tout code s'exécute est lié à l'état du programme tel que défini par son contexte d'exécution.
En d'autres termes, avec l'étendue lexicale, l'environnement que tout code voit est lié à l'état associé à une étendue définie explicitement dans le langage, comme un bloc ou une fonction.
la source
Question ancienne, mais voici mon point de vue.
La portée lexicale (statique) fait référence à la portée d'une variable dans le code source .
Dans un langage comme JavaScript, où les fonctions peuvent être transmises et attachées et reconnectées à divers objets, vous pourriez avoir bien que cette portée dépende de qui appelle la fonction à l'époque, mais ce n'est pas le cas. Changer la portée de cette façon serait une portée dynamique, et JavaScript ne le fait pas, sauf éventuellement avec la
this
référence d'objet.Pour illustrer ce point:
Dans l'exemple, la variable
a
est définie globalement, mais ombrée dans ladoit()
fonction. Cette fonction renvoie une autre fonction qui, comme vous le voyez, repose sur lea
variable en dehors de sa propre portée.Si vous exécutez cela, vous constaterez que la valeur utilisée n'est
aardvark
pas celleapple
qui, bien qu'elle soit dans la portée de latest()
fonction, n'est pas dans la portée lexicale de la fonction d'origine. Autrement dit, la portée utilisée est la portée telle qu'elle apparaît dans le code source, pas la portée où la fonction est réellement utilisée.Ce fait peut avoir des conséquences gênantes. Par exemple, vous pourriez décider qu'il est plus facile d'organiser vos fonctions séparément, puis de les utiliser le moment venu, comme dans un gestionnaire d'événements:
Cet exemple de code fait l'un de chacun. Vous pouvez voir qu'en raison de la portée lexicale, le bouton
A
utilise la variable interne, tandis que le boutonB
contrairement au . Vous pouvez finir par imbriquer des fonctions plus que vous ne l'auriez souhaité.Soit dit en passant, dans les deux exemples, vous remarquerez également que les variables de portée lexicale interne persistent même si la fonction de fonction contenant a suivi son cours. Ceci est appelé fermeture et fait référence à l'accès d'une fonction imbriquée aux variables externes, même si la fonction externe est terminée. JavaScript doit être suffisamment intelligent pour déterminer si ces variables ne sont plus nécessaires et, dans le cas contraire, peut les récupérer.
la source
J'apprends normalement par l'exemple, et voici un petit quelque chose:
la source
Cette rubrique est fortement liée à la
bind
fonction intégrée et introduite dans ECMAScript 6 Arrow Functions . C'était vraiment ennuyeux, car pour chaque nouvelle méthode de "classe" (fonction en fait) que nous voulions utiliser, nous devions le fairebind
pour avoir accès à la portée.JavaScript par défaut ne définit pas son champ d'
this
sur les fonctions (il ne définit pas le contexte surthis
). Par défaut, vous devez indiquer explicitement le contexte que vous souhaitez avoir.Les fonctions fléchées obtiennent automatiquement ce que l'on appelle la portée lexicale (ont accès à la définition de la variable dans son bloc conteneur). Lorsque vous utilisez des fonctions de flèche, il se lie automatiquement
this
à l'endroit où la fonction de flèche a été définie en premier lieu, et le contexte de ces fonctions de flèche est son bloc conteneur.Voyez comment cela fonctionne dans la pratique sur les exemples les plus simples ci-dessous.
Avant les fonctions flèches (pas de portée lexicale par défaut):
Avec les fonctions flèches (portée lexicale par défaut):
la source