4

I am attempting to write a function that will convert anything into a function. The function should work as follows:

  1. Check to see if it already is a function and if so just returns it.
  2. Check to see if it is a string and if so is there a global function with that name, if so return that.
  3. Check to see if the string can be evaluated and if so return a lambda (anonymous) function that evaluates the string.
  4. If all previous attempts to convert the value into a function fail, return a lambda (anonymous) function that returns the value. This also is how I handle all other variable types (numbers, null, undefined, boolean, objects and arrays).
function canEval(str){
  try { eval(str); } catch (e){ return false; }
  return true;
}
function toFunc(v){
  if(typeof(v) == "function") return v;
  if(typeof(v) == "string"){
    if(
      window[v] != undefined &&
      typeof(window[v]) == "function"
    ) return window[v];
    if(canEval(v))
      return function(){eval(v)};
  }
  return function(){return v;};
};

The problem with this code is that canEval actually evaluates the code, in some circumstances I do not want the code (in a string) to run more than once. Currently it evaluates the string to see if there are any errors, and if not it returns the evaluated code (string) as a function, to then be ran again at a later date.

Is there any way I can test to see if the code can be evaluated (without errors) without actually running the code? or run it isolated so that what happens in the evaluated code (string) doesn't take ANY affect on the current file?

jcubic
  • 61,973
  • 54
  • 229
  • 402
Dustin Poissant
  • 3,201
  • 1
  • 20
  • 32
  • 9
    So, as a toy and a learning experience, this is fine, but if you ever find yourself doing this for real-world use in a production system, you should really reconsider. This is a really bad idea, and you should never build a system that actually uses your hypothetical `toFunc` function. – user229044 Jan 28 '16 at 14:03
  • Do you care to elaborate as to why this is a bad idea instead of just stating that it is? – Dustin Poissant Jan 28 '16 at 14:09
  • 3
    The better question is: What use could you possibly have for this? What would justify such a contrived solution? – user229044 Jan 28 '16 at 14:11
  • I am building a plugin which allows the user to pass in a callback function, but the plugin may also be called via HTML by giving an element a specific class, and I want them to be able to use a `data-` attribute to pass a "callback" into the plugin via HTML. And for this case I will need to convert a string into a function. – Dustin Poissant Jan 28 '16 at 14:14
  • As for why it's bad, [there are a myriad of reasons](http://stackoverflow.com/questions/86513/why-is-using-the-javascript-eval-function-a-bad-idea) not to unnecessarily use `eval`, but in this case: Suppose I type `myMethod(;)`. That's a pretty obvious typo, and JavaScript will fail to even parse the file. However, using your `toFunc` function, I would type something like `toFunc('myMethod(;)')`, which not only parses, it actually doesn't even generate a runtime error, because your function will find that it can't `eval` it, so it will just *return a useless string of garbage*. That's insane. – user229044 Jan 28 '16 at 14:14
  • I can deal with a potential "useless" function, if they pass in something "useless" then nothing happens, but the benefits of if this worked would far outweigh the risks or "uselessness" of this function. – Dustin Poissant Jan 28 '16 at 14:16
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/101902/discussion-between-meagar-and-dustin-poissant). – user229044 Jan 28 '16 at 14:17
  • It's actually an interesting question, though there are reasons to avoid eval: [When is JavaScript's eval() not evil?](http://stackoverflow.com/questions/197769/when-is-javascripts-eval-not-evil) – Yogi Jan 28 '16 at 14:20

1 Answers1

1

Someone posted an answer that worked but removed it. So here is a working toFunc that works as described.

function toFunc(v){
    if(typeof(v) == "function") return v;
    if(typeof(v) == "string"){
      if(
        window[v] != undefined &&
        typeof(window[v]) == "function"
      ) return window[v];
      try {
        return new Function(v);
      }catch(e){}
    }
    return function(){return v;};
  };

The answer is to use the Function constructor within a try-catch statement

Dustin Poissant
  • 3,201
  • 1
  • 20
  • 32
  • No reason to use that `worked` variable, actually :-) – Bergi Jan 28 '16 at 14:25
  • How would I determine after the catch statement whether or not an error was thrown? – Dustin Poissant Jan 28 '16 at 14:27
  • @DustinPoissant Return from the `try` block. I added some comments in the chat as well. – Dave Newton Jan 28 '16 at 14:29
  • Maybe I don't understand the point of the try block but wouldn't that return the function even if there is an error? My understanding of a try block is that it runs all the code in the block, and if it finds an error it executes the catch block. But I don't want it returned if there is an error, if I did it would just not even bother with the try block at all. – Dustin Poissant Jan 28 '16 at 14:39
  • Interesting solution. Here's the MDN reference with some information on how it differs from eval: [Function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function) – Yogi Jan 28 '16 at 14:44
  • @DaveNewton: No, once it encounters an error, an exception is thrown and the execution is stopped - it won't return any more. The exception will be caught by the `try`, and control flow jumps to the `catch` block. Just do `try { return new Function(v); } catch() {} …` – Bergi Jan 28 '16 at 14:49
  • Oh i did not realized it ceased execution of the try block on the first error, I assumed it continued execution, and collected all errors, until the catch block which would be executed if there were any errors. Thanks you for explaining, I will update my answer – Dustin Poissant Jan 28 '16 at 14:50