如何列出我的Node.js脚本中的所有函数?

6

我尝试查看global,但它只包含变量而不是函数。我如何列出在我的脚本中创建的所有函数?


这个有什么使用场景? - mihai
1
我正在为一个Node应用程序创建一个自定义AOP脚本。我想将其用于诸如分析、限流和自定义安全策略等方面。我查看了Dojo,但在将其加载到我的Node应用程序中时遇到了一些早期问题,因此我想编写一个自定义脚本。这应该不难。 - Trindaz
所以你想从脚本内部完成这个操作?我不确定这么做是否容易,因为你可能会有匿名函数、闭包内的函数、动态创建的函数等。 - mihai
真的 - 但在我的情况下,仅将方面应用于“根级别”命名函数就足够了。 - Trindaz
6个回答

5

在命令行中使用node debug命令并指定要查看的文件。然后,您可以使用list(此处填写一个大数字)命令。

node debug mini_file_server.js 
< debugger listening on port 5858
connecting... ok
debug> scripts
  26: mini_file_server.js
debug> list(1000)
  1 var http = require('http'),
  2     util = require('util'),
  3     fs   = require('fs');
  4 
  5 server = http.createServer(function(req, res){  
  6     var stream  = fs.createReadStream('one.html'),
  7         stream2 = fs.createReadStream('two.html');
  8     console.log(stream);
  9     console.log(stream2);
 10     stream.on('end', function(){
 11         stream2.pipe(res, { end:false});
 12     });
 13 
 14     stream2.on('end', function(){
 15         res.end("Thats all!");
 16     });
 17 
 18     res.writeHead(200, {'Content-Type' : 'text/plain'});
 19     stream.pipe(res, { end:false});
 20     stream2.pipe(res, { end:true});
 21 
 22 }).listen(8001);
 23 });
debug> 

4
如果函数有名称,它将正常显示在全局范围内:
mb-work-laptop:~ markbessey$ node
> for (var k in global) { console.log(k); }
global
process
GLOBAL
root
Buffer
setTimeout
setInterval
clearTimeout
clearInterval
console
module
require
k
> function z(a) { return a*10; }
> for (var k in global) { console.log(k); }
global
process
GLOBAL
root
Buffer
setTimeout
setInterval
clearTimeout
clearInterval
console
module
require
k
z
> 
> global.z
[Function: z]

我本来以为我已经测试过了,发现函数在全局不可用,但是你的脚本对我也能正常工作。 - Trindaz
1
抱歉马克,我已经将其标记为答案 - 它在控制台中运行良好,但对于脚本 function a(){ return 1; } for(var k in global) console.log(k) 它不显示函数'a'。 - Trindaz
没错。事实证明,在交互模式下,这些定义会进入全局环境,但如果你从“node script.js”运行脚本,则它们最终成为模块内部的局部变量。调试器肯定有一种方式可以获取这些信息,但不确定脚本能否访问它... - Mark Bessey

3

如果没有像debugger这样更高级的反射工具,那么在Node中实现这一点是不可能的。

唯一的方法是使用__parent__,但由于安全问题和其他原因,该方法已被移除。正如Mark Bessey所说,当您运行脚本时,这些变量成为模块闭包变量。您不能在其他地方访问它们,除非显式地导出它们。

这不是一个错误,而是设计上的考虑。这就是Node的工作方式。然而,如果您要求用户编写函数表达式赋值,那么一切都会正常工作:

module.exports = {
    a:function(){
        //same logic you had in the function declaration
    }
}

然后,您可以轻松地反思和列举 module.exports,并获得所有函数名称。

0

0

想要的正是你在寻找的东西。没有找到其他解决方案(大多数假定使用“window”或“this”或“global”,但在Node中都不起作用)。

借助几个库:fs,esprima和escodegen,我们可以使用大约25行代码完成它。

const fs = require('fs');
const esprima = require("esprima");
const escodegen = require("escodegen");

逻辑如下:
  1. 首先,我们将读取包含此函数的文件,并将其作为纯文本读取,就像读取任何其他文件一样
  2. 我们将使用esprima将该文本解析为有效的树形结构,其中一部分将包含我们的函数
  3. 我们将过滤该树以仅包含函数(除了我们用于执行此操作的函数!我们不需要它)
  4. 为此,我们需要获取易于声明的函数
  5. 但是,理想情况下,我们还可以获取使用箭头表达式声明为变量的任何函数,这需要更多的工作,但非常可行。
  6. 接下来,我们希望将这些对象从树中重构回代码中实际可用的函数,因此对于我们所有的函数:
  7. 使用escodegen将该对象重构为看起来像该函数的编写代码的字符串
  8. 将该字符串转换回脚本中的可用函数

最终结果将返回一个对象,其中键值对的键是函数名称的字符串,而值是函数本身。

function getAllFunctionsInThisFileExceptThisFunction() {
    const thisFunctionName = arguments.callee.name;
    const rawTextFromThisFile = fs.readFileSync(__filename, "utf8");
    const parsed = esprima.parseScript(rawTextFromThisFile);
    const allDeclaredVariables = parsed.body.filter(e=>e.type === "VariableDeclaration");
    const allDeclaredFunctions = parsed.body.filter(e=>e.type === "FunctionDeclaration");
   
    let allFunctions = []
    for (declaredVariable of allDeclaredVariables){ 
        const declarations = declaredVariable.declarations[0];
        if (declarations.init.type === "ArrowFunctionExpression"){ 
            const anonymousFunction = declarations.init;
            let reconstructedFunction = anonymousFunction;
            reconstructedFunction.id = declarations.id;
            allFunctions.push(reconstructedFunction);
        }
    }
    allFunctions.push(...allDeclaredFunctions)

    const allFunctionsExceptThisOne = allFunctions.filter(e => e.id.name !== thisFunctionName);
    let functionsDict = {};
    for (parsedFunction of allFunctionsExceptThisOne) {
        const functionString = escodegen.generate(parsedFunction);
        const newFunction = eval(`(${functionString})`)
        functionsDict[parsedFunction.id.name] = newFunction;
    }
    return functionsDict;
}

从那里开始,您可以像处理其他对象/字典一样随意操作它。

const allFunctionsDict = getAllFunctionsInThisFileExceptThisFunction();
console.log( allFunctionsDict["sum"](10,30) ) //prints 40
console.log( allFunctionsDict["difference"](350,250) ) //prints 100
console.log( allFunctionsDict["product"](6,4) ) // prints 24
console.log( Object.keys(allFunctionsDict) ) //prints [ 'product', 'sum', 'difference' ]

function sum(a, b) {
    return a + b;
}

function difference(a, b) {
    return a - b;
}

const product = (a,b) => { 
    return a * b;
}

0

如果你想做一些AOP,路线就是AST。

你可以使用类似这样的工具构建自己的AOP框架:http://esprima.org

或者你可以尝试使用node-burrito,该工具非常适用于不太复杂的方面:

var burrito = require('burrito');

var src = burrito('someCall()', function (node) {
    if (node.name === 'call') node.wrap('qqq(%s)');
});

将生成

qqq(somecall())

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接