Non hai frainteso le closure e lo scope delle variabili, il debugger di Chrome ti trolla

Supponiamo di dover aggiungere una nuova funzionalità ad un modulo relativamente complesso già esistente, al suo interno troveremo già dichiarate diverse variabili locali come dipendenze esterne e degli alias per percorsi di oggetti particolarmente verbosi.

Tendenzialmente l’approccio che uso quando devo esplorare un componente o un flusso nuovo è quello di impostare un breakpoint nel punto desiderato e in quel punto osservare il ruolo degli elementi a mia dispozione in quel contesto. Particolarmente utile mi risulta il pannello watch expression con il quale tengo sotto controllo eventuali modelli e variabili che vengono modificati come side-effect dell’esecuzione di determinate funzioni.

Durante una delle mie sessioni esplorative ho pensato di fermarmi all’interno della funzione ancora vuota che doveva essere chiamata in risposta ad un evento e da li analizzare la situazione, e mi sono accorto che molti elementi del contesto superiore non risultano accessibili.

1

Closure, scope e variabili locali

Una delle nozioni fondamentali con cui si viene subito a contatto parlando di JavaScript è la demonizzazione dell’uso del global scope e di come invece avvalersi delle closure ovvero di variabili locali dichiarate all’interno di funzioni chiamate esternamente sia la salvezza per evitare il fatidico namespace conflict o Clash of Vars.

clash-of-vars-JSAspetto fondamentale del meccanismo closure è che i contesti superiori non hanno accesso alle variabili dichiarate in quelli interni annidati, ma quelli interni possono a ritroso accedere alle variabili di tutti i contesti superiori dello stack, fino a quello globale.

Questo comportamento viene rispettato da tutti gli interpreti JavaScript in fase di esecuzione, ma non in fase di debugging, dove a fare eccezzione è il motore V8 di Chrome, impiegato anche nelle ultime versioni di Opera.

È un’ottimizzazione di memoria

Il ragionamento fatto in V8 è: se nessuno dei contesti inferiori fa mai riferimento ad una specifica variabile è inutile allocare memoria per salvare un riferimento che non viene mai utilizzato. Chiaramente ciò comporta dei vantaggi in termini di velocità di esecuzione runtime una volta che il codice è “finalizzato” e deve solo essere eseguito dal browser, è invece piuttosto controintuitivo per lo sviluppatore che si scontra con un’incongruenza tra ciò che il linguaggio prescrive e ciò che fa il browser.

V8 engine Chrome and Opera debugger closure
V8 engine Chrome and Opera debugger closure

A tal proposito era stato aperto nel lontano 2014 l’Issue 3491 per evidenziare come tale incongruenza si trasformi di fatto in un ostacolo in diverse situazioni in cui uno sviluppatore si trova a debuggare del codice non suo, purtroppo con scarso successo dato che il ticket è stato chiuso senza essere risolto nel 2015.

Ad aggiungere potenziale confusione è il fatto che solo in caso di presenza del comando eval il motore alloca effettivamente ogni variabile dello stack superiore a prescindere che sia referenziata o no nel corpo della funzione interna, dato che in caso di eval tutto può succedere, tuttavia le variabili seppur visibili dal pannello “Scope” del debugger non risultano comunque utilizzabili dallo sviluppatore nel terminale del browser.

chrome eval

E gli altri browser?

Se questo comportamento dovesse rilevarsi un rompicapo bloccante mentre si cerca di risolvere un baco ci si può rivolgere serenamente alla concorrenza, ovvero FireFox e Safari, in cui il codice JavaScript viene eseguito come da specifiche ECMAScript senza particolari sorprese, in stile WYSIWYG.

https://sresc.io/tEF

Lascia un commento

Questo sito usa Akismet per ridurre lo spam. Scopri come i tuoi dati vengono elaborati.