Wat is lexicale reikwijdte?

Wat is een korte introductie tot lexicale scoping?


Antwoord 1, autoriteit 100%

Ik begrijp ze door middel van voorbeelden. 🙂

Eerst, lexicale scope(ook wel static scopegenoemd), in C-achtige syntaxis:

void fun()
{
    int x = 5;
    void fun2()
    {
        printf("%d", x);
    }
}

Elk innerlijk niveau heeft toegang tot zijn buitenste niveaus.

Er is een andere manier, genaamd dynamisch bereik, gebruikt door de eerste implementatie van Lisp, opnieuw in een C-achtige syntaxis:

void fun()
{
    printf("%d", x);
}
void dummy1()
{
    int x = 5;
    fun();
}
void dummy2()
{
    int x = 10;
    fun();
}

Hier heeft funtoegang tot xin dummy1of dummy2, of elke xin elke functie die funaanroept met xerin gedeclareerd.

dummy1();

drukt 5 af,

dummy2();

drukt er 10 af.

De eerste wordt statisch genoemd omdat deze tijdens het compileren kan worden afgeleid, en de tweede wordt dynamisch genoemd omdat het buitenste bereik dynamisch is en afhankelijk is van de ketenaanroep van de functies.

Ik vind statische scoping gemakkelijker voor het oog. De meeste talen gingen uiteindelijk deze kant op, zelfs Lisp (kan beide, toch?). Dynamische scoping is als het doorgeven van verwijzingen van alle variabelen naar de aangeroepen functie.

Als een voorbeeld van waarom de compiler de buitenste dynamische reikwijdte van een functie niet kan afleiden, bekijk ons laatste voorbeeld. Als we zoiets als dit schrijven:

if(/* some condition */)
    dummy1();
else
    dummy2();

De oproepketen is afhankelijk van een runtime-conditie. Als het waar is, ziet de oproepketen er als volgt uit:

dummy1 --> fun()

Als de voorwaarde onwaar is:

dummy2 --> fun()

Het buitenste bereik van funis in beide gevallen de beller plus de beller van de beller, enzovoort.

Om te vermelden dat de C-taal geen geneste functies of dynamische scoping toestaat.


Antwoord 2, autoriteit 45%

Laten we de kortst mogelijke definitie proberen:

Lexicale bereikdefinieert hoe variabelenamen worden opgelost in geneste functies: innerlijke functies bevatten het bereik van bovenliggende functies, zelfs als de bovenliggende functie is geretourneerd.

Dat is alles!


Antwoord 3, autoriteit 10%

var scope = "I am global";
function whatismyscope(){
   var scope = "I am just a local";
   function func() {return scope;}
   return func;
}
whatismyscope()()

De bovenstaande code retourneert “Ik ben gewoon een local”. Het zal niet terugkeren “Ik ben een global”. Omdat de functie func() telt waar is oorspronkelijk is gedefinieerd, wat onder het bereik van de functie whatismyscope valt.

Het maakt niet uit hoe het wordt genoemd (de globale scope/zelfs vanuit een andere functie), daarom wordt de globale scope-waarde I am global niet afgedrukt.

Dit wordt lexicale scoping genoemd waarbij “functies worden uitgevoerd met behulp van de bereikketen die van kracht was toen ze werden gedefinieerd” – volgens de JavaScript-definitiegids.

Lexicale scope is een heel erg krachtig concept.

Hopelijk helpt dit..:)


Antwoord 4, autoriteit 6%

Lexicale (ook wel statische) scoping verwijst naar het bepalen van het bereik van een variabele uitsluitend op basis van zijn positie binnen het tekstuele corpus van code. Een variabele verwijst altijd naar zijn omgeving op het hoogste niveau. Het is goed om het te begrijpen in relatie met dynamisch bereik.


Antwoord 5, autoriteit 6%

Scope definieert het gebied waar functies, variabelen en dergelijke beschikbaar zijn. De beschikbaarheid van een variabele wordt bijvoorbeeld gedefinieerd binnen de context, laten we zeggen de functie, het bestand of het object waarin ze zijn gedefinieerd. Meestal noemen we deze lokale variabelen.

Het lexicale gedeelte houdt in dat je de reikwijdte kunt afleiden uit het lezen van de broncode.

Lexicale scope is ook bekend als statische scope.

Dynamisch bereik definieert globale variabelen die overal kunnen worden aangeroepen of waarnaar verwezen kan worden nadat ze zijn gedefinieerd. Soms worden ze globale variabelen genoemd, hoewel globale variabelen in de meeste programmeertalen een lexicale reikwijdte hebben. Dit betekent dat uit het lezen van de code kan worden afgeleid dat de variabele in deze context beschikbaar is. Misschien moet je een gebruiks- of include-clausule volgen om de instatiatie of definitie te vinden, maar de code/compiler weet van de variabele op deze plaats.

Bij dynamische scoping zoek je daarentegen eerst in de lokale functie, dan zoek je in de functie die de lokale functie aanroept, dan zoek je in de functie die die functie aanroept, enzovoort, in de aanroepstack. “Dynamisch” verwijst naar verandering, in die zin dat de aanroepstapel anders kan zijn elke keer dat een bepaalde functie wordt aangeroepen, en dus kan de functie verschillende variabelen raken, afhankelijk van waar deze wordt aangeroepen. (zie hier)

Voor een interessant voorbeeld van dynamische scope, zie hier.

Voor meer details zie hieren hier.

Enkele voorbeelden in Delphi/Object Pascal

Delphi heeft een lexicale reikwijdte.

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.

Het dichtste dat Delphi bij dynamisch bereik komt, is het functiepaar RegisterClass()/GetClass(). Zie voor het gebruik hier.

Stel dat de tijd dat RegisterClass([TmyClass]) wordt aangeroepen om een bepaalde klasse te registreren niet kan worden voorspeld door de code te lezen (deze wordt aangeroepen in een door de gebruiker aangeroepen knopklikmethode), code die GetClass(‘TmyClass’ aanroept) ) een resultaat krijgt of niet. De aanroep van RegisterClass() hoeft niet binnen het lexicale bereik van de eenheid te vallen met GetClass();

Een andere mogelijkheid voor dynamisch bereik zijn anonieme methoden(sluitingen) in Delphi 2009, omdat ze de variabelen van hun aanroepfunctie kennen. Het volgt het aanroepende pad niet recursief en is daarom niet volledig dynamisch.


Antwoord 6, autoriteit 5%

Ik ben dol op de volledig functionele, taalonafhankelijke antwoorden van mensen als @Arak. Aangezien deze vraag echter is getagd met JavaScript, wil ik graag enkele opmerkingen toevoegen die zeer specifiek zijn voor deze taal.

In JavaScript zijn onze keuzes voor scoping:

  • zoals het is (geen aanpassing van het bereik)
  • lexicale var _this = this; function callback(){ console.log(_this); }
  • gebonden callback.bind(this)

Het is volgens mij vermeldenswaard dat JavaScript heeft niet echt dynamische scoping. .bindpast het trefwoord thisaan, en dat komt in de buurt, maar technisch gezien niet hetzelfde.

Hier is een voorbeeld dat beide benaderingen demonstreert. U doet dit elke keer dat u een beslissing neemt over het bereik van callbacks, dus dit is van toepassing op beloften, event-handlers en meer.

Lexicale

Dit is wat je zou kunnen noemen Lexical Scopingvan callbacks in 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
  }
  //...
};

Gebonden

Een andere manier om het bereik te bepalen is om te gebruiken Function.prototype.bind:

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

Deze methoden zijn, voor zover ik weet, qua gedrag gelijkwaardig.


Antwoord 7, autoriteit 3%

Een lexicale scope in JavaScript betekent dat een variabele die buiten een functie is gedefinieerd, toegankelijk kan zijn binnen een andere functie die is gedefinieerd na de variabeledeclaratie. Maar het tegenovergestelde is niet waar; de variabelen die binnen een functie zijn gedefinieerd, zijn niet toegankelijk buiten die functie.

Dit concept wordt veel gebruikt bij afsluitingen in JavaScript.

Stel dat we de onderstaande code hebben.

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

Als je nu add() –> dit zal 3 afdrukken.

Dus de functie add() heeft toegang tot de globale variabele xdie is gedefinieerd vóór de methodefunctie add. Dit wordt genoemd vanwege lexicale scoping in JavaScript.


Antwoord 8, autoriteit 3%

Lexicale scopebetekent dat in een geneste groep functies de interne functies toegang hebben tot de variabelen en andere bronnen van hun bovenliggende scope.

Dit betekent dat de functies van het kind lexicaal gebonden zijn aan de uitvoeringscontext van hun ouders.

Lexicale scope wordt soms ook wel statische scopegenoemd.

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';
        }
    }
}

Wat je opvalt aan lexicale reikwijdte is dat het vooruit werkt, wat betekent dat de naam toegankelijk is via de uitvoeringscontexten van de onderliggende elementen.

Maar het werkt niet terug naar zijn ouders, wat betekent dat de variabele likesniet toegankelijk is voor zijn ouders.

Dit vertelt ons ook dat variabelen met dezelfde naam in verschillende uitvoeringscontexten voorrang krijgen van boven naar beneden in de uitvoeringsstapel.

Een variabele met een naam die lijkt op een andere variabele, in de binnenste functie (bovenste context van de uitvoeringsstapel) heeft een hogere prioriteit.

Bron.


Antwoord 9, autoriteit 2%

In eenvoudige taal is lexicale scope een variabele die buiten je scope is gedefinieerd, of een hogere scope is automatisch beschikbaar binnen je scope, wat betekent dat je hem daar niet hoeft door te geven.

Voorbeeld:

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

// Uitvoer: JavaScript


Antwoord 10, autoriteit 2%

Lexicale scoping: variabelen die buiten een functie worden gedeclareerd, zijn globale variabelen en zijn overal zichtbaar in een JavaScript-programma. Variabelen gedeclareerd binnen een functie hebben een functiebereik en zijn alleen zichtbaar voor code die binnen die functie verschijnt.


Antwoord 11, autoriteit 2%

IBMdefinieert het als:

Het gedeelte van een programma of segmenteenheid waarin een declaratie
is van toepassing. Een identifier die in een routine is gedeclareerd, is daarin bekend
routine en binnen alle geneste routines. Als een geneste routine verklaart
een item met dezelfde naam, het buitenste item is niet beschikbaar in de
geneste routine.

Voorbeeld 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();

Voorbeeld 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();

Antwoord 12

Lexicale scope betekent dat een functie variabelen opzoekt in de context waarin deze is gedefinieerd, en niet in de scope er direct omheen.

Bekijk hoe het lexicale bereik werkt in Lispals je meer details wilt. Het geselecteerde antwoord van Kyle Cronin in Dynamische en Lexicale variabelen in Common Lispis een stuk duidelijker dan de antwoorden hier.

Toevallig heb ik dit pas geleerd in een Lisp-les, en het is ook van toepassing in JavaScript.

Ik heb deze code uitgevoerd in de Chrome-console.

// 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)
})();                     //)

Uitvoer:

5
10
5

Antwoord 13

Lexicale scope verwijst naar het lexicon van identifiers (bijv. variabelen, functies, etc.) die zichtbaar zijn vanaf de huidige positie in de uitvoeringsstack.

- global execution context
    - foo
    - bar
    - function1 execution context
        - foo2
        - bar2
        - function2 execution context
            - foo3
            - bar3

fooen barvallen altijd binnen het lexicon van beschikbare identifiers omdat ze globaal zijn.

Wanneer function1wordt uitgevoerd, heeft het toegang tot een lexicon van foo2, bar2, fooen bar.

Wanneer function2wordt uitgevoerd, heeft het toegang tot een lexicon van foo3, bar3, foo2, bar2, fooen bar.

De reden dat globale en/of buitenste functies geen toegang hebben tot een inner function identifier, is dat de uitvoering van die functie nog niet heeft plaatsgevonden en dat daarom geen van de identifiers aan het geheugen is toegewezen. Wat meer is, als die innerlijke context eenmaal wordt uitgevoerd, wordt deze verwijderd uit de uitvoeringsstapel, wat betekent dat alle ID’s zijn verzameld en niet langer beschikbaar zijn.

Dit is ten slotte de reden waarom een geneste uitvoeringscontext ALTIJD toegang heeft tot de uitvoeringscontext van de voorouders en dus toegang heeft tot een groter lexicon van ID’s.

Zie:

Met dank aan @robr3rdvoor hulp bij het vereenvoudigen van de bovenstaande definitie.


Antwoord 14

Er is een belangrijk deel van het gesprek rond lexicaleen dynamische scopingdat ontbreekt: een duidelijke uitleg van de levensduurvan de scoped variabele – of wanneerde variabele toegankelijk is.

Dynamische scoping komt slechts heel losjes overeen met ‘globale’ scoping op de manier waarop we er traditioneel over denken (de reden dat ik de vergelijking tussen de twee naar voren breng, is dat het al genoemd– en ik hou niet zo van de gelinkteuitleg van het artikel); het is waarschijnlijk het beste dat we de vergelijking tussen globaal en dynamisch niet maken – hoewel vermoedelijk, volgens het gelinkte artikel, “…[het] nuttig is als vervanging voor variabelen met een globaal bereik.”

Dus, in gewoon Engels, wat is het belangrijke onderscheid tussen de twee scoping-mechanismen?

Lexicale scoping is in de bovenstaande antwoorden heel goed gedefinieerd: variabelen met lexicale scope zijn beschikbaar – of, toegankelijk – op het lokale niveau van de functie waarin ze zijn gedefinieerd.

Echter – aangezien het niet de focus van het OP is – heeft dynamische scoping niet veel aandacht gekregen en de aandacht die het heeft gekregen betekent dat het waarschijnlijk wat meer nodig heeft (dat is geen kritiek op andere antwoorden, maar eerder een “oh, dat antwoord maakte dat we wensten dat er een beetje meer was”). Dus, hier is een beetje meer:

Dynamische scoping betekent dat een variabele toegankelijk is voor het grotere programma tijdens de levensduur van de functieaanroep – of, terwijl de functie wordt uitgevoerd. Wikipedia doet echt goed werk met de uitleg van het verschiltussen de twee. Om het niet te verdoezelen, volgt hier de tekst die dynamische scoping beschrijft:

…[I]n dynamische scoping (of dynamische scope), als de scope van een variabele a . is
bepaalde functie, dan is de reikwijdte ervan de tijdsperiode waarin de
functie wordt uitgevoerd: terwijl de functie actief is, wordt de variabele
naam bestaat, en is gebonden aan zijn variabele, maar na de functie
retourneert, bestaat de variabelenaam niet.


Antwoord 15

Hier is een andere invalshoek op deze vraag die we kunnen krijgen door een stap terug te doen en te kijken naar de rol van scoping in het grotere interpretatiekader (een programma draaien). Met andere woorden, stel je voor dat je een tolk (of compiler) voor een taal aan het bouwen was en verantwoordelijk was voor het berekenen van de uitvoer, met een programma en wat input ervoor.

Interpretatie omvat het bijhouden van drie dingen:

  1. Status – namelijk variabelen en geheugenlocaties waarnaar wordt verwezen op de heap en stack.

  2. Bewerkingen op die staat – namelijk elke regel code in uw programma

  3. De omgevingwaarin een bepaalde operatiewordt uitgevoerd – namelijk de projectie van stateop een operatie.

Een interpreter begint bij de eerste regel code in een programma, berekent de omgeving, voert de regel in die omgeving uit en legt het effect vast op de status van het programma. Het volgt dan de besturingsstroom van het programma om de volgende regel code uit te voeren en herhaalt het proces totdat het programma eindigt.

De manier waarop u de omgeving voor elke bewerking berekent, is door middel van een formele set regels die zijn gedefinieerd door de programmeertaal. De term “binding” wordt vaak gebruikt om het in kaart brengen van de algehele staat van het programma aan een waarde in de omgeving te beschrijven. Merk op dat we met “algemene toestand” niet de globale toestand bedoelen, maar eerder het totaal van elke bereikbare definitie, op elk punt in de uitvoering).

Dit is het raamwerk waarin het scopingprobleem wordt gedefinieerd. Nu naar het volgende deel van wat onze opties zijn.

  • Als de uitvoerder van de interpreter, zou je je taak kunnen vereenvoudigen door de omgeving zo dicht mogelijk bij de staat van het programma te brengen. Dienovereenkomstig zou de omgeving van een regel code eenvoudigweg worden gedefinieerd door de omgeving van de vorige regel code met de effecten van die bewerking erop toegepast, ongeacht of de vorige regel een toewijzing, een functieaanroep, terugkeer van een functie, of een controlestructuur zoals een while-lus.

Dit is de kern van dynamische scoping, waarbij de omgeving waarin een code wordt uitgevoerd, is gebonden aan de status van het programma zoals gedefinieerd door de uitvoeringscontext.

  • Of, je zou kunnen denken aan een programmeur die jouw taal gebruikt en zijn of haar taak vereenvoudigt om de waarden bij te houden die een variabele kan aannemen. Er zijn veel te veel paden en te veel complexiteit betrokken bij het redeneren over de uitkomst van de totaliteit van uitvoering in het verleden. Lexical Scopinghelpt hierbij door de huidige omgeving te beperken tot het deel van de toestand gedefinieerd inhet huidige blok, de functie of andere eenheid van bereik, en zijn bovenliggende (dwz het blok dat de huidige klok, of de functie die de huidige functie heeft aangeroepen).

Met andere woorden, met lexicale scopeis de omgeving die elke code ziet, gebonden aan een status die is gekoppeld aan een scope die expliciet in de taal is gedefinieerd, zoals een blok of een functie.


Antwoord 16

Oude vraag, maar hier is mijn mening.

Lexicale(statisch) bereik verwijst naar het bereik van een variabele in de broncode.

In een taal als JavaScript, waar functies kunnen worden doorgegeven en gekoppeld en opnieuw kunnen worden gekoppeld aan diverse objecten, zou het kunnen zijn dat dat bereik afhangt van wie de functie op dat moment aanroept, maar dat is niet het geval. Het op die manier wijzigen van het bereik zou een dynamisch bereik zijn, en JavaScript doet dat niet, behalve mogelijk met de objectreferentie this.

Om het punt te illustreren:

var a='apple';
function doit() {
    var a='aardvark';
    return function() {
        alert(a);
    }
}
var test=doit();
test();

Antwoord 17

Ik hoop dat dit nuttig is, hier is mijn poging tot een iets abstractere definitie:

Lexicaal bereik:
De toegang of het bereik dat iets (bijvoorbeeld een functie of variabele) heeft tot andere elementen in het programma, zoals bepaald door de positie in de broncode.

Fwiw, mijn logica hier bouwt gewoon voort op de definities van:

Lexicale: met betrekking tot de woorden of het vocabulaire van een taal (met name het woord los van zijn grammatica of constructie) {in ons geval – een programmeertaal}.

Scope(zelfstandig naamwoord): het bereik van de operatie {in ons geval – het bereik is: wat kan worden benaderd}.


Antwoord 18

Normaal gesproken leer ik door het voorbeeld, en hier is een kleinigheidje:

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();

Antwoord 19

Dit onderwerp is sterk gerelateerd aan de ingebouwde bind-functie en is geïntroduceerd in ECMAScript 6 Pijlfuncties. Het was echt vervelend, want voor elke nieuwe “class” (functie eigenlijk) methode die we wilden gebruiken, moesten we deze bindom toegang te krijgen tot de scope.

JavaScript stelt standaard het bereik van thisniet in op functies (het stelt niet de contextop this). Standaard moet je expliciet aangeven welke contextje wilt hebben.

De pijlfunctieskrijgen automatisch het zogenaamde lexicale bereik(heb toegang tot de definitie van de variabele in het bevattende blok). Bij gebruik van pijlfunctiesbindt het automatisch thisaan de plaats waar de pijlfunctie in de eerste plaats was gedefinieerd, en de contextvan deze pijlfunctiesis het bevattende blok.

Bekijk hoe het in de praktijk werkt aan de hand van de eenvoudigste voorbeelden hieronder.

Vóór pijlfuncties(standaard geen lexicale scope):

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"

Met pijlfuncties(standaard lexicale scope):

const programming = {
  language: "JavaScript",
  getLanguage: function() {
    return this.language;
  }
}
const arrowFunction = () => {
    console.log(programming.getLanguage());
}
arrowFunction(); // Output: "JavaScript"

Other episodes