Alle Variablen in den Geltungsbereich aufnehmen

194

Gibt es eine Möglichkeit, alle Variablen, die derzeit in Javascript enthalten sind, abzurufen?

Ian
quelle
Bezüglich Ihrer Antwort an Camsoft: Das ist eine ganz andere Frage. Ich habe meine Antwort aktualisiert, um sie zu adressieren.
TJ Crowder
3
Es ist nur eine allgemeine Frage, genauer zu sein, hilft nicht viel, da ich mit einer obskuren API mit schlechter Dokumentation arbeite.
Ian
Du meinst globale Variablen! Sie können die aufzählbaren globalen Variablen mit anzeigen for (v in this) alert(v);. Allerdings sind nicht alle Globals aufzählbar, und ich kenne keinen Standardweg, um eine Liste der nicht aufzählbaren zu erhalten.
Jason Orendorff
2
@ Jason - Nein, die Frage ist klar. Innerhalb einer Funktion in ihrem Umfang die Variablen werden die globalen Variablen umfassen this, die argumentsParameter und alle in umschließenden Bereiche definierten Variablen.
Tim Down
2
Deshalb vermisse ich Perls Symboltabellen. Gibt es Pläne, dies einer zukünftigen Version von Javascript hinzuzufügen?
Dexygen

Antworten:

84

Nein. "In Scope" -Variablen werden durch die "Scope Chain" bestimmt, auf die programmgesteuert nicht zugegriffen werden kann.

Ausführliche Informationen (ziemlich viele davon) finden Sie in der ECMAScript-Spezifikation (JavaScript). Hier ist ein Link zur offiziellen Seite, auf der Sie die kanonische Spezifikation (ein PDF) herunterladen können, und hier ist eine offiziellen, verlinkbaren HTML-Version.

Update basierend auf Ihrem Kommentar zu Camsoft

Die Variablen im Gültigkeitsbereich Ihrer Ereignisfunktion hängen davon ab, wo Sie Ihre Ereignisfunktion definieren und nicht davon, wie sie aufgerufen wird. Aber können Sie nützliche Informationen über das finden , was über an Ihre Funktion verfügbar ist thisund Argumente etwas entlang der Linien zu tun , was KennyTM hingewiesen ( for (var propName in ____)) da , dass Sie sagen , was auf verschiedene Objekte zur Verfügung steht Ihnen zur Verfügung gestellt ( thisund Argumente, wenn Sie Nicht sicher, welche Argumente sie Ihnen geben, können Sie über die herausfindenarguments Variable , die implizit für jede Funktion definiert ist.

Zusätzlich zu dem, was aufgrund der Definition Ihrer Funktion im Geltungsbereich liegt, können Sie herausfinden, was auf andere Weise noch verfügbar ist, indem Sie Folgendes tun:

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]);
    }
}

(Sie können dies erweitern, um weitere nützliche Informationen zu erhalten.)

Stattdessen würde ich wahrscheinlich einen Debugger wie die Entwicklertools von Chrome verwenden (auch wenn Sie Chrome normalerweise nicht für die Entwicklung verwenden) oder Firebug (auch wenn Sie Firefox normalerweise nicht für die Entwicklung verwenden) oder Dragonfly on Opera verwenden oder "F12 Developer Tools" im IE. Und lesen Sie alle JavaScript-Dateien durch, die sie Ihnen zur Verfügung stellen. Und schlagen Sie sie über den Kopf, um die richtigen Dokumente zu erhalten. :-)

TJ Crowder
quelle
Nebenbei bemerkt hat Google Chrome einen großartigen Debugger, der mit Firebug vergleichbar ist (mit etwas mehr Sahnehäubchen).
Swivel
4
@ Swivelgames: Oh, ich bevorzuge die Dev Tools von Chrome gegenüber Firefox + Firebug. Sie hatten 2010 einfach nicht viel zu tun :-)
TJ Crowder
1
@ tjcrowder In der Tat! Ich bemerkte, dass der Zeitstempel auf der Antwort vor einer Weile war :)
Swivel
98

Zwar antworten alle mit " Nein " und ich weiß, dass "Nein" die richtige Antwort ist, aber wenn Sie wirklich lokale Variablen benötigen gibt es einen eingeschränkten Weg einer Funktion abrufen müssen.

Betrachten Sie diese Funktion:

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

Sie können Ihre Funktion in eine Zeichenfolge konvertieren:

var s = f + '';

Sie erhalten die Funktionsquelle als Zeichenfolge

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

Jetzt können Sie einen Parser wie esprima verwenden , um Funktionscode zu analysieren und lokale Variablendeklarationen zu finden.

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

und finde Objekte mit:

obj.type == "VariableDeclaration"

im Ergebnis (ich habe console.log(x)unten entfernt ):

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

Ich habe dies in Chrome, Firefox und Node getestet.

Aber die Problem bei dieser Methode ist jedoch, dass Sie nur die Variablen in der Funktion selbst definiert haben. Zum Beispiel für diesen:

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

Sie haben nur Zugriff auf das x und nicht auf y . Sie können jedoch Ketten von Aufrufern (Argumente.callee.caller.caller.caller) in einer Schleife verwenden, um lokale Variablen von Aufruferfunktionen zu finden. Wenn Sie alle lokalen Variablennamen haben, haben Sie Bereichsvariablen . Mit den Variablennamen haben Sie mit einer einfachen Auswertung Zugriff auf Werte.

Iman
quelle
7
Hervorragende Technik, um das ursprüngliche Problem anzugehen. Verdient eine Gegenstimme.
deepelement
4
Die Variablen eines Aufrufers sind nicht unbedingt Bereichsvariablen. Außerdem sind Bereichsvariablen nicht unbedingt in der Aufrufkette enthalten. Variablen, die geschlossen wurden, können von einer Schließungsfunktion referenziert werden, wenn sie von einem Aufrufer aufgerufen werden, der außerhalb des Definitions- / Schließungsbereichs liegt (und auf dessen Variablen über den Abschlusskörper überhaupt nicht zugegriffen werden kann).
DDS
Könnten Sie bitte "eine einfache Bewertung" klar erklären? Ich habe es mit dem folgenden Code versucht, konnte jedoch keine Variable im Gültigkeitsbereich einfangen. function getCounter(start){ return function(){ start ++; return start; } } var counter = getCounter(1); var startInClosure = eval.call(counter, 'this.start;'); console.log(startInClosure);Es druckt, undefinedwährend ich erwarte, dass es 2 sein sollte.
Pylipala
@Pylipala Die Frage hier ist, ob Variablen im Gültigkeitsbereich keinen Wert für lokale Variablen von außerhalb des Gültigkeitsbereichs erhalten. Dies ist nicht möglich, da dies die Definition einer lokalen Variablen ist, auf die von außen nicht zugegriffen werden sollte. In Bezug auf Ihren Code meinen Sie Kontext nicht Bereich, aber Sie haben nicht start in den Kontext (this) gesetzt, sodass Sie ihn mit this.start nicht lesen können . Siehe hier: paste.ubuntu.com/11560449
Iman
@iman Danke für deine Antwort. Ich denke auch, dass es auf der Javascript-Seite nicht möglich ist, also denke ich, ob es auf der v8-Seite möglich ist. Ich habe unten einen Fragenlink gefunden , der mein Problem genau beschreibt. Darin sagt Lasse Reichstein nein, aber Mako-Taco sagt ja. Ich habe versucht, C ++ - Code als Link zu verwenden , weiß aber nicht, wie ich ihn schreiben soll.
Pylipala
32

Ja und nein. "Nein" in fast jeder Situation. "Ja", aber nur in begrenztem Umfang, wenn Sie den globalen Bereich überprüfen möchten. Nehmen Sie das folgende Beispiel:

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

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

Welche Ausgaben, unter 150+ anderen Dingen , die folgenden:

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

Es ist also möglich, einige Variablen im aktuellen Bereich aufzulisten, aber sie sind jedoch nicht zuverlässig, prägnant, effizient oder leicht zugänglich.

Eine bessere Frage ist, warum Sie wissen möchten, welche Variablen im Geltungsbereich liegen.

Justin Johnson
quelle
2
Ich denke, die Frage war allgemeiner und nicht auf Javascript im Kontext eines Webbrowsers beschränkt.
Radu Simionescu
Ändern Sie einfach das Wort "Fenster" für "global", wenn Sie Node verwenden ... oder Sie können das Wort "this" verwenden, aber das ist unter "use strict" verboten
Ivan Castellanos
Ich habe eine Zeilenüberprüfung für hasOwnProperty hinzugefügt. Zum Beispiel : for ( var i in window ) { if (window.hasOwnProperty(i)) { console.log(i, window[i]); }}. Dadurch werden zumindest die geerbten Eigenschaften reduziert, und Ihre Variablen gehören zu nur etwa 50 anderen Eigenschaften.
Timctran
8
Warum sollte nicht jemand während des Debuggens und / oder der Entwicklung zumindest überprüfen wollen, welche Variablen im Gültigkeitsbereich sind?
Dexygen
Ein weiterer Grund, um zu wissen, welche Variablen sich im lokalen Bereich befinden, ist die Serialisierung eines Abschlusses.
Michael
29

In ECMAScript 6 ist es mehr oder weniger möglich, den Code in eine withAnweisung mit einem Proxy-Objekt zu verpacken . Beachten Sie, dass ein nicht strenger Modus erforderlich ist und dass dies eine schlechte Praxis ist.

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

Der Proxy behauptet, alle darin angegebenen Bezeichner zu besitzen with, sodass variable Zuweisungen im Ziel gespeichert werden. Bei Suchvorgängen ruft der Proxy den Wert vom Proxy-Ziel oder vom globalen Objekt ab (nicht vom übergeordneten Bereich). letund constVariablen sind nicht enthalten.

Inspiriert von dieser Antwort von Bergi .

Oriol
quelle
1
Dies ist für eine so einfache Technik überraschend erfolgreich.
Jonathan Eunice
WOW Super Power
Pery Mimon
16

Das kannst du nicht.

Variablen, Bezeichner von Funktionsdeklarationen und Argumente für Funktionscode werden als Eigenschaften des Variablenobjekts gebunden , auf die nicht zugegriffen werden kann.

Siehe auch:

CMS
quelle
1
Richtig, aber die In-Scope-Variablen gehen über das aktuelle Variablenobjekt hinaus. In-Scope-Variablen werden durch die Scope-Kette bestimmt , bei der es sich um eine Kette handelt, die (im Normalfall) aus einer Reihe variabler Objekte besteht und mit dem globalen Objekt endet (obwohl die withAnweisung zum Einfügen anderer Objekte in die Kette verwendet werden kann).
TJ Crowder
+1 für Links zu bclary.com. Eine HTML-Version der aktuellen Spezifikation wird in den nächsten Monaten auf der ECMA-Website erscheinen.
TJ Crowder
3
Nur eine Anmerkung, beide Links sind kaputt.
0xc0de
Sie können - mit statischer Analyse jsfiddle.net/mathheadinclouds/bvx1hpfn/11
mathheadinclouds
8

Der einfachste Weg, um in einem bestimmten Bereich Zugang zu Vars zu erhalten

  1. Öffnen Sie Developer Tools> Resources (in Chrome).
  2. Öffnen Sie die Datei mit einer Funktion, die Zugriff auf diesen Bereich hat (Tipp cmd / Strg + p, um die Datei zu finden).
  3. Setzen Sie den Haltepunkt innerhalb dieser Funktion und führen Sie Ihren Code aus
  4. Wenn es an Ihrem Haltepunkt stoppt, können Sie über die Konsole (oder das Fenster "Bereichsvariable") auf die Bereichsvariable zugreifen.

Hinweis: Sie möchten dies gegen nicht minimierte js tun.

Der einfachste Weg, alle nicht privaten Vars anzuzeigen

  1. Konsole öffnen (in Chrome)
  2. Typ: this.window
  3. Drücke Enter

Jetzt sehen Sie einen Objektbaum, den Sie mit allen deklarierten Objekten erweitern können.

Timothy Perez
quelle
7

Wie jeder bemerkte: Sie können nicht. Sie können jedoch ein Objekt erstellen und jede Variable, die Sie deklarieren, diesem Objekt zuweisen. Auf diese Weise können Sie ganz einfach Ihre Vars überprüfen:

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
rafaelcastrocouto
quelle
1
+1 danke für cooles Beispiel, nur für die Fertigstellung kann das gleiche auch über console.log(window); jsfiddle.net/4x3Tx
Stano
5

Ich habe eine Geige gemacht, die (im Wesentlichen) die von iman skizzierten Ideen umsetzt. So sieht es aus, wenn Sie mit der Maus über das zweite Ipsum in fahrenreturn ipsum*ipsum - ...

Geben Sie hier die Bildbeschreibung ein

Die Variablen, die sich im Gültigkeitsbereich befinden, werden dort hervorgehoben, wo sie deklariert sind (mit unterschiedlichen Farben für unterschiedliche Gültigkeitsbereiche). Daslorem mit rotem Rand ist eine schattierte Variable (nicht im Gültigkeitsbereich, aber im Gültigkeitsbereich, wenn das andere Lorem weiter unten im Baum nicht vorhanden wäre.)

Ich verwende die esprima-Bibliothek, um das JavaScript zu analysieren, und estraverse, escodegen, escope (Dienstprogrammbibliotheken über esprima). Das "schwere Heben" wird von all diesen Bibliotheken durchgeführt (das komplexeste ist natürlich esprima selbst).

Wie es funktioniert

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

macht den abstrakten Syntaxbaum. Dann,

analysis = escope.analyze(ast);

generiert eine komplexe Datenstruktur, die Informationen zu allen Bereichen des Programms enthält. Der Rest sammelt die in diesem Analyseobjekt (und dem abstrakten Syntaxbaum selbst) codierten Informationen und erstellt daraus ein interaktives Farbschema.

Die richtige Antwort lautet also eigentlich nicht "nein", sondern "ja, aber". Das "aber" ist ein großes: Sie müssen grundsätzlich wichtige Teile des Chrome-Browsers (und seiner Devtools) in JavaScript neu schreiben. JavaScript ist eine vollständige Turing-Sprache, das ist natürlich grundsätzlich möglich. Was unmöglich ist, ist das Ganze zu tun, ohne den gesamten Quellcode (als Zeichenfolge) zu verwenden und dann hochkomplexe Dinge damit zu tun.

Mathheadinclouds
quelle
3

Wenn Sie die Variablen nur manuell überprüfen möchten, um das Debuggen zu erleichtern, starten Sie einfach den Debugger:

debugger;

Direkt in die Browserkonsole.

David Meister
quelle