Como corrigir o erro jslint ‘Não faça funções dentro de um loop’.

Eu estou trabalhando para fazer todo o nosso código JS passar pelo jslint, às vezes com muitos ajustes com as opções para obter passagem de código legado para agora com a intenção de corrigi-lo corretamente mais tarde.

Há uma coisa que jslint reclama que eu não tenho um trabalho para. É quando usamos construções como essa, obtemos o erro “Não faça funções dentro de um 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]); } } 

Esse loop faz parte de uma implementação JS da inheritance clássica, em que as classs que estendem as classs existentes retêm a superpropriedade da class estendida ao invocar um membro da class estendida. Só para esclarecer, a implementação acima é inspirada neste post de John Resig.

Mas também temos outras instâncias de funções criadas dentro de um loop.

A única solução até agora é excluir esses arquivos JS do jslint, mas gostaríamos de usar o jslint para validação de código e verificação de syntax como parte de nossa continuous integration e criação de stream de trabalho.

Existe uma maneira melhor de implementar funcionalidade como essa ou existe uma maneira de ajustar código como este através do jslint?

Douglas Crockford tem uma nova maneira idiomática de alcançar o que foi dito acima – sua antiga técnica era usar uma function interna para ligar as variables, mas a nova técnica usa um criador de funções. Veja o slide 74 nos slides para a palestra “Function the Ultimate” . [Este compartilhamento de slides não existe mais]

Para os preguiçosos, aqui está o código:

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

(Acabei de me deparar com estas questões muitos meses depois de ter sido publicado …)

Se você fizer uma function em um loop, uma instância de uma function será criada para cada iteração do loop. A menos que a function que está sendo feita seja de fato diferente para cada iteração, use o método de colocar o gerador de funções fora do loop – isso não é apenas Crockery, ele permite que outras pessoas que lêem seu código saibam que essa foi sua intenção. .

Se a function é na verdade a mesma function sendo atribuída a diferentes valores em uma iteração (ou objects produzidos em uma iteração), então você precisa atribuir a function a uma variável nomeada e usar essa instância singular da function em atribuição dentro de uma iteração. loop:

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

Maior comentário / discussão sobre isso foi feito por outros mais espertos do que eu quando fiz uma pergunta semelhante aqui no Stack Overflow: JSlint error ‘Não faça funções dentro de um loop’. leva a questionar sobre o próprio Javascript

Quanto ao JSLint: Sim, é dogmático e idiomático. Dito isso, geralmente é “certo” – eu descubro que muitas pessoas que vocalizam negativamente sobre o JSLint na verdade não entendem (as sutilezas do) Javascript, que são muitas e obtusas.

Literalmente, contornar o problema, fazendo o seguinte:

  1. Crie um arquivo .jshintrc
  2. Adicione a seguinte linha ao seu arquivo .jshintrc

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

O JSLint é apenas um guia, você nem sempre precisa seguir as regras. A coisa é, você não está criando funções em um loop no sentido que ele está se referindo. Você só cria suas classs uma vez na sua aplicação, não de novo e de novo.

Se você estiver usando o JQuery, você pode querer fazer algo assim em um loop:

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

Para satisfazer o JSLint, uma maneira de contornar isso é (no JQuery 1.4.3+) usar o argumento de dados adicionais do manipulador para .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); } 

Apenas mova seu:

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

bloquear fora do loop e atribuí-lo a uma variável, como:

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

Então no loop tem:

prototype[prop] = makeFn(...)