Is there a way to get all variables that are currently in scope in javascript?
You can't.
Variables, identifiers of function declarations and arguments for function code, are bound as properties of the Variable Object, which is not accesible.
See also:
If you hate your cpu you can bruteforce through every valid variable name, and eval
each one to see if it results in a value!
The following snippet tries the first 1000 bruteforce strings, which is enough to find the contrived variable names in scope:
let alpha = 'abcdefghijklmnopqrstuvwxyz';
let everyPossibleString = function*() {
yield '';
for (let prefix of everyPossibleString()) for (let char of alpha) yield `${prefix}${char}`;
};
let allVarsInScope = (iterations=1000) => {
let results = {};
let count = 0;
for (let bruteforceString of everyPossibleString()) {
if (!bruteforceString) continue; // Skip the first empty string
try { results[bruteforceString] = eval(bruteforceString); } catch(err) {}
if (count++ > iterations) break;
}
return results;
};
let myScope = (() => {
let dd = 'ffffd';
let ee = 'eee';
let ff = 'fff';
((gg, hh) => {
// We can't call a separate function, since that function would be outside our
// scope and wouldn't be able to see any variables - but we can define the
// function in place (using `eval(allVarsInScope.toString())`), and then call
// that defined-in-place function
console.log(eval(allVarsInScope.toString())());
})('ggg', 'hhh');
})();
This script will eventually (after a very long time) find all scoped variable names, as well as abc
nifty
and swell
, some example variables I created. Note it will only find variable names consisting of alpha characters.
let preElem = document.getElementsByClassName('display')[0];
let statusElem = document.getElementsByClassName('status')[0];
let alpha = 'abcdefghijklmnopqrstuvwxyz';
alpha += alpha.toUpperCase();
let everyPossibleString = function*() {
yield '';
for (let prefix of everyPossibleString()) for (let char of alpha) yield `${prefix}${char}`;
};
(async () => {
let abc = 'This is the ABC variable :-|';
let neato = 'This is the NEATO variable :-)';
let swell = 'This is the SWELL variable :-D';
let results = {};
let batch = 25000;
let waitMs = 25;
let count = 0;
let startStr = null;
for (let bruteStr of everyPossibleString()) {
try {
if (bruteStr === '') continue;
if (startStr === null) startStr = bruteStr;
try { results[bruteStr] = eval(bruteStr); } catch(err) {}
if (count++ >= batch) {
statusElem.innerHTML = `Did batch of ${batch} from ${startStr} -> ${bruteStr}`;
preElem.innerHTML = JSON.stringify(results, null, 2);
count = 0;
startStr = null;
await new Promise(r => setTimeout(r, waitMs));
}
} catch(err) {
// It turns out some global variables are protected by stackoverflow's snippet
// system (these include "top", "self", and "this"). If these values are touched
// they result in a weird iframe error, captured in this `catch` statement. The
// program can recover by replacing the most recent `result` value (this will be
// the value which causes the error).
let lastEntry = Object.entries(results).slice(-1)[0];
results[lastEntry[0]] = '<a protected value>';
}
}
console.log('Done...'); // Will literally never happen
})();
html, body { position: fixed; left: 0; top: 0; right: 0; bottom: 0; margin: 0; padding: 0; overflow: hidden }
.display {
position: fixed;
box-sizing: border-box;
left: 0; top: 0;
bottom: 30px; right: 0;
overflow-y: scroll;
white-space: pre;
font-family: monospace;
padding: 10px;
box-shadow: inset 0 0 10px 1px rgba(0, 0, 0, 0.3);
}
.status {
position: fixed;
box-sizing: border-box;
left: 0; bottom: 0px; right: 0; height: 30px; line-height: 30px;
padding: 0 10px;
background-color: rgba(0, 0, 0, 1);
color: rgba(255, 255, 255, 1);
font-family: monospace;
}
<div class="display"></div>
<div class="status"></div>
I am all too aware there is virtually no situation where this is practical
Although everyone answer "No" and I know that "No" is the right answer but if you really need to get local variables of a function there is a restricted way.
Consider this function:
var f = function() {
var x = 0;
console.log(x);
};
You can convert your function to a string:
var s = f + '';
You will get source of function as a string
'function () {\nvar x = 0;\nconsole.log(x);\n}'
Now you can use a parser like esprima to parse function code and find local variable declarations.
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);
and find objects with:
obj.type == "VariableDeclaration"
in the result (I have removed console.log(x)
below):
{
"type": "Program",
"body": [
{
"type": "VariableDeclaration",
"declarations": [
{
"type": "VariableDeclarator",
"id": {
"type": "Identifier",
"name": "x"
},
"init": {
"type": "Literal",
"value": 0,
"raw": "0"
}
}
],
"kind": "var"
}
]
}
I have tested this in Chrome, Firefox and Node.
But the problem with this method is that you just have the variables defined in the function itself. For example for this one:
var g = function() {
var y = 0;
var f = function() {
var x = 0;
console.log(x);
};
}
you just have access to the x and not y. But still you can use chains of caller (arguments.callee.caller.caller.caller) in a loop to find local variables of caller functions. If you have all local variable names so you have scope variables. With the variable names you have access to values with a simple eval.
If you just want to inspect the variables manually to help debug, just fire up the debugger:
debugger;
Straight into the browser console.