4
function y(fct) {
   var a = 2;
   var fctStr = String(fct);
   var fct1 = eval(fctStr);
   console.log("fctStr=" + fctStr); // output: fctStr=function x() { return a + 1 }
   console.log("fct1=");
   console.log(fct1);  // output: undefined.  Why it is undefined?  I expect fct1 is a function.
   return fct1();  // exception: undefined is not a function.
}
function x() { return a + 1 }
y(x)   // I expect it returns 3.  However, it throws exception at above "return fct1()" statement.

This code in Chrome will get fct1 as undefined. Why? I expected that fct1 would be a function.

Why this question is asked is because of this: How to write function-y accepting parameter-fct_x which accesses var-a which is required to be defined in function-y?

Community
  • 1
  • 1

2 Answers2

5

You need an expression to be produced.

Change

var fct1 = eval(fctStr);

to

var fct1 = eval("("+fctStr+")");
Denys Séguret
  • 372,613
  • 87
  • 782
  • 758
  • 1
    From the MDN docs: "`eval()` returns the value of the last expression evaluated." - without an expression, there is nothing to return :) – Niet the Dark Absol Dec 16 '14 at 16:07
  • Your answer is good. Thank you very much. I have read this, "eval() returns the value of the last expression evaluated.", but it does NOT make me think of I need to do this eval("(" + fctString + ")") instead of eval(fctString). I must miss something. What is it? Or, why var aa = eval("function x() {}") returns undefined? But var aa = eval("(function x() {})") returns a function? – Johnson Cheung HK06 Dec 16 '14 at 16:17
  • 1
    I don't know the exact answer, but I imagine it's the same reason that if you write an IIFE (Immediately Invoked Function Expression), that you need to wrap it in parenthesis before you can invoke it. – Brian Ball Dec 16 '14 at 16:20
  • 1
    Yes, read about "statement vs expression", and then "function expression". – Denys Séguret Dec 16 '14 at 16:26
  • It is an un-document behavior of eval(). I have just added an exmaple to the MDN document: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval as "Example: eval a string defining function required "(" and ")" as prefix and suffix" – Johnson Cheung HK06 Dec 16 '14 at 16:30
  • It's not un-documented : as pointed by Niet the MDN says you need an expression. – Denys Séguret Dec 16 '14 at 16:32
  • dystroy, can you show me the link of "statement vs expression"? – Johnson Cheung HK06 Dec 16 '14 at 16:33
  • 2
    @user2899992: If a function definition such as `function foo() {}` could be parsed as either function *declaration* or function *expression*, it will be parsed as function *declaration*. That happens in your case. A declaration is like a statement. Using the grouping operator (`(...)`) forces the definition to be evaluated as function expression. – Felix Kling Dec 16 '14 at 16:39
  • Yes, this is context dependent. You don't need parenthesis when an expression is expected (for example in the list of arguments of a function call or in the right part of an assignment). – Denys Séguret Dec 16 '14 at 16:41
  • In browser console, input **(function y(){})**, console returns **function y(){}**. It implies it is a value. Input **y**, console returns **undefined**. It means, function-y is not defined yet. On the other hand, input **function y(){}**, console returns **undefined**, it means it is not a value, implies it is a statement. Input **y**, console now returns **function y(){}**. The function-y is declared. – Johnson Cheung HK06 Dec 23 '14 at 12:23
  • @JohnsonCheungHK06 Yes. This is also useful to have named function expression not polluting the external scope (mainly for recursive functions). – Denys Séguret Dec 23 '14 at 12:33
0

Other's have answered in bits an pieces, but here's the whole answer:

function x() {return a + 1}

That defines a function named x, this is very similar to the following:

var x = function() {return a + 1}

In that case, an anonymous function is created and assigned to the variable x. So if you want to assign a function to a variable, there shouldn't be an identifier between function and ().

If you want the eval statement to return the function, then you need to wrap the function declaration with parenthesis (and not give the function a name). Edit: The function name is optional:

var x = eval('(function () {return a + 1})');
Brian Ball
  • 12,268
  • 3
  • 40
  • 51
  • Hu ? It works when the function expression is named. You can test OP's code with just added parethesis, as per my answer. – Denys Séguret Dec 16 '14 at 16:13
  • When I first tried it, it didn't work, but I must have fat-fingered something; you are correct, it does work. I've updated my answer. – Brian Ball Dec 16 '14 at 16:17
  • Is there a document telling people that "function() {}" is a statement-string, but "(function() {})" is expression-string? – Johnson Cheung HK06 Dec 16 '14 at 16:37
  • var aa = function() {}; will assign a function to var aa. This makes people thinking that "function() {}" is an "expression-string". However, javascript treats it as "statement-string". We need to use "(function() {})" as "expression-string". – Johnson Cheung HK06 Dec 16 '14 at 16:40