How to fix jslint error 'Don't make functions within a loop.'?

I am working on making all of our JS code pass through jslint, sometimes with a lot of tweaking with the options to get legacy code pass for now on with the intention to fix it properly later.

There is one thing that jslint complains about that I do not have a workround for. That is when using constructs like this, we get the error 'Don't make functions within a loop.'

for (prop in newObject) {
    // Check if we're overwriting an existing function
    if (typeof newObject[prop] === "function" && typeof _super[prop] === "function" &&
        fnTest.test(newObject[prop])) {
        prototype[prop] = (function(name, func) {
            return function() {
                var result, old_super;

                old_super = this._super;
                this._super = _super[name];
                result = func.apply(this, arguments);
                this._super = old_super;

                return result;
            };
        })(prop, newObject[prop]);
    }
}

This loop is part of a JS implementation of classical inheritance where classes that extend existing classes retain the super property of the extended class when invoking a member of the extended class. Just to clarify, the implementation above is inspired by this blog post by John Resig.

But we also have other instances of functions created within a loop.

The only workaround so far is to exclude these JS files from jslint, but we would like to use jslint for code validation and syntax checking as part of our continuous integration and build workflow.

Is there a better way to implement functionality like this or is there a way to tweak code like this through jslint?

Answers:

Answer

Douglas Crockford has a new idiomatic way of achieving the above - his old technique was to use an inner function to bind the variables, but the new technique uses a function maker. See slide 74 in the slides to his "Function the Ultimate" talk. [This slideshare no longer exists]

For the lazy, here is the code:

function make_handler(div_id) {
    return function () {
        alert(div_id);
    };
}
for (i ...) {
    div_id = divs[i].id;
    divs[i].onclick = make_handler(div_id);
}
Answer

(I just stumbled on this questions many months after it was posted...)

If you make a function in a loop, an instance of a function is created for each iteration of the loop. Unless the function that is being made is in fact different for each iteration, then use the method of putting the function generator outside the loop -- doing so isn't just Crockery, it lets others who read your code know that this was your intent.

If the function is actually the same function being assigned to different values in an iteration (or objects produced in an iteration), then instead you need to assign the function to a named variable, and use that singular instance of the function in assignment within the loop:

handler = function (div_id) {
    return function() { alert(div_id); }
}

for (i ...) {
    div_id = divs[i].id;
    divs[i].onclick = handler(div_id);
}

Greater commentary/discussion about this was made by others smarter than me when I posed a similar question here on Stack Overflow: JSlint error 'Don't make functions within a loop.' leads to question about Javascript itself

As for JSLint: Yes, it is dogmatic and idiomatic. That said, it is usually "right" -- I discover that many many people who vocalize negatively about JSLint actually don't understand (the subtleties of) Javascript, which are many and obtuse.

Answer

Literally, get around the problem by doing the following:

  1. Create a .jshintrc file
  2. Add the following line to your .jshintrc file

    {"loopfunc" : true, // tolerate functions being defined in loops }

Answer

JSLint is only a guide, you don't always have to adhere to the rules. The thing is, you're not creating functions in a loop in the sense that it's referring to. You only create your classes once in your application, not over and over again.

Answer

If you are using JQuery, you might want to do something like this in a loop:

for (var i = 0; i < 100; i++) {
  $("#button").click(function() {
    alert(i);
  });
}

To satisfy JSLint, one way to work around this is (in JQuery 1.4.3+) to use the additional handler data argument to .click():

function new_function(e) {
  var data = e.data; // from handler
  alert(data); // do whatever
}

for (var i = 0; i < 100; i++) {
  $("#button").click(i, new_function);
}
Answer

Just move your:

(function (name, func) {...})()

block out of the loop and assign it to a variable, like:

var makeFn = function(name, func){...};

Then in the loop have:

prototype[prop] = makeFn(...)

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us Javascript

©2020 All rights reserved.