Qu'est-ce qu'une brève introduction à la portée lexicale?
Qu'est-ce qu'une brève introduction à la portée lexicale?
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:
void fun()
{
int x = 5;
void fun2()
{
printf("%d", x);
}
}
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:
void fun()
{
printf("%d", x);
}
void dummy1()
{
int x = 5;
fun();
}
void dummy2()
{
int x = 10;
fun();
}
Ici, vous fun
pouvez accéder x
à dummy1
ou dummy2
, ou à n'importe x
quelle fonction qui appelle fun
avec x
déclaré en elle.
dummy1();
imprimera 5,
dummy2();
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:
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 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.
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érente
for
boucle est le problème typique. La portée lexicale de JavaScript est uniquement au niveau de la fonction, sauf si l'ES6 let
ou const
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!
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 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 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 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.
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 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.
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 le this
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.
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
}
//...
};
Une autre façon d'étendre 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`
}
//...
Pour autant que je sache, ces méthodes sont équivalentes sur le plan du comportement.
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.
IBM le définit comme:
Portion d'une unité de programme ou de segment à laquelle s'applique une déclaration. Un identifiant déclaré dans une routine est connu dans cette routine et dans toutes les routines imbriquées. Si une routine imbriquée déclare un élément du même nom, l'élément externe n'est pas disponible dans la 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();
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 .
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';
}
}
}
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 .
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:
let str="JavaScript";
const myFun = () => {
console.log(str);
}
myFun();
// Sortie: JavaScript
bind
. Avec eux, le bind
n'est plus nécessaire. Pour plus d'informations sur cette modification, consultez stackoverflow.com/a/34361380/11127383
Il 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:
... [I] n portée dynamique (ou portée dynamique), si la portée d'un nom de variable est une certaine fonction, alors sa portée est la période pendant laquelle la fonction s'exécute: pendant que la fonction est en cours d'exécution, le nom de la variable existe et est lié à sa variable, mais après le retour de la fonction, le nom de la variable n'existe pas.
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.
// 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)
})(); //)
Production:
5
10
5
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.
var x = 2;
var add = function() {
var y = 1;
return x + y;
};
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.
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.
- global execution context
- foo
- bar
- function1 execution context
- foo2
- bar2
- function2 execution context
- foo3
- bar3
foo
et bar
sont toujours dans le lexique des identifiants disponibles car ils sont globaux.
Quand function1
est exécuté, il a accès à un lexique foo2
, bar2
, foo
et bar
.
Quand function2
est exécuté, il a accès à un lexique foo3
, bar3
, foo2
, bar2
, foo
et bar
.
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.
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.
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:
var a='apple';
function doit() {
var a='aardvark';
return function() {
alert(a);
}
}
var test=doit();
test();
Dans l'exemple, la variable a
est définie globalement, mais ombrée dans la doit()
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 celle apple
qui, bien qu'elle soit dans la portée de la test()
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:
var a='apple',b='banana';
function init() {
var a='aardvark',b='bandicoot';
document.querySelector('button#a').onclick=function(event) {
alert(a);
}
document.querySelector('button#b').onclick=doB;
}
function doB(event) {
alert(b);
}
init();
<button id="a">A</button>
<button id="b">B</button>
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.
J'apprends normalement par l'exemple, et 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();
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 faire bind
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 sur this
). 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):
const programming = {
language: "JavaScript",
getLanguage: function() {
return this.language;
}
}
const globalScope = programming.getLanguage;
console.log(globalScope()); // Output: undefined
const localScope = programming.getLanguage.bind(programming);
console.log(localScope()); // Output: "JavaScript"
Avec les fonctions flèches (portée lexicale par défaut):
const programming = {
language: "JavaScript",
getLanguage: function() {
return this.language;
}
}
const arrowFunction = () => {
console.log(programming.getLanguage());
}
arrowFunction(); // Output: "JavaScript"