Как исправить ошибку jslint "Не создавать функции в цикле"?

Я работаю над тем, чтобы все наши JS-коды проходили через jslint, иногда с большой настройкой с параметрами для передачи устаревшего кода в настоящее время с намерением исправить его правильно позже.

Есть одна вещь, о которой jslint жалуется, что у меня нет рабочего места. То есть при использовании таких конструкций, мы получаем ошибку "Не выполняйте функции в цикле".

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]);
    }
}

Этот цикл является частью реализации классического наследования JS, когда классы, расширяющие существующие классы, сохраняют свойство super расширенного класса при вызове члена расширенного класса. Просто для пояснения, вышеприведенная реализация вдохновлена ​​этим сообщением от Джона Ресига.

Но у нас также есть другие экземпляры функций, созданных в цикле.

Единственным обходным решением до сих пор является исключение этих JS файлов из jslint, но мы хотели бы использовать jslint для проверки кода и проверки синтаксиса как часть непрерывного процесса интеграции и сборки.

Есть ли лучший способ реализовать такие функции или есть способ настроить такой код через jslint?

Ответ 1

У Дугласа Крокфорда есть новый идиоматический способ достижения вышеуказанного - его старой техникой было использование внутренней функции для привязки переменных, но в новой технике используется создатель функции. См. слайд 74 в слайдах в его разговоре "Function the Ultimate" . [Это слайд-шоу больше не существует]

Для лени, вот код:

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

Ответ 2

(Я просто наткнулся на этот вопрос спустя много месяцев после его публикации...)

Если вы выполняете функцию в цикле, экземпляр функции создается для каждой итерации цикла. Если функция, которая выполняется, на самом деле отличается для каждой итерации, тогда используйте метод помещения генератора функции вне цикла - это не просто "Посуда", она позволяет другим, кто читает ваш код, знать, что это было вашим намерением.

Если функция на самом деле является той же функцией, которой назначаются разные значения в итерации (или объектах, созданных на итерации), вместо этого вам нужно назначить функцию именованной переменной и использовать этот особый экземпляр функции в назначение в цикле:

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

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

Большой комментарий/обсуждение об этом были сделаны другими умнее меня, когда я задал аналогичный вопрос здесь о переполнении стека: Ошибка JSlint 'Не выполняйте функции в цикле.' приводит к вопросу о самом Javascript

Что касается JSLint: Да, это догматично и идиоматично. Тем не менее, это, как правило, "правильно" - я обнаружил, что многие люди, которые отрицательно отзываются о JSLint, на самом деле не понимают (тонкости) Javascript, которые много и тупые.

Ответ 3

Буквально обойти проблему, выполнив следующие действия:

  • Создайте файл .jshintrc
  • Добавьте следующую строку в ваш .jshintrc файл

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

Ответ 4

JSLint - это только руководство, вы не всегда должны придерживаться правил. Дело в том, что вы не создаете функции в цикле в том смысле, в котором это имеет место. Вы только создаете свои классы один раз в своем приложении, но не снова и снова.

Ответ 5

Если вы используете JQuery, вы можете сделать что-то вроде этого в цикле:

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

Чтобы удовлетворить JSLint, один из способов обойти это (в JQuery 1.4.3+) использовать дополнительный аргумент данных обработчика для .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);
}

Ответ 6

Просто переместите:

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

заблокировать из цикла и присвоить его переменной, например:

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

Тогда в цикле есть:

prototype[prop] = makeFn(...)