获取范围内的所有变量


194

有没有办法获取当前在javascript中作用域内的所有变量?


回答您对Camsoft的回答:这是完全不同的问题。我已更新我的答案以解决此问题。
TJ Crowder

3
这只是一个普遍的问题,因为我使用的文档晦涩难懂,所以使用更具体的API并没有太大帮助。
伊恩(Ian)2010年

您的意思是全局变量!您可以使用来显示可枚举的全局变量for (v in this) alert(v);。但是,并非所有的全局变量都是可枚举的,而且我不知道获取不可枚举列表的标准方法。
杰森·奥伦多夫

2
@Jason-不,问题很明确。在函数范围的变量将包括全局变量,thisarguments,参数和封闭范围中定义的所有变量。
蒂姆·唐

2
这就是为什么我想念Perl的符号表的原因。有计划将其添加到Javascript的未来版本中吗?
Dexygen

Answers:


84

不能。“范围内”变量由“作用域链”确定,该变量无法通过编程方式访问。

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

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

事件函数作用域中的变量取决于定义事件函数的位置,而不是它们的调用方式。但是,您可以this按照KennyTM指出(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]);
    }
}

(您可以对此进行扩展以获得更多有用的信息。)

取而代之的是,我可能会使用调试器,例如Chrome的开发工具(即使您通常不使用Chrome进行开发)或Firebug(即使您通常不使用Firefox进行开发),或Opera上的Dragonfly ,或IE上的“ F12开发人员工具”。并通读它们提供给您的所有JavaScript文件。并击败他们以获得适当的文档。:-)


附带说明一下,Google Chrome浏览器具有与Firebug相当的出色调试器(顶部还有很多其他功能)。
转至2014年

4
@Swivelgames:哦,我更喜欢Chrome的开发工具,而不是Firefox + Firebug。他们在2010
理想

1
@tjcrowder确实!我注意到答案的时间戳记是前一阵子的:)
旋转

98

尽管每个人都回答“ ”,并且我知道“否”是正确的答案,但是如果您确实需要获取函数的局部变量,则有一种受限的方法。

考虑以下功能:

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

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

var s = f + '';

您将以字符串形式获取函数源

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

现在,您可以使用诸如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);

并找到具有以下内容的对象:

obj.type == "VariableDeclaration"

结果(我在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"
        }
    ]
}

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

但是这种方法的问题在于,您仅具有在函数本身中定义的变量。例如此示例:

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

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


7
解决原始问题的出色技术。值得赞扬。
deepelement 2014年

4
调用方的变量不一定是作用域变量。另外,作用域变量不一定在调用链中。从定义/闭包范围之外的调用者调用闭包函数时,闭包函数可以引用已被关闭的变量(并且无法随闭包主体访问其变量)。
DDS 2015年

你能解释清楚“简单的评估”吗?我尝试使用以下代码,但无法将变量困在范围内。function getCounter(start){ return function(){ start ++; return start; } } var counter = getCounter(1); var startInClosure = eval.call(counter, 'this.start;'); console.log(startInClosure);它打印undefined,而我希望它应该是2
Pylipala

@Pylipala这里的问题是关于作用域中的变量不能从作用域外部获取局部变量的值。这是不可能的,因为这是不应从外部访问的局部变量的定义。对于您的代码,您的意思是上下文不是范围,但是您没有将start放在上下文中(this),因此您无法使用this.start来读取它。看到这里:paste.ubuntu.com/11560449
iman

@iman感谢您的回答。我也猜想在Javascript方面是不可能的,所以我猜在v8方面是否可行。我在下面的问题链接中找到了准确描述我的问题的链接。拉瑟·赖希斯坦(Lasse Reichstein)在讲话中说不,但马可塔可说是。我尝试了一些C ++代码作为链接,但不知道如何编写它。
Pylipala

32

是的,没有。几乎在每种情况下都为“否”。如果要检查全局范围,则“是”,但仅以有限的方式。请看以下示例:

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

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

在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

因此可以在当前范围内列出一些变量,但是它不是可靠,简洁,有效或易于访问的。

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


2
我认为这个问题更笼统,不仅仅局限于Web浏览器中的javascript。
Radu Simionescu 2012年

如果您使用的是节点,只需将单词“ window”更改为“ global” ...或可以使用单词“ this”,但是在“ use strict”下禁止使用
Ivan Castellanos 2014年

我添加了一行检查hasOwnProperty。例如:for ( var i in window ) { if (window.hasOwnProperty(i)) { console.log(i, window[i]); }}。这将至少减少继承的属性,并且您的变量将仅位于其他约50个属性中。
timctran 2015年

8
为什么在调试和/或开发期间,至少不应该有人要检查范围内的变量。
Dexygen

想要知道局部范围内的变量的另一个原因是序列化闭包。
迈克尔(Michael)

29

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

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

Bergi 这个答案启发


1
对于这种简单的技术,这令人惊讶地成功。
乔纳森·尤尼斯

哇的超级力量
佩里·蒙蒙

16

你不能

变量,函数声明的标识符以及函数代码的参数作为不可访问的变量对象的属性绑定。

也可以看看:


1
是的,但作用域内的变量不仅限于当前变量对象。范围内变量由作用域链确定,作用域链是(通常情况下)由一系列变量对象组成并以全局对象终止with的链(尽管该语句可用于在链中插入其他对象)。
TJ Crowder

+1链接到bclary.com。我很高兴地说,最新规范的HTML版本将在接下来的几个月中出现在ECMA网站上。
TJ Crowder 2010年

3
请注意,两个链接都断开了。
0xc0de


8

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

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

注意:您要针对未缩小的js执行此操作。

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

  1. 打开控制台(在Chrome中)
  2. 类型:this.window
  3. 按下Enter

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


7

大家都注意到:您不能。但是您可以创建一个obj,并将您声明的每个var分配给该obj。这样,您可以轻松签出您的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感谢酷例如,仅完成同样的,也可以看成通过console.log(window); jsfiddle.net/4x3Tx
Stano

5

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

在此处输入图片说明

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

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

这个怎么运作

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

制作抽象语法树。然后,

analysis = escope.analyze(ast);

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

因此,正确的答案实际上不是“否”,而是“是,而是”。“但是”是一个很大的问题:您基本上必须用JavaScript重写chrome浏览器的重要部分(它是devtools)。JavaScript是Turing完整的语言,因此原则上当然可以。不可能在不使用整个源代码(作为字符串)的情况下完成整个操作,然后再进行非常复杂的操作。


3

如果您只是想手动检查变量以帮助调试,请启动调试器:

debugger;

直接进入浏览器控制台。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.