javascriptexpressionfunction-expressionjavascript-function-declaration

Determine if JavaScript code is an expression


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?


Solution

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