Quelqu'un pourrait-il me donner une brève introduction à la portée lexicale?
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.
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!
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..:)
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.
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.
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:
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.
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
}
//...
};
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.
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();
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.
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.
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.
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
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.
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.
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.
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
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();
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