Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JSLint claims certain recursive function calls are "out of scope"

I have a JavaScript snippet with a recursive function call:

(function () {
    "use strict";

    var recurse = function (x) {
        if (x <= 0) {
            return;
        }
        return recurse(x - 1);
    };

    recurse(3);
}());

This does nothing but call itself a few times, but it runs.

Pasting the above into JSLint gives me this error:

'recurse' is out of scope.

However, if I paste in the following snippet, (using a function declaration instead of var):

(function () {
    "use strict";

    function recurse(x) {
        if (x <= 0) {
            return;
        }
        return recurse(x - 1);
    }

    recurse(3);
}());

JSLint loves it, no errors.

I know that the goal of JSLint is to prevent bugs in JavaScript code. Does anyone know why JSLint thinks the first one is bad JavaScript? What bug am I preventing by not making a recursive call in the first way?

EDIT: To any future visitors to this question: Neither of these JavaScript snippets throw any errors in the latest version of JSLint.

like image 234
Captain Delano Avatar asked Nov 18 '15 18:11

Captain Delano


People also ask

What does the JSLint error {a} used out of scope mean?

The " {a} used out of scope" error (and the alternative " {a} used outside of binding context" error) are thrown when JSLint, JSHint or ESLint encounters a reference to a variable declared in an inner block. In the following example we declare the variable x in the body of an if statement and then attempt to return it from the enclosing function:

How does JSLint work in JavaScript?

JSLint takes a JavaScript source and scans it. If it finds a problem, it returns a message describing the problem and an approximate location within the source. The problem is not necessarily a syntax error, although it often is. JSLint looks at some style conventions as well as structural problems.

What does JSLint look for in a void statement?

In JavaScript, void is a prefix operator that always returns undefined. JSLint does not expect to see void because it is confusing and not very useful. Regular expressions are written in a terse and cryptic notation. JSLint looks for problems that may cause portability problems.

What happens if you don't change the prefix in JSLint?

If you neglect to use the new prefix, no new object will be made and this will be bound to the global object. JSLint enforces the convention that constructor functions be given names with initial uppercase. JSLint does not expect to see a function invocation with an initial uppercase name unless it has the new prefix.


1 Answers

There is nothing wrong with either style. As far as I can tell, this is an inappropriate warning.

The issue appears to be that a variable declaration that includes an assignment does not cause JSLint to register the presence of the declared variable name in scope until the entire assignment is evaluated. That is, when JSLint reads var recurse = ..., it does not realize recurse is a declared variable until after it evaluates the right-hand side of the assignment. In this case, the right-hand side includes a function that makes use of the declared variable recurse, but JSLint didn't know about the existence of recurse yet, because it hadn't finished parsing the entire assignment.

Consider that this code works exactly the same as your var example but produces no warnings in JSLint:

(function () {
    "use strict";

    var recurse;
    recurse = function (x) {
        if (x <= 0) {
            return;
        }
        return recurse(x - 1);
    };

    recurse(3);
}());

By drawing out var recurse as a separate statement, JSLint first learns that recurse is declared in the current scope, and then parses the assignment. With your combined var recurse = ... (which, again, is not wrong), JSLint erroneously parses the assignment first and then learns about the existence of recurse.

like image 124
apsillers Avatar answered Oct 22 '22 22:10

apsillers