Cos'è l'ambito lessicale?


682

Che cos'è una breve introduzione allo scoping lessicale?


89
Nel podcast 58, Joel incoraggia domande come queste in quanto vuole che diventino IL posto per le risposte, anche se hanno avuto risposta in altri posti. Questa è una domanda valida, anche se si potrebbe dire che è un po 'più educato.
Ralph M. Rickenbach,

5
@rahul ho capito che è una vecchia domanda. Ma sono sicuro che anche nel 2009, SO si aspettava che i richiedenti facessero qualche sforzo di base per risolverlo. Allo stato attuale non mostra alcuno sforzo. Può essere, è per questo che è stato downvoted da molti?
PP,

13
È possibile che il richiedente non sia (o non fosse) fluente in inglese quando scrive questa domanda
Martin

27
La domanda è educata, dice solo quello che vuole. Sei libero di rispondere. Non c'è bisogno di educazione sovrasensibile qui.
Markus Siebeneicher,

25
Penso che domande come queste siano fantastiche perché crea contenuti per SO. IMO, a chi importa se la domanda non ha sforzo ... le risposte avranno un ottimo contenuto ed è quello che conta in questa bacheca.
Jwan622,

Risposte:


687

Li capisco attraverso esempi. :)

Innanzitutto, ambito lessicale (chiamato anche ambito statico ), in sintassi simil-C:

void fun()
{
    int x = 5;

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

Ogni livello interno può accedere ai suoi livelli esterni.

C'è un altro modo, chiamato ambito dinamico usato dalla prima implementazione di Lisp , sempre in una sintassi simile al C:

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

void dummy1()
{
    int x = 5;

    fun();
}

void dummy2()
{
    int x = 10;

    fun();
}

Qui è funpossibile accedere xin dummy1o dummy2oppure xin qualsiasi funzione che chiami funcon xdichiarato in esso.

dummy1();

stamperà 5,

dummy2();

stamperà 10.

Il primo è chiamato statico perché può essere dedotto in fase di compilazione e il secondo è chiamato dinamico perché l'ambito esterno è dinamico e dipende dalla chiamata a catena delle funzioni.

Trovo l'ambito statico più facile per gli occhi. La maggior parte delle lingue alla fine è andata così, persino Lisp (può fare entrambe le cose, giusto?). L'ambito dinamico è come passare i riferimenti di tutte le variabili alla funzione chiamata.

Come esempio del perché il compilatore non può dedurre l'ambito dinamico esterno di una funzione, si consideri il nostro ultimo esempio. Se scriviamo qualcosa del genere:

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

La catena di chiamate dipende da una condizione di runtime. Se è vero, la catena di chiamate è simile a:

dummy1 --> fun()

Se la condizione è falsa:

dummy2 --> fun()

L'ambito esterno di funentrambi i casi è il chiamante più il chiamante del chiamante e così via .

Solo per dire che il linguaggio C non consente funzioni nidificate né scoping dinamico.


19
Vorrei anche sottolineare un tutorial molto molto semplice da capire che ho appena trovato. L'esempio di Arak è buono, ma potrebbe essere troppo breve per qualcuno che ha bisogno di più esempi (in realtà, confrontandolo con altre lingue ..). Guarda. È importante capire questo , poiché quella parola chiave ci porterà a comprendere l'ambito lessicale. howtonode.org/what-is-this
CppLearner

9
Questa è una buona risposta Ma la domanda è taggata con JavaScript. Pertanto penso che questo non dovrebbe essere contrassegnato come risposta accettata. L'ambito lessicale specificamente in JS è diverso
Boyang,

6
Risposta estremamente buona. Grazie. @Boyang Non sono d'accordo. Non sono un programmatore Lisp, ma ho trovato utile l'esempio Lisp, poiché è un esempio di scoping dinamico, che non si ottiene in JS.
Dudewad,

4
Inizialmente pensavo che l'esempio fosse un codice C valido ed ero confuso se ci fosse uno scoping dinamico in C. Forse il disclaimer alla fine poteva essere spostato prima dell'esempio di codice?
Yangshun Tay

2
Questa è ancora una risposta molto utile, ma penso che @Boyang sia corretto. Questa risposta si riferisce al "livello", che è più lungo le linee di ambito del blocco che C ha. JavaScript per impostazione predefinita non ha un ambito a livello di blocco, quindi all'interno di un forciclo c'è il problema tipico. L'ambito lessicale per JavaScript è solo a livello di funzione, a meno che non venga utilizzato ES6 leto const.
icc97,

275

Proviamo la definizione più breve possibile:

L'ambito lessicale definisce come i nomi delle variabili vengono risolti nelle funzioni nidificate: le funzioni interne contengono l'ambito delle funzioni padre anche se la funzione padre è tornata .

Questo è tutto quello che c'è da fare!


21
L'ultima parte: "anche se la funzione genitore è tornata" si chiama Closure.
Juanma Menendez,

1
Compresi lo scoping lessicale e la chiusura in una sola frase. Grazie!!
Prigione

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

whatismyscope()()

Il codice sopra riportato restituirà "I am just a local". Non restituirà "I am a global". Perché la funzione func () conta dove è stato originariamente definito che rientra nell'ambito della funzione whatismyscope.

Non si preoccuperà di come viene chiamato (l'ambito globale / anche all'interno di un'altra funzione), ecco perché il valore dell'ambito globale I am global non verrà stampato.

Questo si chiama scoping lessicale in cui "le funzioni vengono eseguite utilizzando la catena dell'ambito che era in vigore al momento della loro definizione ", secondo la Guida alle definizioni JavaScript.

L'ambito lessicale è un concetto molto potente.

Spero che sia di aiuto..:)


3
è una spiegazione molto bella voglio aggiungere un'altra cosa se scrivi la funzione func () {return this.scope;} quindi restituirà "I am global" basta usare questa parola chiave e il tuo ambito otterrà il cambiamento
Rajesh Kumar Bhawsar

41

L'ambito lessicale (statico AKA) si riferisce alla determinazione dell'ambito di una variabile basandosi esclusivamente sulla sua posizione all'interno del corpus testuale del codice. Una variabile si riferisce sempre al suo ambiente di massimo livello. È bene capirlo in relazione all'ambito dinamico.


41

L'ambito definisce l'area, dove sono disponibili funzioni, variabili e simili. La disponibilità di una variabile, ad esempio, è definita nel suo contesto, diciamo la funzione, il file o l'oggetto, in cui sono definiti. Di solito chiamiamo queste variabili locali.

La parte lessicale significa che è possibile derivare l'ambito dalla lettura del codice sorgente.

L'ambito lessicale è anche noto come ambito statico.

L'ambito dinamico definisce le variabili globali che possono essere richiamate o referenziate da qualsiasi luogo dopo essere state definite. A volte vengono chiamate variabili globali, anche se le variabili globali nella maggior parte dei linguaggi di programmazione hanno portata lessicale. Ciò significa che dalla lettura del codice si può derivare che la variabile è disponibile in questo contesto. Forse si deve seguire una clausola usi o include per trovare l'installazione o la definizione, ma il codice / compilatore conosce la variabile in questo posto.

Nell'ambito dinamico, invece, si cerca prima nella funzione locale, quindi si cerca nella funzione che ha chiamato la funzione locale, quindi si cerca nella funzione che ha chiamato quella funzione e così via, nello stack di chiamate. "Dinamico" si riferisce al cambiamento, in quanto lo stack di chiamate può essere diverso ogni volta che viene chiamata una determinata funzione, e quindi la funzione potrebbe colpire variabili diverse a seconda di dove viene chiamata. (vedi qui )

Per vedere un esempio interessante di ambito dinamico vedere qui .

Per ulteriori dettagli vedere qui e qui .

Alcuni esempi in Delphi / Object Pascal

Delphi ha portata lessicale.

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.

Il Delphi più vicino all'ambito dinamico è la coppia di funzioni RegisterClass () / GetClass (). Per il suo utilizzo vedi qui .

Diciamo che il tempo in cui RegisterClass ([TmyClass]) viene chiamato per registrare una determinata classe non può essere previsto leggendo il codice (viene chiamato in un metodo click-button chiamato dall'utente), il codice che chiama GetClass ('TmyClass') otterrà un risultato o no. La chiamata a RegisterClass () non deve rientrare nell'ambito lessicale dell'unità mediante GetClass ();

Un'altra possibilità di ambito dinamico sono i metodi anonimi (chiusure) in Delphi 2009, poiché conoscono le variabili della loro funzione di chiamata. Non segue il percorso della chiamata da lì ricorsivamente e quindi non è completamente dinamico.


2
In realtà privato è accessibile in tutta l'unità in cui è definita la classe. Questo è il motivo per cui "Strict private" è stato introdotto nel D2006.
Marco van de Voort,

2
+1 per un linguaggio semplice (al contrario sia del linguaggio complicato che degli esempi senza molta descrizione)
apre il

36

Adoro le risposte agnostiche completamente linguistiche di persone come @Arak. Dato che questa domanda è stata taggata con JavaScript , vorrei aggiungere alcune note molto specifiche per questa lingua.

In JavaScript le nostre scelte per l'ambito sono:

  • così com'è (nessuna regolazione dell'ambito)
  • lessicale var _this = this; function callback(){ console.log(_this); }
  • limite callback.bind(this)

Vale la pena notare, penso, che JavaScript non ha realmente l'ambito dinamico . .bindregola la thisparola chiave, e questo è vicino, ma tecnicamente non è lo stesso.

Ecco un esempio che dimostra entrambi gli approcci. Lo fai ogni volta che prendi una decisione su come estendere i callback così questo vale per promesse, gestori di eventi e altro.

Lessicale

Ecco cosa potresti chiamare Lexical Scopingdei callback 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
  }
  //...
};

Limite

Un altro modo di ambito è utilizzare Function.prototype.bind:

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

Questi metodi sono, per quanto ne so, equivalenti dal punto di vista comportamentale.


L'uso bindnon influisce sull'ambito.
Ben Aston,

12

Scoping lessicale: le variabili dichiarate al di fuori di una funzione sono variabili globali e sono visibili ovunque in un programma JavaScript. Le variabili dichiarate all'interno di una funzione hanno un ambito di funzione e sono visibili solo al codice che appare all'interno di quella funzione.


12

IBM lo definisce come:

La parte di un programma o unità di segmento in cui si applica una dichiarazione. Un identificatore dichiarato in una routine è noto all'interno di quella routine e in tutte le routine nidificate. Se una routine nidificata dichiara un articolo con lo stesso nome, l'elemento esterno non è disponibile nella routine nidificata.

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

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

8

Ambito lessicale significa che in un gruppo nidificato di funzioni, le funzioni interne hanno accesso alle variabili e ad altre risorse del loro ambito genitore . Ciò significa che le funzioni figlio sono legate in modo lessicale al contesto di esecuzione dei loro genitori. L'ambito lessicale viene talvolta definito anche ambito statico .

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 cosa che noterai dell'ambito lessicale è che funziona in avanti, il che significa che il nome è accessibile dai contesti di esecuzione dei suoi figli. Ma non funziona a ritroso con i suoi genitori, il che significa che i likessuoi genitori non possono accedere alla variabile .

Questo ci dice anche che le variabili che hanno lo stesso nome in contesti di esecuzione diversi hanno la precedenza dall'alto verso il basso dello stack di esecuzione. Una variabile, che ha un nome simile a un'altra variabile, nella funzione più interna (contesto più in alto dello stack di esecuzione) avrà una precedenza più alta.

Si noti che questo è preso da qui .


8

In un linguaggio semplice, l'ambito lessicale è una variabile definita all'esterno dell'ambito o l'ambito superiore è automaticamente disponibile all'interno dell'ambito, il che significa che non è necessario passarlo lì.

Esempio:

let str="JavaScript";

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

myFun();

// Output: JavaScript


2
La risposta più breve e migliore per me con un esempio. Avrebbe potuto essere aggiunto che le funzioni freccia dell'ES6 risolvono il problema bind. Con loro, bindnon è più necessario. Per ulteriori informazioni su questa modifica di controllo stackoverflow.com/a/34361380/11127383
Daniel Danielecki

4

Manca una parte importante della conversazione relativa allo scoping lessicale e dinamico : una chiara spiegazione della durata della variabile con ambito o quando è possibile accedere alla variabile.

Lo scoping dinamico corrisponde solo molto vagamente allo scoping "globale" nel modo in cui tradizionalmente ci pensiamo (il motivo per cui ho sollevato il confronto tra i due è che è già stato menzionato - e non mi piace particolarmente il link spiegazione dell'articolo ); probabilmente è meglio non fare il confronto tra globale e dinamico - sebbene presumibilmente, secondo l'articolo collegato, "... [è] utile come sostituto di variabili con portata globale".

Quindi, in parole povere, qual è l'importante distinzione tra i due meccanismi di scoping?

L'ambito lessicale è stato definito molto bene in tutte le risposte di cui sopra: sono disponibili variabili o con ambito lessicale a livello locale della funzione in cui è stata definita.

Tuttavia, poiché non è al centro dell'OP, l'ambito dinamico non ha ricevuto molta attenzione e l'attenzione che ha ricevuto significa che probabilmente ha bisogno di un po 'di più (non è una critica ad altre risposte, ma piuttosto un "oh, quella risposta ci fece desiderare che ci fosse un po 'di più "). Quindi, ecco un po 'di più:

Scoping dinamico significa che una variabile è accessibile al programma più grande durante la durata della chiamata della funzione o, mentre la funzione è in esecuzione. Davvero, Wikipedia in realtà fa un buon lavoro con la spiegazione della differenza tra i due. Per non offuscarlo, ecco il testo che descrive lo scoping dinamico:

... [I] n scoping dinamico (o scope dinamico), se l'ambito di un nome di variabile è una determinata funzione, allora il suo ambito è il periodo di tempo durante il quale viene eseguita la funzione: mentre la funzione è in esecuzione, esiste il nome della variabile , ed è associato alla sua variabile, ma dopo che la funzione ritorna, il nome della variabile non esiste.


3

Ambito lessicale significa che una funzione cerca le variabili nel contesto in cui è stata definita e non nell'ambito immediatamente circostante.

Guarda come funziona l'ambito lessicale in Lisp se vuoi maggiori dettagli. La risposta selezionata da Kyle Cronin nelle variabili dinamiche e lessicali in Common Lisp è molto più chiara delle risposte qui.

Per coincidenza, l'ho imparato solo in una classe Lisp, e succede anche in JavaScript.

Ho eseguito questo codice nella console di 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)
})();                     //)

Produzione:

5
10
5

3

Un ambito lessicale in JavaScript significa che una variabile definita all'esterno di una funzione può essere accessibile all'interno di un'altra funzione definita dopo la dichiarazione della variabile. Ma non è vero il contrario; le variabili definite all'interno di una funzione non saranno accessibili al di fuori di tale funzione.

Questo concetto è ampiamente utilizzato nelle chiusure in JavaScript.

Diciamo che abbiamo il seguente codice.

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

Ora, quando chiami add () -> questo stamperà 3.

Pertanto, la funzione add () sta accedendo alla variabile globale xdefinita prima dell'aggiunta della funzione metodo. Questo viene chiamato a causa dell'ambito lessicale in JavaScript.


Considera che lo snippet di codice era per un linguaggio con ambito dinamico. Se la add()funzione fosse chiamata immediatamente dopo lo snippet di codice specificato, verrebbe anche stampata 3. L'ambito lessicale non significa semplicemente che una funzione può accedere alle variabili globali al di fuori del contesto locale. Quindi il codice di esempio in realtà non aiuta a mostrare cosa significa scoping lessicale. Mostrare l'ambito lessicale nel codice ha davvero bisogno di un contro esempio o almeno una spiegazione di altre possibili interpretazioni del codice.
C Perkins,

2

L'ambito lessicale si riferisce al lessico degli identificatori (ad es. Variabili, funzioni, ecc.) Visibili dalla posizione corrente nello stack di esecuzione.

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

foo e bar sono sempre nel lessico degli identificatori disponibili perché sono globali.

Quando function1viene eseguito, si ha accesso a un lessico di foo2, bar2, fooe bar.

Quando function2viene eseguito, si ha accesso a un lessico di foo3, bar3, foo2, bar2, foo, e bar.

Il motivo per cui le funzioni globali e / o esterne non hanno accesso agli identificatori di funzioni interne è perché l'esecuzione di quella funzione non è ancora avvenuta e pertanto nessuno dei suoi identificatori è stato assegnato alla memoria. Inoltre, una volta eseguito quel contesto interno, viene rimosso dallo stack di esecuzione, il che significa che tutti i suoi identificatori sono stati raccolti in modo inutile e non sono più disponibili.

Infine, è per questo che un contesto di esecuzione nidificata può SEMPRE accedere al suo contesto di esecuzione degli antenati e quindi perché ha accesso a un più ampio lessico di identificatori.

Vedere:

Un ringraziamento speciale a @ robr3rd per aiutare a semplificare la definizione di cui sopra.


1

Ecco un'angolazione diversa su questa domanda che possiamo ottenere facendo un passo indietro e osservando il ruolo dello scoping nel più ampio quadro di interpretazione (eseguendo un programma). In altre parole, immagina di creare un interprete (o un compilatore) per una lingua e di essere responsabile del calcolo dell'output, dato un programma e alcuni input ad esso.

L'interpretazione implica tenere traccia di tre cose:

  1. Stato: ovvero variabili e posizioni di memoria di riferimento nell'heap e nello stack.

  2. Operazioni su quello stato - vale a dire, ogni riga di codice nel programma

  3. L' ambiente in cui viene eseguita una determinata operazione , ovvero la proiezione di stato su un'operazione.

Un interprete inizia dalla prima riga di codice in un programma, calcola il suo ambiente, esegue la linea in quell'ambiente e acquisisce il suo effetto sullo stato del programma. Segue quindi il flusso di controllo del programma per eseguire la riga di codice successiva e ripete il processo fino al termine del programma.

Il modo in cui si calcola l'ambiente per qualsiasi operazione è attraverso un insieme formale di regole definite dal linguaggio di programmazione. Il termine "associazione" viene spesso utilizzato per descrivere la mappatura dello stato generale del programma su un valore nell'ambiente. Si noti che per "stato complessivo" non intendiamo stato globale, ma piuttosto la somma totale di ogni definizione raggiungibile, in qualsiasi momento dell'esecuzione).

Questo è il framework in cui è definito il problema di scoping. Ora alla prossima parte di quali sono le nostre opzioni.

  • Come implementatore dell'interprete, potresti semplificare il tuo compito rendendo l'ambiente il più vicino possibile allo stato del programma. Di conseguenza, l'ambiente di una riga di codice verrebbe semplicemente definito dall'ambiente della precedente riga di codice con gli effetti di tale operazione applicati ad essa, indipendentemente dal fatto che la riga precedente fosse un compito, una chiamata di funzione, ritorno da una funzione, o una struttura di controllo come un ciclo while.

Questo è l'essenza dell'ambito dinamico , in cui l'ambiente in cui viene eseguito qualsiasi codice è legato allo stato del programma come definito dal suo contesto di esecuzione.

  • Oppure , potresti pensare a un programmatore che usa la tua lingua e semplifica il compito di tenere traccia dei valori che una variabile può assumere. Ci sono troppi percorsi e troppe complessità implicate nel ragionamento sul risultato della totalità dell'esecuzione passata. Lo scopical lessico aiuta a fare ciò limitando l'ambiente corrente alla porzione di stato definita nel blocco, funzione o altra unità di ambito corrente e al suo genitore (cioè il blocco che racchiude l'orologio corrente o la funzione che ha chiamato la funzione attuale).

In altre parole, con ambito lessicale l'ambiente che ogni codice vede è destinato a dichiararsi associato a un ambito definito esplicitamente nella lingua, come un blocco o una funzione.


0

Antica domanda, ma ecco la mia opinione su di essa.

L'ambito lessicale (statico) si riferisce all'ambito di una variabile nel codice sorgente .

In un linguaggio come JavaScript, in cui le funzioni possono essere passate in giro e associate e ricollegate a oggetti vari, si potrebbe pensare che tale ambito dipenda da chi sta chiamando la funzione in quel momento, ma non è così. Cambiare l'ambito in questo modo sarebbe un ambito dinamico e JavaScript non lo fa, tranne forse con ilthis riferimento all'oggetto.

Per illustrare il punto:

var a='apple';

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

var test=doit();
test();

Nell'esempio, la variabile aè definita a livello globale, ma ombreggiata nella doit()funzione. Questa funzione restituisce un'altra funzione che, come vedi, si basa sua variabile al di fuori del proprio ambito.

Se lo esegui, scoprirai che il valore utilizzato è aardvark, non applequale, sebbene rientri nell'ambito ditest() funzione, non rientra nell'ambito lessicale della funzione originale. Cioè, l'ambito utilizzato è l'ambito come appare nel codice sorgente, non l'ambito in cui viene effettivamente utilizzata la funzione.

Questo fatto può avere conseguenze fastidiose. Ad esempio, potresti decidere che è più semplice organizzare le tue funzioni separatamente e quindi usarle quando arriva il momento, come in un gestore di eventi:

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>

Questo esempio di codice fa uno di ciascuno. Si può vedere che a causa dell'ambito lessicale, il pulsante Autilizza la variabile interna, mentre il pulsanteB no. Potresti finire per annidare le funzioni più di quanto ti piacerebbe.

A proposito, in entrambi gli esempi, noterai anche che le variabili interne con ambito lessicale persistono anche se la funzione di contenimento ha seguito il suo corso. Questo si chiama chiusura e si riferisce all'accesso di una funzione nidificata alle variabili esterne, anche se la funzione esterna è terminata. JavaScript deve essere abbastanza intelligente da determinare se tali variabili non sono più necessarie e, in caso contrario, è possibile raccoglierle.


-1

Normalmente imparo con l'esempio, ed ecco qualcosa:

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

-1

Questo argomento è fortemente correlato alla bindfunzione integrata e introdotto nelle funzioni freccia ECMAScript 6 . È stato davvero fastidioso, perché per ogni nuovo metodo di "classe" (funzione in realtà) che volevamo usare, dovevamo farlo bindper avere accesso all'ambito.

JavaScript per impostazione predefinita non imposta il suo ambito di thisfunzioni on (non imposta il contesto su this). Di default devi dire esplicitamente quale contesto vuoi avere.

Le funzioni freccia ottengono automaticamente il cosiddetto ambito lessicale (hanno accesso alla definizione della variabile nel suo blocco contenitore). Quando si usano le funzioni freccia, si lega automaticamente thisal punto in cui la funzione freccia è stata definita in primo luogo e il contesto di queste funzioni freccia è il suo blocco contenitore.

Guarda come funziona in pratica sugli esempi più semplici di seguito.

Prima delle funzioni freccia (nessun ambito lessicale per impostazione predefinita):

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"

Con le funzioni freccia (ambito lessicale di default):

const programming = {
  language: "JavaScript",
  getLanguage: function() {
    return this.language;
  }
}

const arrowFunction = () => {
    console.log(programming.getLanguage());
}

arrowFunction(); // Output: "JavaScript"
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.