【问题标题】:Call function from string in nodejs从nodejs中的字符串调用函数
【发布时间】:2013-10-26 08:51:01
【问题描述】:

nodejs中window[my_func_name]的等价物是什么?我正在从标准输入读取一个字符串,如果它是一个函数名,我想执行它。我认为 global[my_func_name] 可能会起作用,但不幸的是它不起作用。

【问题讨论】:

    标签: string node.js function window global


    【解决方案1】:

    效果很好

    global.foo = function foo () {
      console.log("foo was called");
    }
    
    process.stdin.on("data", function(input) {
    
      // don't forget to call .trim() to remove the \n
      var fn = input.toString().trim();
    
      // function exists
      if (fn in global && typeof global[fn] === "function") {
        global[fn]();
      }
    
      // function does not exist
      else {
        console.log("could not find " + fn + " function");
      }
    });
    
    process.stdin.resume();
    

    输出

    foo
    foo was called
    bar
    could not find bar function
    

    这是否是个好主意......好吧,这是一个完全不同的讨论。


    EDIT — 大约 18 个月后...是的,调用这样的全局函数是一个可怕的想法。

    这么说,这是一种方法,您可以以更好的方式解决问题。下面我们将构建一个小REPL(读取-评估-打印循环)。为了更好地理解它,我将它分成几个部分。

    首先,我们要确保我们的 REPL 在尝试运行他们的命令之前等待用户按 Enter。为此,我们将创建一个transform stream,它在发送line 之前等待"\n" 字符

    下面的代码是使用ES6 编写的。如果您在找到运行代码的兼容环境时遇到问题,建议您查看babel

    // line-unitizer.js
    import {Transform} from 'stream';
    
    class LineUnitizer extends Transform {
      constructor(delimiter="\n") {
        super();
        this.buffer = "";
        this.delimiter = delimiter;
      }
      _transform(chunk, enc, done) {
        this.buffer += chunk.toString();
        var lines = this.buffer.split(this.delimiter);
        this.buffer = lines.pop();
        lines.forEach(line => this.push(line));
        done();
      }
    }
    
    export default LineUnitizer;
    

    如果您不熟悉流处理并且它不太有意义,请不要过于关注 LineUnitizer。这种流变换极为常见。总体思路是这样的:一旦我们将process.stdin 传送到接收流中,process.stdin 将在用户每次按键时发出数据。然而,我们的Repl(在下面实现)在用户完成输入命令之前无法对命令执行操作。 LineUnitizer 是等待用户按下回车键(将"\n" 插入流中)然后向_transform 发出信号,表明该命令已准备好发送到repl 进行处理!

    我们现在看看Repl

    // repl.js
    import {Writable} from 'stream';
    
    class Repl extends Writable {
      _parse(line) {
        var [cmd, ...args] = line.split(/\s+/);
        return {cmd, args};
      }
      _write(line, enc, done) {
        var {cmd, args} = this._parse(line.toString());
        this.emit(cmd, args);
        done();
      }
    }
    
    export default Repl;
    

    嗯,嘿,这很容易!它有什么作用?每次repl 收到一行时,它都会发出一个带有一些参数的事件。这是查看如何解析命令的直观方式

    The user enters       emit event      args
    -------------------------------------------------------------
    add 1 2 3             "add"           ["1", "2", "3"]
    hens chocobo cucco    "hens"          ["chocobo", "cucco"]
    yay                   "yay"           []
    

    好的,现在让我们将所有东西连接起来看看它是否工作

    // start.js
    import LineUnitizer from './line-unitizer';
    import Repl         from './repl';
    
    process.stdin
      .pipe(new LineUnitizer())
      .pipe(
        (new Repl())
          .on("add", function(args) {
            var sum = args.map(Number).reduce((a,b) => a+b, 0);
            console.log("add result: %d", sum);
          })
          .on("shout", function(args) {
            var allcaps = args.map(s => s.toUpperCase()).join(" ");
            console.log(allcaps);
          })
          .on("exit", function(args) {
            console.log("kthxbai!");
            process.exit();
          }));
    

    运行它

    $ node start.js
    

    输出

    > 为前缀的行是用户输入。 > 实际上不会在您的终端中显示。

    > add 1 2 3
    add result: 6
    > shout I can see it in your face!
    I CAN SEE IT IN YOUR FACE!
    > exit
    kthxbai!
    

    如果您认为这很棒,我们还没有完成。以这种方式编写程序的好处是,无论命令如何到达我们的程序,我们都可以对其执行操作。

    考虑这个commands.txt 文件

    add 100 200
    shout streams are the bee's knees
    exit
    

    现在像这样运行它

    $ cat commands.txt | node start.js
    

    输出

    300
    STREAMS ARE THE BEE'S KNEES
    kthxbai!
    

    好的,那真是太棒了。现在考虑命令可能来自任何地方。可以是数据库事件、网络中的某事、CRON 作业等。因为一切都很好地分离,我们可以轻松地调整这个程序以轻松接受各种输入。

    【讨论】:

    • 谢谢,你是对的。我只是认为一个全局函数会自动添加到全局对象中,就像窗口对象一样。
    • @PaulK 如果可以解决您的问题,请考虑接受此答案
    • @PaulK,不管怎样,我已经编写了许多复杂的 node.js 应用程序,但我从未(甚至一次)接触过global
    • 谢谢,但是您的代码中有错误。您需要将“函数存在”替换为:if (fn in global && typeof global[fn] === "function") {您的测试失败,“fn”是一个字符串,您测试它是否是一个函数。
    • @PaulK 我对上述答案进行了重大改进。我希望这可以帮助您以有效的方式解决类似的问题^.^
    【解决方案2】:

    将您的函数写在一个单独的文件中并导出它们并使用名称引用来调用它们,例如

    //    functions.js
    var funcOne = function(){
                       console.log('function ONE called')
                  }
    module.exports={
        //  name_exported : internal_name
        funcOne : funcOne
    }
    

    在 index.js 中使用 functions.js 中定义的函数:

    //    index.js
    var methods = require('./functions.js')   // path to functions.js
    methods['funcOne']()
    

    输出:

    > node index.js
    > function ONE called
    

    【讨论】:

      【解决方案3】:
      var theTests = require('./tests'); 
      //@TODO CaseID should be caseId
      var CaseID = 5678;
      // use dynamic functions
      var functionName = 'theTests.TEST_' + CaseID;
      var functionString = String('TEST_' + CaseID);
      
      var check = eval( 'typeof ' + functionName ); // bad
      if ( check == 'function' ) {
          // run the function
          // testResult = eval( functionName ); nope
          testResult = theTests[functionString](); //yep :)
          console.log(testResult);
      }
      else {
          console.log( 'No test functions for ' + CaseID );
      }
      

      在tests.js的这个例子中 正在寻找...

      TEST_5678: function(){
           return some thing;
      },
      

      【讨论】:

        【解决方案4】:

        我自己也遇到过同样的问题,而且我也是新手,所以对此持保留态度 - 从其他 posts here on SO 提到 global 对象,我还发现 module 似乎是 Node.js 中的本地对象,在其上定义了 exports

        所以如果你有一个模块:

        exports.myFunction = myFunction;

        在您声明导出之后(甚至在声明 myFunction() 之前,您可以在文件/脚本的顶部编写导出),然后调用:

        module.exports['myFunction']()

        这实际上会调用myFunction()

        再一次,新手,所以请谨慎行事。

        【讨论】:

          猜你喜欢
          • 2021-11-17
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-04-19
          • 1970-01-01
          相关资源
          最近更新 更多