0

我想获取在 Javascript 代码中进行的所有函数和变量声明。我使用 esprima,我想知道是否有脚本可以用于我的目标?

例如我们有这样的代码:

var myVar1;
var myVar2;
function myTestFunction(funcVar1, funcVar2) {
  var myVar3; 
}

我除了:

带变量的数组

["myVar1", "myVar2"]

还有一个带有函数的数组:

[{"name": "myTestFuncttion", "params":["funcVar1", "funcVar2"], "variables": ["myVar3"]}]

任何想法如何实现这一目标?

4

1 回答 1

2

用小提琴完成解决方案:

下面是一些用于测试运行的 JavaScript 代码:

var hello = 41;
function aaaaa(p1, p2, p3){
    var a1 = 7, a2 = 8;
    var a3 = 9;
    function bbbbb(q1, q2){
        var b1 = 10, b2 = 11;
        return 12;
    }
    var a4 = 99;
    function ccccc(r1, r2, r3){
        var c1 = 13;
        var c2 = 14;
        var c3 = 15, c4 = 16;
        return 17;
    }
    var a5 = 88, a6 = 77;
    function ddddd(s1){
        return s1 === 18
            ? function (x){ return x+1; }
            : function (y){ return 22; }
    }
    return p1 + a3 <= 42 ? 55 : ccc(p1, 0, 0);
}
var world = 42;
function xxxxx(x){
    var z=0;
    return 0;
}

我假设这是所需的输出:

{
    "vars": ["hello", "world" ],
    "funcs": [
        {
            "name": "aaaaa",
            "params": ["p1", "p2", "p3"],
            "variables": ["a1","a2","a3","a4","a5","a6"]
        },
        {
            "name": "bbbbb",
            "params": ["q1","q2"],
            "variables": ["b1","b2"]
        },
        {
            "name": "ccccc",
            "params": ["r1","r2","r3"],
            "variables": ["c1","c2","c3","c4"]
        },
        {
            "name": "ddddd",
            "params": ["s1"],
            "variables": []
        },
        {
            "name": "xxxxx",
            "params": ["x"],
            "variables": ["z"]
        }
    ]
}

列表是扁平的,并且 ddddd 中的匿名函数被忽略(它们是 FunctionExpressions 而不是 FunctionDeclarations)。猜猜这就是你想要的。

这是代码 - 可能/希望很容易理解,无需进一步解释:

function findDeclarations(code){
    var ast = esprima.parse(code);
    var funcDecls = [];
    var globalVarDecls = [];
    var funcStack = [];
    function visitEachAstNode(root, enter, leave){
        function visit(node){
            function isSubNode(key){
                var child = node[key];
                if (child===null) return false;
                var ty = typeof child;
                if (ty!=='object') return false;
                if (child.constructor===Array) return ( key!=='range' );
                if (key==='loc') return false;
                if ('type' in child){
                    if (child.type in esprima.Syntax) return true;
                    debugger; throw new Error('unexpected');
                } else { return false; }
            }
            enter(node);
            var keys = Object.keys(node);
            var subNodeKeys = keys.filter(isSubNode);
            for (var i=0; i<subNodeKeys.length; i++){
                var key = subNodeKeys[i];
                visit(node[key]);
            }
            leave(node);
        }
        visit(root);
    }
    function myEnter(node){
        if (node.type==='FunctionDeclaration') {
            var current = {
                name      : node.id.name,
                params    : node.params.map(function(p){return p.name;}),
                variables : []
            }
            funcDecls.push(current);
            funcStack.push(current);
        }
        if (node.type==='VariableDeclaration'){
            var foundVarNames = node.declarations.map(function(d){ return d.id.name; });
            if (funcStack.length===0){
                globalVarDecls = globalVarDecls.concat(foundVarNames);
            } else {
                var onTopOfStack = funcStack[funcStack.length-1];
                onTopOfStack.variables = onTopOfStack.variables.concat(foundVarNames);
            }
        }
    }
    function myLeave(node){
        if (node.type==='FunctionDeclaration') {
            funcStack.pop();
        }
    }
    visitEachAstNode(ast, myEnter, myLeave);
    return {
        vars  : globalVarDecls,
        funcs : funcDecls
    };
}

对于测试,您可以键入

JSON.stringify(
    findDeclarations(
        'var hello=41;\n' +
        aaaaa.toString() +
        'var world=42;\n' +
        xxxxx.toString()
    ),
    null, 4
)

你也可以使用 estraverse 包,它可以在 github 上找到。然后,本质上,函数 visitEachAstNode 应该替换为 estraverse.traverse,否则您可以保持代码不变。

小提琴

于 2019-11-07T12:14:30.183 回答