Как исправить ошибку 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?


person Ernelli    schedule 14.06.2010    source источник


Ответы (6)


У Дугласа Крокфорда есть новый идиоматический способ достижения вышеизложенного - его старая техника заключалась в использовании внутренней функции для связывания переменных, но в новой технике используется создатель функций. См. слайд 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);
}
person Skilldrick    schedule 14.06.2010
comment
Чем создатель функций лучше, чем создание новой функции для каждой итерации цикла? Разве это не то же самое? - person Gili; 19.12.2012
comment
@Gili: код для создания функции make_handler выполняется в точке, где указано function make_handler(div_id). Оказавшись внутри цикла for, make_handler теперь является ссылкой на эту функцию и не создается заново для каждой итерации цикла. - person Ash Clarke; 19.02.2013
comment
Не было бы более эффективно выделить функцию в var - person Pascalius; 11.03.2013
comment
Поскольку слайды все еще недоступны, посмотрите видео (начиная с 1:06:15) — youtube.com /watch?v=ya4UHuXNygM - person Chris Herring; 04.09.2013
comment
Какой смысл оборачивать функцию внутри функции - почему бы просто не объявить функцию выше? Есть ли какое-то преимущество, которое я упускаю? -- Ах, я вижу, это только для чего-то вроде обработчиков, которые не вызываются сразу (поэтому не относится к примеру OP) - person Dominic; 14.05.2014
comment
@AshClarke Правильно, что make_handler не воссоздается для каждой итерации цикла, но остается то, что для каждой итерации цикла значение, возвращаемое make_handler, является новым функциональным объектом. Код в этом ответе просто скрывает от средства проверки кода, что новая функция создается с каждой итерацией, но не предотвращает создание новой функции с каждой итерацией. . Сравните возвращаемые значения нескольких вызовов make_handler с === и убедитесь в этом сами. - person Louis; 13.10.2014
comment
@ Луи Да, правда; Я должен был быть более явным. Я действительно пытался объяснить, что создание функции make_handler не оценивалось снова и снова, но это хорошее замечание, которое я должен был сделать о возвращаемом значении. - person Ash Clarke; 14.10.2014
comment
Создатель функций лучше, потому что он действительно будет работать правильно. См. stackoverflow.com/questions/ 750486/ - person Vadim; 10.01.2017

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

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

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

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

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

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

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

person Zhami    schedule 21.01.2011
comment
пожалуйста, добавьте vars для переменных (обработчик, div_id). - person Ohad Kravchick; 10.01.2012
comment
когда вы вызываете обработчик (div_id), вы фактически запускаете функцию, которая покажет предупреждение. Это произойдет, когда запускается for, а не когда пользователь нажимает на div. - person Ohad Kravchick; 10.01.2012
comment
Хотя переменные не объявлены, это псевдокод... также, когда вы вызываете handler(div_id); он возвращает функцию, он не будет запускать предупреждение, пока не будет нажат в этом примере. Вот полный пример: jsfiddle.net/scottux/RWCje - person Scottux; 31.05.2012
comment
+1, но я считаю это ложным срабатыванием, когда функция фактически использует разные переменные для каждой итерации цикла. Использование создателя функций ничем не лучше, чем создание его экземпляра внутри цикла. - person Gili; 19.12.2012

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

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

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

person lifebalance    schedule 15.10.2016
comment
Я вижу вашу точку зрения. Если вы понимаете вопрос буквально, то на самом деле это правильный ответ. Проголосовал против, потому что я думаю, что это плохой совет. Любой, кто задает этот вопрос, должен быть образован. SO не позволяет мне отменить свой голос против, если ответ не отредактирован... - person Thijs Koerselman; 02.11.2016

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

person Evan Trimboli    schedule 14.06.2010
comment
Я знаю, что jslint — это всего лишь рекомендация и что он предназначен для выявления плохого кода JS, но все же это хороший инструмент для предварительной квалификации и проверки работоспособности вашего кода в автоматизированной среде сборки/тестирования. Как я уже писал, меня интересуют обходные пути, чтобы заставить код передавать jslint, а не вообще избегать запуска jslint. - person Ernelli; 14.06.2010
comment
Конечно, но если вы посмотрите на ответ ниже, вы все еще делаете функцию в цикле. Вы добавляете дополнительный код только для того, чтобы успокоить JSLint. Зачем тебе это? - person Evan Trimboli; 15.06.2010
comment
Да, я все еще делаю функции в цикле, моя проблема заключалась в том, что я хотел, чтобы этот код передал jslint. Теперь Skilldrick и Matt Eberts дали мне рабочие решения, которые я протестировал, и теперь мой код работает и проходит jslint. Я старый программист C/C++, я привык, что проверка синтаксиса является частью этапа компиляции. Javascript не компилируется, jslint максимально близок к использованию современного компилятора c/c++ с предупреждениями. - person Ernelli; 15.06.2010
comment
Конечно, эти решения очевидны. Дело в том, что вы все еще создаете функции внутри цикла, вы только что написали их определенным образом, который, возможно, более запутан, просто чтобы успокоить какой-то инструмент. В любом случае, если это то, чем вы хотите заниматься, действуйте. - person Evan Trimboli; 15.06.2010
comment
Возможно, это обсуждалось кем-то еще в этом обсуждении, но я этого не вижу: мне кажется, что предложенное решение лучше, поскольку подразумевает, что вы привязываете функцию к циклу, а не создаете ее. Технически да (очевидно), но насколько я понимаю, JSLint дает вам эту подсказку, потому что функции должны быть доступны во время выполнения, до того, как будет определен итератор или предел цикла. В этом случае, если итератор приводит к 0 итерациям цикла, функция все еще объявлена, но не привязана. Что касается того, как javascript на самом деле интерпретирует это, я предполагаю, что эта часть спорна. - person NateDSaint; 14.05.2012

Если вы используете 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);
}
person jevon    schedule 20.02.2012

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

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

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

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

Затем в цикле есть:

prototype[prop] = makeFn(...)

person Matt Eberts    schedule 14.06.2010
comment
Вы действительно должны научиться форматировать свои ответы немного лучше. Я отредактирую это для вас. - person TheCarver; 18.07.2016