2

I found this script on a web page:

eval(function(a, b, c, d, e, f) {
    //do some thing here and return value
}(val1, val2, val3, val4, val5, {}))

Do you know what above expression really does?

Mikev
  • 2,012
  • 1
  • 15
  • 27
Asghar M
  • 33
  • 1
  • 4
  • 3
    Did you search the net for eval()? https://www.w3schools.com/jsref/jsref_eval.asp – Anurag Srivastava Mar 02 '19 at 12:31
  • 1
    `function(param){ console.log(param) }("hello world")` will immediately print `"hello world"`. It's an IIFE - immediately executed function expression which has parameters supplied to it in the last bracket. – VLAZ Mar 02 '19 at 12:31
  • I will call the function with `a=val1, b=val2.... ` – Maheer Ali Mar 02 '19 at 12:32
  • 1
    I think the question is not solely for `eval` but the entire construct. I do agree that the IIFE inside makes the whole usage that much harder to grasp at a glance but if you know what IIFEs do, it's more clear. Well, to an extent - the inner expression will likely generate some code but we don't know what - `eval` then consumes it. – VLAZ Mar 02 '19 at 12:34
  • NEVER, EVER, NEVER, BUT THEN NEVER use `eval` EVER! – Mouser Mar 02 '19 at 12:56
  • @Mouser [never say never](https://stackoverflow.com/q/197769/1048572). Although I agree that in most reasonable use cases, `new Function` is better than `eval`. – Bergi Mar 02 '19 at 13:15
  • Please post the complete code of *`do some thing here and return value`* – Bergi Mar 02 '19 at 13:16

3 Answers3

2

Let's unpack things one by one, first the inner part

function(a, b, c, d, e, f) {
    //do some thing here and return value
}(val1, val2, val3, val4, val5, {})

In fact, let's simplify it even more

(function() { //takes no paremeters
    console.log("this was executed");
}())//no parameters given here

This is called an immediately invoked function expression or very often shortened to IIFE. It is rather boring, honestly, the name is the entire description but let me rephrase - it's a function that is declared and then executed straight away. This is done by the final () brackets - exactly how you'd otherwise execute a function, for example parseInt(). In this simple case, it simply prints something to the console and finishes.

However, it is a normal function and it can do anything you want, including take parameters:

(function(param1, param2) { //takes two parameters
    console.log("this was executed with parameters", param1, param2);
}("hello", "world"))//these are passed to the function

So, you can do a lot more stuff. You can even return a value and the parameters passed to the function could be variables:

var someVariable = "hello";
var anotherVariable = "world";

var resultFromIIFE = (function(param1, param2) { //takes two parameters
    console.log("this was executed with parameters", param1, param2);
    return param1 + param2;
}(someVariable, anotherVariable));

console.log(resultFromIIFE);

So, hopefully that clears up the inner part. I'll get back to it.

As for eval - it takes a string and executes it as JavaScript code.

eval("console.log('this comes from eval()')");


var evalCanReturnResults = eval("1 + 2");
console.log(evalCanReturnResults);


var a = 3;
var b = 4;
var evalCanUseVariables = eval("a + b");

console.log(evalCanUseVariables);

That's a quick crash course in eval. It's enough to understand that it can take any arbitrary string and execute it as JS.

So, if we put the two together, the inner IIFE will most likely be generating some code dynamically for eval to execute. For example, you could have something like::

eval(function(limit, toPrint) {
    return "for (var i = 0; i < " + limit + "; i++) { console.log('" + toPrint + "') }"
}(3, "foo"))

Which will generate a for loop with the parameters specified and execute it.

VLAZ
  • 26,331
  • 9
  • 49
  • 67
  • No problem. Would you be able to speak to the nature of WHY someone would choose this route? Is there a pro to using a IIFE to generate a function and executing it with eval? Eval is generally considered dangerous and bad practice, so is there an alternative that doesn’t use eval that would achieve the same result without eval? – Nate Mar 02 '19 at 18:54
  • @Nate I really can't say why this was chosen. Not without seeing the code. Even without that, it would be odd to have code that generates other code that you execute - surely, if you can generate the code, you might also just skip `eval` and have your code produce the result needed. You are correct that `eval` is considered dangerous, so I suspect this might be the use - I can see it being used as a malicious code obfuscation method. It could also be a hack somebody used - `eval` is often used as a crutch. There might be uses like dynamic programming but I don't know enough about those to say – VLAZ Mar 02 '19 at 19:23
  • Thanks for the insight! – Nate Mar 02 '19 at 19:24
1

This does 2 things.

  1. Creates an anonymous function and executes it.
  2. eval the results of the function.

Here is a simplified example.

eval(
  function(a) {
    let str = a + ' world'
    let toEval = 'console.log("' + str + '")'
    // the string is now -> 'console.log("hello world")'
    return toEval
  }('hello')
)

Defines a function that accepts a string, adds ' world', and creates code as a new string, then returns it. This function has no name, and so is anonymous.

Executes the function with param hello.

Then execute the returned code (in string form) using eval.

Steven Spungin
  • 27,002
  • 5
  • 88
  • 78
0

eval method is actually used for executing scripts as soon as that eval statement is executed. It can execute script in string format. Just like below -

eval("console.log('hello world')")

In your case, it is actually nothing but just executing the script as soon as that statement is executed.

Ashvin777
  • 1,446
  • 13
  • 19
  • 2
    Except it DOES more than "executing the script". It GENERATES it before `eval` is called. – VLAZ Mar 02 '19 at 12:37
  • Also, for the record `eval(console.log('hello world'))` only does the same as the first line by coincidence. It's actually evaluating `console.log('hello world')` *first*, which prints to the console, and it then calls `eval(undefined)` because `console.log()` doesn't return nothing. So that's equivalent to `console.log('hello world')` followed by a line that is simply `eval()` and thus with no input it's a no-op and ignored. It's not *actually* an equivalent. – VLAZ Mar 02 '19 at 14:04