0

I'm using the following function named isExpression to determine whether some JavaScript code is an expression:

function isExpression(code) {
    try {
        new Function("return " + code);
        return true;
    } catch (e) {
        return false;
    }
}

It works correctly for all test cases except one - it incorrectly treats a FunctionDeclaration as a FunctionExpression and returns true instead of false. Is there some way to fix this problem without having to write a parser?

Aadit M Shah
  • 72,912
  • 30
  • 168
  • 299
  • How about `new Function(code + " + 0");` ? Oh wait; `new Function(code.replace(/\n/g, ' ') + " + 0");` ... no that won't work either. Probably you have to write a parser :-) – Pointy Aug 12 '12 at 14:18
  • 3
    I don't think you can. A function declaration is a valid expression in an expression context. So if you just evaluate code in an expression context, there is no way to tell the difference. – Felix Kling Aug 12 '12 at 14:21
  • @Pointy - It wouldn't make any difference. It would still treat a __`FunctionDeclaration`__ as a __`FunctionExpression`__. – Aadit M Shah Aug 12 '12 at 14:22
  • @FelixKling - I know. The question is do I need to write a full fledged parser to determine if it's a function declaration. – Aadit M Shah Aug 12 '12 at 14:25
  • Well, I think it depends on what `code` can be. You could test if it starts with `function` (trim spaces), then it must be a declaration (or at least it would be interpreted as such). – Felix Kling Aug 12 '12 at 14:28
  • @FelixKling - You would still need to check whether it's a named function (if it's unnamed then it must be an expression). – Aadit M Shah Aug 12 '12 at 14:33
  • It depends on the context, you did not specify that. A function definition without a name will still be interpreted as declaration if it is not in an expression context (and throw an error). But you could easily test whether the definition has a name or not. If you just wont the test whether the code *can* be an expression, why bother to distinguish between declarations and expressions? – Felix Kling Aug 12 '12 at 14:35
  • @FelixKling - So is it safe to assume that if `code` starts with `"function"` after being trimmed then it's not an expression? – Aadit M Shah Aug 12 '12 at 14:37
  • 1
    Mmmh... let me think again. You want to take a piece of code and test whether it possibly could be an expression? Code starting with `function` could still be a function expression, either named or anonymous. Only by the **context** you can distinguish a named function expression from a function declaration. But it seems you don't have any context, so there would be no way to tell. Sorry, got a bit lost in between. – Felix Kling Aug 12 '12 at 14:42
  • @FelixKling - Well I'm writing this function to create a JavaScript REPL and `code` will contain the buffered input the user types (either a line or multiple lines if the brackets in the buffered lines are not [balanced](http://codereview.stackexchange.com/q/14532/15640)). So I'm assuming that the context wouldn't make a difference in this case. Taking this into consideration is it safe to assume that `code` is not an expression if it starts with `"function"` (after being trimmed)? – Aadit M Shah Aug 12 '12 at 15:10

2 Answers2

0

As @FelixKling pointed out the only way to determine if a function is a declaration and not an expression is to check the context of the function. However in a REPL (for which the isExpression function is intended) there is no context. Thus matters become simpler.

The code typed into a REPL can only be a function declaration if it starts with the keyword function (after trimming whitespace in the beginning). Hence it can be tested by the regular expression /^\s*function\s/.

@FelixKling points out that such a function may still be an expression and not a declaration depending upon the context of the function (i.e. the function is an expression if it's a non-source element). However the code passed to this function is guaranteed to be a source element.

If such a function construct were to be used as an expression using the conditional operator (e.g. function f() {} ? x : y) or using the comma operator (e.g. function f() {}, x) then isExpression would still return false. However such code would raise a SyntaxError. Hence the following implementation of isExpression will correctly test whether some code is an expression for all cases:

var isExpression = function (functionDeclaration) {
    return function (code) {
        if (functionDeclaration.test(code)) return false;

        try {
            Function("return " + code);
            return true;
        } catch (error) {
            return false;
        }
    };
}(new RegExp(/^\s*function\s/));
Aadit M Shah
  • 72,912
  • 30
  • 168
  • 299
-1

By checking the type of the value returned by your constructed function we can determine that the original expression was a function.

function isExpression(code) {
    try {
        return typeof (new Function("return " + code)) () !== 'function';
    } catch (e) {
        return false;
    }
}

Caveat : calls in the string will be invoked. This may make this less useful.

HBP
  • 15,685
  • 6
  • 28
  • 34
  • But the object is to determine if it is a function expression or a function declaration. – Quentin Aug 12 '12 at 14:40
  • This would be wrong in case `code` was genuinely a function expression (e.g. `(function () {})`). – Aadit M Shah Aug 12 '12 at 14:41
  • 1
    See http://jsfiddle.net/7HVkK/ - you get the same result. As comments on the question pointed out, the difference between a function expression and a function declaration is the context in which the code appears. Sticking `return` in front of it (as your code does) turns it into a function expression. – Quentin Aug 12 '12 at 18:03