Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Call function from string in nodejs

What is the equivalent to window[my_func_name] in nodejs? I'm reading a string from stdin and if it's a function name I want to execute it. I thought global[my_func_name] might work but unfortunately it doesn't.

like image 202
PaulK Avatar asked Oct 17 '13 18:10

PaulK


People also ask

How do I call a node js function from a string?

There are two methods to call a function from string stored in a variable. The first one is by using the window object method and the second one is by using eval() method.

How do you call a function in node JS?

To call a function, you can either pass its name and arguments to User. callFunction() or call the function as if it was a method on the User.

How do I turn a string into a function?

To convert a string in to function "eval()" method should be used. This method takes a string as a parameter and converts it into a function.

Can you call a function in a variable?

we put the function in a variable if inside the function block we use the return method: var multiplyTwo = function (a) { return a * 2; }; if we simply call this function, nothing will be printed, although nothing is wrong with the writing of the function itself.


2 Answers

It works just fine

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

Output

foo
foo was called
bar
could not find bar function

Whether or not this is a good idea... well, that's an entirely different discussion.


EDIT — ≈18 months later... Yeah, it's a horrible idea to call a global function like that.

In saying that, here's one way you could approach the problem in a much better way. Below we're going to build a little REPL (read-eval-print loop). In order to understand it better, I'll break it down into a couple parts.

First, we want to make sure our REPL waits for the user to press enter before we try running their command. To do that, we'll create a transform stream that waits for a "\n" character before sending a line down the pipe

The code below is written using ES6. If you're having trouble finding a compatible environment to run the code, I suggest you check out 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;

Don't get too hung up on LineUnitizer if you're new to stream processing and it doesn't quite make sense. This kind of stream transform is extremely common. The general idea is this: once we pipe process.stdin into a receiving stream, process.stdin will be emitting data every time the user presses a key. However, our Repl (implemented below) can't act on a command until the user has finished typing the command. LineUnitizer is the part that waits for the user to press enter (which inserts a "\n" into the stream) and then signals to _transform that the command is ready to be sent to repl for processing!

Let's look at Repl now

// 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;

Well hey, that was easy! What does it do tho? Each time repl receives a line, it emits an event with some args. Here's a visual way to see how a command is parsed

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

Ok, now let's wire everything together to see it work

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

Run it

$ node start.js

Output

Lines prefixed with > are user input. The > will not actually be visible in your terminal.

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

If you think that's awesome, we're not even done yet. The benefits of writing our program this way is that we can act on the commands regardless of how they arrive at our program.

Consider this commands.txt file

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

Now run it like this

$ cat commands.txt | node start.js

Output

300
STREAMS ARE THE BEE'S KNEES
kthxbai!

Ok, so that's pretty fricken great. Now consider that the commands could come from anywhere. Could be a database event, something across the network, a CRON job, etc. Because everything is nicely separated, we could easily adapt this program to accept a variety of inputs with ease.

like image 112
Mulan Avatar answered Oct 07 '22 18:10

Mulan


Write your functions in a separate file and export them and use with name reference of that to call them, Like

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

Use function defined in functions.js in index.js :

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

OUTPUT :

> node index.js
> function ONE called
like image 26
Rupinder Singh Avatar answered Oct 07 '22 19:10

Rupinder Singh