获取范围内的所有变量

Ian*_*Ian 184 javascript

有没有办法获取当前在javascript范围内的所有变量?

ima*_*man 91

虽然每个人都回答" ",但我知道"否"是正确的答案,但如果你真的需要获得函数的局部变量,那么就有一种限制的方式.

考虑这个功能:

var f = function() {
    var x = 0;
    console.log(x);
};
Run Code Online (Sandbox Code Playgroud)

您可以将函数转换为字符串:

var s = f + '';
Run Code Online (Sandbox Code Playgroud)

您将获得函数源作为字符串

'function () {\nvar x = 0;\nconsole.log(x);\n}'
Run Code Online (Sandbox Code Playgroud)

现在,您可以使用像esprima这样的解析器来解析函数代码并查找局部变量声明.

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);
Run Code Online (Sandbox Code Playgroud)

并找到对象:

obj.type == "VariableDeclaration"
Run Code Online (Sandbox Code Playgroud)

在结果中(我已删除console.log(x)):

{
    "type": "Program",
    "body": [
        {
            "type": "VariableDeclaration",
            "declarations": [
                {
                    "type": "VariableDeclarator",
                    "id": {
                        "type": "Identifier",
                        "name": "x"
                    },
                    "init": {
                        "type": "Literal",
                        "value": 0,
                        "raw": "0"
                    }
                }
            ],
            "kind": "var"
        }
    ]
}
Run Code Online (Sandbox Code Playgroud)

我已经在Chrome,Firefox和Node中对此进行了测试.

但是这个方法的问题在于你只需要在函数本身中定义变量.例如这个:

var g = function() {
    var y = 0;
    var f = function() {
        var x = 0;
        console.log(x);
    };
}
Run Code Online (Sandbox Code Playgroud)

你只能访问x而不是y.但是你仍然可以在循环中使用调用者链(arguments.callee.caller.caller.caller)来查找调用函数的局部变量.如果您有所有本地变量名称,那么您有范围变量.使用变量名称,您可以使用简单的eval访问值.

  • 解决原始问题的优秀技术.值得一提. (7认同)
  • 调用者的变量不一定是范围变量.此外,范围变量不一定在调用链中.已经被关闭的变量可以在从定义/闭包范围之外的调用者调用它时被闭包函数引用(并且其变量完全不能通过使用闭包的主体来访问). (4认同)

T.J*_*der 82

否."范围内"变量由"范围链"确定,该范围链无法以编程方式访问.

有关详细信息(非常多),请查看ECMAScript(JavaScript)规范.这里是官方页面的链接,您可以在其中下载规范规范(PDF),这里是官方可链接HTML版本的链接.

根据您对Camsoft的评论进行更新

事件函数范围内的变量取决于您定义事件函数的位置,而不是它们如何调用它.但是,您可以通过thisKennyTM指出的方式(for (var propName in ____))来找到有关您的函数可用的有用信息以及参数的有用信息,因为这将告诉您提供给您的各种对象的可用内容(this以及参数;如果您是不知道他们给你什么参数,你可以通过arguments为每个函数隐式定义的变量找出.

因此,除了您定义函数的位置之外,您还可以通过以下方式找到其他可用的内容:

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]);
    }
}
Run Code Online (Sandbox Code Playgroud)

(您可以扩展它以获得更多有用的信息.)

不过,我可能会使用像Chrome开发工具这样的调试器(即使你通常不使用Chrome进行开发)或Firebug(即使你通常不使用Firefox进行开发),也可能使用Opera上的Dragonfly ,或IE上的"F12开发人员工具".并阅读他们为您提供的任何JavaScript文件.并且为了正确的文档而击败他们.:-)

  • @Swivelgames:哦,我更喜欢Chrome的Dev Tools和Firefox + Firebug.他们在2010年的表现并不多.:-) (4认同)
  • @tjcrowder 确实!我注意到答案上的时间戳是前一段时间:) (2认同)

Jus*_*son 32

是的,不是.几乎在所有情况下都"不"."是的,"但是只有在有限的方式,如果你想检查全球范围.请看以下示例:

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

for ( var i in window ) {
    console.log(i, typeof window[i], window[i]);
}
Run Code Online (Sandbox Code Playgroud)

其中150多个其他产品的输出如下:

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
Run Code Online (Sandbox Code Playgroud)

因此,可以在当前范围中列出一些变量,但它不可靠,简洁,高效或易于访问.

一个更好的问题是为什么你想知道范围内的变量是什么?

  • 为什么在调试和/或开发过程中,至少有人不想检查范围内的变量. (7认同)
  • 我认为这个问题更笼统,不限于 Web 浏览器上下文中的 javascript。 (2认同)

Ori*_*iol 18

在ECMAScript 6中,通过with使用代理对象将代码包装在语句中或多或少是可能的.注意它需要非严格模式,这是不好的做法.

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);
Run Code Online (Sandbox Code Playgroud)

代理声称拥有内部引用的所有标识符with,因此变量赋值存储在目标中.对于查找,代理将从代理目标或全局对象(而不是父作用域)中检索值.letconst变量不包括在内.

灵感来自这个答案BERGI.

  • 这是什么巫术 (3认同)
  • 对于如此简单的技术来说,这是令人惊讶的成功。 (2认同)

CMS*_*CMS 16

你不能.

变量,函数声明的标识符和函数代码的参数被绑定为变量对象的属性,这是不可访问的.

也可以看看:

  • 请注意,两个链接都断开了。 (2认同)

mat*_*uds 8

我做了一个小提琴实现(基本上)上述 iman 概述的想法。这是当您将鼠标悬停在第二个 ipsum 上时的外观return ipsum*ipsum - ...

在此处输入图片说明

范围内的变量在声明它们的地方突出显示(不同的范围用不同的颜色)。在lorem与红色边框是一个阴影变量(不在范围内,但在范围上,如果其他LOREM进一步下跌的树不会在那里。)

我正在使用 esprima 库来解析 JavaScript,以及 estraverse、escodegen、escope(esprima 之上的实用程序库。)“繁重的工作”全部由这些库完成(当然,最复杂的是 esprima 本身。)

这个怎么运作

ast = esprima.parse(sourceString, {range: true, sourceType: 'script'});
Run Code Online (Sandbox Code Playgroud)

制作抽象语法树。然后,

analysis = escope.analyze(ast);
Run Code Online (Sandbox Code Playgroud)

生成一个复杂的数据结构,封装有关程序中所有范围的信息。其余的工作是将在该分析对象(以及抽象语法树本身)中编码的信息收集在一起,并从中制定出交互式着色方案。

所以正确的答案实际上不是“不”,而是“是的,但是”。“但是”很重要:您基本上必须用 JavaScript 重写 chrome 浏览器(及其开发工具)的重要部分。JavaScript 是一种图灵完备的语言,所以原则上当然是可能的。不可能的是在不使用整个源代码(作为字符串)的情况下完成整个事情,然后用它做非常复杂的事情。


raf*_*uto 7

众所周知:你不能.但是你可以创建一个obj并将你声明的每个var分配给该obj.这样你就可以轻松查看你的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
Run Code Online (Sandbox Code Playgroud)


Tim*_*rez 7

在特定范围内访问变量的最简单方法

  1. 打开开发人员工具>资源(在Chrome中)
  2. 使用可访问该范围的函数打开文件(tip cmd/ctrl + p查找文件)
  3. 在该函数内设置断点并运行代码
  4. 当它在断点处停止时,您可以通过控制台(或范围var窗口)访问范围var

注意:您希望针对未缩小的js执行此操作.

显示所有非私人变种的最简单方法

  1. 打开控制台(在Chrome中)
  2. 输入:this.window
  3. 按Enter键

现在,您将看到一个对象树,您可以使用所有声明的对象进行扩展.

  • -1这不包含范围内的所有内容. (3认同)

Ger*_*ica 5

你有多少时间?

如果你讨厌你的 CPU,你可以暴力破解每个有效的变量名,并eval查看每个变量名是否产生一个值!

以下代码段尝试前 1000 个蛮力字符串,这足以在范围内找到人为的变量名称:

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');
  
})();
Run Code Online (Sandbox Code Playgroud)

该脚本将最终(经过长的时间)找到所有范围的变量名称,以及abc niftyswell我创造了一些例子变数。请注意,它只会找到由字母字符组成的变量名称。

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

})();
Run Code Online (Sandbox Code Playgroud)
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;
}
Run Code Online (Sandbox Code Playgroud)
<div class="display"></div>
<div class="status"></div>
Run Code Online (Sandbox Code Playgroud)

我非常清楚几乎没有任何情况下这是可行的

  • 我很高兴这个答案在这里,即使它不是一个支持@Gershom的人 (2认同)