Ottenere tutte le variabili in ambito


194

C'è un modo per ottenere tutte le variabili che sono attualmente nell'ambito nell'ambito di JavaScript?


Per quanto riguarda la tua risposta a Camsoft: è una domanda completamente diversa; Ho aggiornato la mia risposta per affrontarla.
TJ Crowder,

3
È solo una domanda generale, essere più specifici non aiuterà molto poiché sto lavorando con un'API oscura con scarsa documentazione.
Ian,

Intendi le variabili globali! È possibile visualizzare le variabili globali enumerabili utilizzando for (v in this) alert(v);. Tuttavia, non tutti i globuli sono enumerabili e non conosco alcun modo standard per ottenere un elenco di quelli non enumerabili.
Jason Orendorff il

2
@Jason - No, la domanda è chiara. All'interno di una funzione delle variabili nell'ambito includeranno variabili globali, this, arguments, parametri e tutte le variabili definite racchiude ambiti.
Tim Down,

2
Questo è il motivo per cui mi mancano le tabelle dei simboli di Perl. Hai intenzione di aggiungerlo a una versione futura di Javascript?
Dexygen,

Risposte:


84

No. Le variabili "nell'ambito" sono determinate dalla "catena dell'ambito", che non è accessibile a livello di programmazione.

Per i dettagli (parecchio), controlla la specifica ECMAScript (JavaScript). Ecco un link alla pagina ufficiale in cui è possibile scaricare le specifiche canoniche (un PDF), ed eccone uno alla versione HTML ufficiale e collegabile.

Aggiornamento basato sul tuo commento a Camsoft

Le variabili nell'ambito della funzione dell'evento sono determinate da dove si definisce la funzione dell'evento, non da come la chiamano. Tuttavia , potresti trovare informazioni utili su ciò che è disponibile per la tua funzione tramite thise argomenti facendo qualcosa in linea con ciò che KennyTM ha indicato ( for (var propName in ____)) poiché ciò ti dirà cosa è disponibile su vari oggetti forniti ( thise argomenti; se sei non sei sicuro di quali argomenti ti danno, puoi scoprire tramite la argumentsvariabile che è implicitamente definita per ogni funzione).

Quindi oltre a qualunque cosa rientri nell'ambito in cui si definisce la propria funzione, è possibile scoprire cos'altro è disponibile in altri modi facendo:

var n, arg, name;
alert("typeof this = " + typeof this);
for (name in this) {
    alert("this[" + name + "]=" + this[name]);
}
for (n = 0; n < arguments.length; ++n) {
    arg = arguments[n];
    alert("typeof arguments[" + n + "] = " + typeof arg);
    for (name in arg) {
        alert("arguments[" + n + "][" + name + "]=" + arg[name]);
    }
}

(Puoi estenderlo per ottenere informazioni più utili.)

Invece, probabilmente userei un debugger come gli strumenti di sviluppo di Chrome (anche se normalmente non usi Chrome per lo sviluppo) o Firebug (anche se non usi normalmente Firefox per lo sviluppo) o Dragonfly su Opera o "Strumenti per sviluppatori F12" su Internet Explorer. E leggi tutti i file JavaScript che ti forniscono. E batterli sopra la testa per i documenti adeguati. :-)


Come nota a margine, Google Chrome ha un ottimo debugger paragonabile a Firebug (con anche un po 'più di ghiaccio).
Swivel

4
@Swivelgames: Oh, preferisco di gran lunga gli strumenti Dev di Chrome a Firefox + Firebug. Nel 2010 non erano molto in gamba :-)
TJ Crowder,

1
@tjcrowder Indeed! Ho notato che il timestamp sulla risposta era qualche tempo fa :)
Swivel

98

Sebbene tutti rispondano " No " e so che "No" è la risposta giusta, ma se hai davvero bisogno di ottenere variabili locali di una funzione c'è un modo limitato.

Considera questa funzione:

var f = function() {
    var x = 0;
    console.log(x);
};

Puoi convertire la tua funzione in una stringa:

var s = f + '';

Otterrai l'origine della funzione come stringa

'function () {\nvar x = 0;\nconsole.log(x);\n}'

Ora puoi usare un parser come esprima per analizzare il codice funzione e trovare dichiarazioni di variabili locali.

var s = 'function () {\nvar x = 0;\nconsole.log(x);\n}';
s = s.slice(12); // to remove "function () "
var esprima = require('esprima');
var result = esprima.parse(s);

e trova oggetti con:

obj.type == "VariableDeclaration"

nel risultato (ho rimosso di console.log(x)seguito):

{
    "type": "Program",
    "body": [
        {
            "type": "VariableDeclaration",
            "declarations": [
                {
                    "type": "VariableDeclarator",
                    "id": {
                        "type": "Identifier",
                        "name": "x"
                    },
                    "init": {
                        "type": "Literal",
                        "value": 0,
                        "raw": "0"
                    }
                }
            ],
            "kind": "var"
        }
    ]
}

Ho provato questo in Chrome, Firefox e Node.

Ma il problema con questo metodo è che hai solo le variabili definite nella funzione stessa. Ad esempio per questo:

var g = function() {
    var y = 0;
    var f = function() {
        var x = 0;
        console.log(x);
    };
}

hai solo accesso a xe non a y . Tuttavia, è possibile utilizzare le catene del chiamante (argomenti.callee.caller.caller.caller) in un ciclo per trovare le variabili locali delle funzioni del chiamante. Se hai tutti i nomi delle variabili locali, quindi hai le variabili dell'ambito . Con i nomi delle variabili hai accesso ai valori con una semplice valutazione.


7
Ottima tecnica qui per affrontare il problema originale. Merita un voto.
deepelement

4
Le variabili di un chiamante non sono necessariamente variabili dell'ambito. Inoltre, le variabili dell'ambito non sono necessariamente nella catena di chiamate. Le variabili che sono state chiuse possono essere referenziate da una funzione di chiusura al momento della chiamata da un chiamante che è al di fuori dell'ambito della definizione / chiusura (e le cui variabili non sono affatto accessibili dal corpo della chiusura).
DDS

Potresti spiegare chiaramente "un semplice giudizio"? Ho provato con il codice qui sotto ma non sono riuscito a bloccare la variabile nell'ambito. function getCounter(start){ return function(){ start ++; return start; } } var counter = getCounter(1); var startInClosure = eval.call(counter, 'this.start;'); console.log(startInClosure);Esso stampa undefinedmentre mi aspetto che dovrebbe essere 2.
Pylipala

@Pylipala La domanda qui riguarda le variabili nell'ambito che non ottengono il valore di variabili locali al di fuori dell'ambito. Questo non è possibile, perché questa è la definizione di variabile locale che non dovrebbe essere accessibile dall'esterno. Per quanto riguarda il tuo codice, intendi contesto non ambito ma non hai iniziato nel contesto (questo), quindi non puoi leggerlo con this.start . Vedi qui: paste.ubuntu.com/11560449
iman

@iman Grazie per la risposta. Suppongo anche che non sia possibile sul lato Javascript, quindi immagino che sia possibile sul lato v8. Ho trovato sotto il link della domanda che descrive accuratamente il mio problema. In esso Lasse Reichstein dice di no ma mako-taco dice di sì. Ho provato un po 'di codice C ++ come link ma non so come scriverlo.
Pylipala,

32

Sì e no. "No" in quasi ogni situazione. "Sì", ma solo in modo limitato, se si desidera verificare l'ambito globale. Prendi il seguente esempio:

var a = 1, b = 2, c = 3;

for ( var i in window ) {
    console.log(i, typeof window[i], window[i]);
}

Che produce, tra le 150+ cose , le seguenti:

getInterface function getInterface()
i string i // <- there it is!
c number 3
b number 2
a number 1 // <- and another
_firebug object Object firebug=1.4.5 element=div#_firebugConsole
"Firebug command line does not support '$0'"
"Firebug command line does not support '$1'"
_FirebugCommandLine object Object
hasDuplicate boolean false

Quindi è possibile elencare alcune variabili nell'ambito attuale, ma non è affidabile, conciso, efficiente o facilmente accessibile.

Una domanda migliore è: perché vuoi sapere quali variabili rientrano nell'ambito di applicazione?


2
Penso che la domanda fosse più generale e non limitata a javascript nel contesto di un browser web.
Radu Simionescu,

Basta cambiare la parola "finestra" per "globale" se si utilizza il nodo ... oppure è possibile utilizzare la parola "questo" ma è vietato in "Usa rigoroso"
Ivan Castellanos,

Ho aggiunto una riga per verificare hasOwnProperty. Ad esempio: for ( var i in window ) { if (window.hasOwnProperty(i)) { console.log(i, window[i]); }}. Ciò ridurrà almeno le proprietà ereditate e le variabili saranno tra solo circa altre 50 proprietà.
timctran,

8
Perché qualcuno non dovrebbe almeno voler controllare quali variabili rientrano nell'ambito, durante il debug e / o gli scopi di sviluppo.
Dexygen,

Un altro motivo per voler sapere quali variabili sono nell'ambito locale è serializzare una chiusura.
Michael,

29

In ECMAScript 6 è più o meno possibile racchiudendo il codice all'interno di withun'istruzione con un oggetto proxy. Nota che richiede una modalità non rigorosa ed è una cattiva pratica.

function storeVars(target) {
  return new Proxy(target, {
    has(target, prop) { return true; },
    get(target, prop) { return (prop in target ? target : window)[prop]; }
  });
}
var vars = {}; // Outer variable, not stored.
with(storeVars(vars)) {
  var a = 1;   // Stored in vars
  var b = 2;   // Stored in vars
  (function() {
    var c = 3; // Inner variable, not stored.
  })();
}
console.log(vars);

Il proxy afferma di possedere tutti gli identificatori a cui si fa riferimento all'interno with, quindi le assegnazioni di variabili vengono memorizzate nella destinazione. Per le ricerche, il proxy recupera il valore dalla destinazione proxy o dall'oggetto globale (non dall'ambito padre). lete le constvariabili non sono incluse.

Ispirato da questa risposta di Bergi .


1
Questo ha sorprendentemente successo per una tecnica così semplice.
Jonathan Eunice,

WOW super power
pery mimon

16

Non puoi.

Le variabili, gli identificatori delle dichiarazioni di funzioni e gli argomenti per il codice di funzione, sono associati come proprietà dell'oggetto variabile , che non è accessibile.

Guarda anche:


1
Vero, ma le variabili nell'ambito rientrano solo nell'oggetto variabile corrente. Le variabili nell'ambito sono determinate dalla catena dell'ambito , che è una catena costituita (nel caso normale) da una serie di oggetti variabili e che termina con l'oggetto globale (sebbene l' withistruzione possa essere utilizzata per inserire altri oggetti nella catena).
TJ Crowder,

+1 per i collegamenti a bclary.com. Una versione HTML delle specifiche aggiornate apparirà nei prossimi due mesi sul sito web dell'ECMA, sono lieto di poter dire.
TJ Crowder,

3
Solo una nota, entrambi i collegamenti sono interrotti.
0xc0de,


8

Il modo più semplice per accedere a Vars in un ambito particolare

  1. Apri Strumenti per gli sviluppatori> Risorse (in Chrome)
  2. Apri il file con una funzione che ha accesso a quell'ambito (punta cmd / ctrl + p per trovare il file)
  3. Imposta il punto di interruzione all'interno di quella funzione ed esegui il tuo codice
  4. Quando si ferma al punto di interruzione, è possibile accedere all'ambito var tramite la console (o la finestra var dell'ambito)

Nota: vuoi farlo contro js non minificati.

Il modo più semplice per mostrare tutti i Vars non privati

  1. Open Console (in Chrome)
  2. Digitare: this.window
  3. Premi Invio

Ora vedrai un albero di oggetti che puoi espandere con tutti gli oggetti dichiarati.


7

Come tutti hanno notato: non puoi. Ma puoi creare un oggetto e assegnare ogni var che dichiari a quell'oggetto. In questo modo puoi facilmente controllare i tuoi var:

var v = {}; //put everything here

var f = function(a, b){//do something
}; v.f = f; //make's easy to debug
var a = [1,2,3];
v.a = a;
var x = 'x';
v.x = x;  //so on...

console.log(v); //it's all there

1
+1 grazie per un bell'esempio, solo per il completamento lo stesso può essere visto anche tramite console.log(window); jsfiddle.net/4x3Tx
Stano

5

Ho fatto un violino implementando (essenzialmente) sopra le idee delineate da iman. Ecco come appare quando passi il mouse sul secondo ipsum inreturn ipsum*ipsum - ...

inserisci qui la descrizione dell'immagine

Le variabili che rientrano nell'ambito vengono evidenziate nel punto in cui sono dichiarate (con colori diversi per ambiti diversi). Il lorembordo rosso è una variabile ombreggiata (non in ambito, ma sii in ambito se l'altro lorem più in basso l'albero non ci sarebbe.)

Sto usando la libreria esprima per analizzare JavaScript, ed estraverse, escodegen, escope (librerie di utilità in cima a esprima.) Il "sollevamento pesante" è fatto da tutte quelle librerie (il più complesso è lo stesso esprima, ovviamente.)

Come funziona

ast = esprima.parse(sourceString, {range: true, sourceType: 'script'});

rende l'albero di sintassi astratto. Poi,

analysis = escope.analyze(ast);

genera una struttura dati complessa che incapsula informazioni su tutti gli ambiti nel programma. Il resto sta raccogliendo le informazioni codificate in quell'oggetto di analisi (e l'albero di sintassi astratto stesso) e ricavandone uno schema di colorazione interattivo.

Quindi la risposta corretta non è in realtà "no", ma "sì, ma". Il "ma" è grande: in pratica devi riscrivere parti significative del browser Chrome (ed è devtools) in JavaScript. JavaScript è un linguaggio completo di Turing, quindi ovviamente è possibile, in linea di principio. Ciò che è impossibile è fare tutto senza usare l'intero codice sorgente (come una stringa) e quindi fare cose molto complesse con quello.


3

Se vuoi solo ispezionare manualmente le variabili per aiutare il debug, avvia il debugger:

debugger;

Direttamente nella console del browser.

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.