[javascript] Getting All Variables In Scope

Is there a way to get all variables that are currently in scope in javascript?

This question is related to javascript

The answer is


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:


As everyone noticed: you can't. But you can create a obj and assign every var you declare to that obj. That way you can easily check out your vars:

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

The Simplest Way to Get Access to Vars in a Particular Scope

  1. Open Developer Tools > Resources (in Chrome)
  2. Open file with a function that has access to that scope (tip cmd/ctrl+p to find file)
  3. Set breakpoint inside that function and run your code
  4. When it stops at your breakpoint, you can access the scope var through console (or scope var window)

Note: You want to do this against un-minified js.

The Simplest Way to Show All Non-Private Vars

  1. Open Console (in Chrome)
  2. Type: this.window
  3. Hit Enter

Now you will see an object tree you can expand with all declared objects.


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.


Yes and no. "No" in almost every situation. "Yes," but only in a limited manner, if you want to check the global scope. Take the following example:

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

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

Which outputs, amongst 150+ other things, the following:

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

So it is possible to list some variables in the current scope, but it is not reliable, succinct, efficient, or easily accessible.

A better question is why do you want to know what variables are in scope?


How much time do you have?

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:

_x000D_
_x000D_
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 = 'ddd';
  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');
  
})();
_x000D_
_x000D_
_x000D_

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.

_x000D_
_x000D_
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

})();
_x000D_
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;
}
_x000D_
<div class="display"></div>
<div class="status"></div>
_x000D_
_x000D_
_x000D_

I am all too aware there is virtually no situation where this is practical


I made a fiddle implementing (essentially) above ideas outlined by iman. Here is how it looks when you mouse over the second ipsum in return ipsum*ipsum - ...

enter image description here

The variables which are in scope are highlighted where they are declared (with different colors for different scopes). The lorem with red border is a shadowed variable (not in scope, but be in scope if the other lorem further down the tree wouldn't be there.)

I'm using esprima library to parse the JavaScript, and estraverse, escodegen, escope (utility libraries on top of esprima.) The 'heavy lifting' is done all by those libraries (the most complex being esprima itself, of course.)

How it works

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

makes the abstract syntax tree. Then,

analysis = escope.analyze(ast);

generates a complex data structure encapsulating information about all the scopes in the program. The rest is gathering together the information encoded in that analysis object (and the abstract syntax tree itself), and making an interactive coloring scheme out of it.

So the correct answer is actually not "no", but "yes, but". The "but" being a big one: you basically have to rewrite significant parts of the chrome browser (and it's devtools) in JavaScript. JavaScript is a Turing complete language, so of course that is possible, in principle. What is impossible is doing the whole thing without using the entirety of your source code (as a string) and then doing highly complex stuff with that.


In ECMAScript 6 it's more or less possible by wrapping the code inside a with statement with a proxy object. Note it requires non-strict mode and it's bad practice.

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

The proxy claims to own all identifiers referenced inside with, so variable assignments are stored in the target. For lookups, the proxy retrieves the value from the proxy target or the global object (not the parent scope). let and const variables are not included.

Inspired by this answer by Bergi.