是否有一种方法来获得目前在javascript范围内的所有变量?


不。“作用域内”变量由“作用域链”决定,不能通过编程方式访问。

有关详细信息(相当多),请查看ECMAScript (JavaScript)规范。这里有一个官方页面的链接,你可以在那里下载规范规范(PDF格式),这里有一个官方的,可链接的HTML版本。

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

The variables in scope for your event function are determined by where you define your event function, not how they call it. But, you may find useful information about what's available to your function via this and arguments by doing something along the lines of what KennyTM pointed out (for (var propName in ____)) since that will tell you what's available on various objects provided to you (this and arguments; if you're not sure what arguments they give you, you can find out via the arguments variable that's implicitly defined for every function).

所以除了你定义函数的作用域之外,你还可以通过其他方法找到其他可用的函数:

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文件。然后打他们的头找个合适的医生。: -)


你不能。

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

参见:

作用域链和标识符解析


是也不是。几乎在任何情况下都说“不”。“是的”,但如果您想检查全局作用域,则只能以有限的方式进行。举个例子:

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

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

一个更好的问题是,为什么要知道作用域内的变量是什么?


正如大家所注意到的:你不能。 但是你可以创建一个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

访问特定范围内Vars的最简单方法

打开开发人员工具>资源(Chrome) 打开具有该范围的函数的文件(提示cmd/ctrl+p来查找文件) 在该函数中设置断点并运行代码 当它停在断点时,您可以通过控制台(或范围变量窗口)访问范围变量。

注意:你想对未最小化的js做这个操作。

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

打开控制台(Chrome) 类型:this.window 回车

现在你将看到一个对象树,你可以展开所有已声明的对象。


虽然每个人都回答“不”,我知道“不”是正确的答案,但如果你真的需要得到一个函数的局部变量,有一个有限的方法。

考虑这个函数:

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

你可以把你的函数转换成一个字符串:

var s = f + '';

你会得到一个字符串形式的source of function

'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访问值。


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

调试器;

直接进入浏览器控制台。


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

函数storeVars(目标){ 返回新的代理(目标,{ 有(目标,道具){返回true;}, Get(目标,道具){返回(道具在目标中?目标:窗口)[道具];} }); } Var vars = {};//外部变量,未存储。 (storeVars (var)) { Var a = 1;//存储在vars中 Var b = 2;//存储在vars中 (函数(){ Var c = 3;//内部变量,未存储 }) (); } console.log (var);

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

灵感来自Bergi的回答。


我做了一个小提琴实现(本质上)以上的想法概述iman。下面是当你把鼠标放在第二个ipsum上,返回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是一种图灵完备语言,所以原则上这当然是可能的。不可能的是不使用源代码的全部(作为字符串),然后用它来做非常复杂的事情。


你有多少时间?

如果你ḧ̸̬̦̒͑͝ͅa̵͍͖t̴̪̟̬̖̾̋͗̏ͅe̸̳̍͌͝你的cpu,你可以通过每一个有效的变量名,并评估每一个,看看它是否会产生一个值!

下面的代码段尝试了前1000个bruteforce字符串,这足以在作用域内找到虚构的变量名:

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

这个脚本将最终(经过很长时间)找到所有作用域变量名称,以及abc nifty和swell,我创建的一些示例变量。注意,它只会找到由字母组成的变量名。

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>

我非常清楚,这在任何情况下都是不现实的


您可以在[[scopes]]中看到作用域及其变量,甚至可以使用console.dir()查看闭包作用域。

示例1:

counterWithClosure = (function () {
    let internalVar = 0
    return function () {
        return ++internalVar
    }
})()

counterWithClosure() // 1
counterWithClosure() // 2
counterWithClosure() // 3

console.dir(counterWithClosure)

它会显示“[[Scopes]] > Closure”,“[[Scopes]] > Global”,甚至“[[Scopes]] > Script”中的变量,如果页面有脚本可达的话。

图片输出:

即使使用嵌套闭包,也可以看到嵌套作用域。

示例2:

adderWithNestedClosures = (function () {
    let varLevel1 = 1
    return function (param1) {
        let varLevel2 = 10
        return function (param2) {
            let varLevel3 = 100
            return function (param3) {
                let varLevel4 = 1000
                return function (param4) {
                    ++varLevel1
                    ++varLevel2
                    ++varLevel3
                    ++varLevel4
                    return {
                        paramsSum: param1 + param2 + param3 + param4,
                        varsSum: varLevel1 + varLevel2 + varLevel3 + varLevel4
                    }
                }
            }
        }
    }
})()

adderWith123 = adderWithNestedClosures(1)(2)(3) // Preparing function with nested scopes
adderWith123(4) // {paramsSum:10,varsSum:1115}
adderWith123(4) // {paramsSum:10,varsSum:1119}
adderWith123(5) // {paramsSum:11,varsSum:1123}
console.dir(adderWith123) 

它显示了[[scopes]]中的所有嵌套作用域 图片输出:

它适用于基于chromium的浏览器。