Boas práticas de javascript
Padrões de código & melhores práticas em javascript
Aqui mostramos as práticas seguidas no código do Locaweb Style e podem ser mantidas nos projetos que usam este framework.
As principais vantagens de manter este padrão são:
- Consistência de código;
- Melhores práticas;
- Escalabilidade;
- Fácil manutenção;
- Uniformidade entre projetos.
A ideia é que o código mantenha unidade, seja ele escrito por uma ou várias pessoas, dentro de um projeto e entre projetos diferentes, diminuindo o tempo de aprendizado.
As regras a seguir propoem um padrão mínimo para desenvolvimento. O mais importante é manter a consistência de estilo de código. O que for escolhido como estilo para o seu projeto deverá ser seguido em todos os arquivos.
Padrões de escrita
Identação: utilizamos 2 espaços, mas o mais importante é seguir um estilo único. Nunca misture espaços e tabs em único arquivo. Para facilitar incluímos no projeto um arquivo de configuraçao de editores, o ".editorconfig", saiba mais sobre ele em editorconfig.org
Existem plugins para vários editores, no nosso time usamos:
Sintaxe: para separaçao dos blocos de código, utilizamos o estilo1TBS, por exemplo:
if ( x < 0 ) { console.log( x ) } else { console.log( y ) }Observe os alinhamentos, quebras de linhas, posição das chaves e espaços entre variáveis e operadores.
Revealing Module Pattern
Para melhorar a organização dos arquivos, funções, performance e diminuir a chance de erros e conflitos, adotamos o Revealing Module Pattern do Christian Heilmann.
As vantagens desse pattern são: organização, clareza, performance, expõe publicamente apenas as funções e variáveis que se deseja e namespace único evitando sobrescrever métodos facilmente.
Leia mais sobre ele no livro do Addy Osmani.
Um exemplo:
// Define o objeto global do projeto, igual a um já existente ou cria um novo objeto. var projeto = projeto || {}; // Define o módulo no objeto global. projeto.moduloUm = (function() { 'use strict'; function iniciar() { interno(); // ... } function atualizar() { // ... } function interno() { // ... } return { iniciar:iniciar, atualizar: atualizar }; }());
O ideal é ter um módulo por arquivo, e que eles não se iniciem sozinhos.
Um módulo central que funcione como controlador de quais serão executados é uma boa prática.
Exemplo:
Módulo de gráficos no arquivo charts.js
var projeto = projeto || {}; project.Grafico = (function() { 'use strict'; function iniciar() { inserirGrafico(); } function inserirGrafico() { // ... } return { iniciar:iniciar }; }());
Módulo servidores no arquivo servers.js
var projeto = projeto || {}; projeto.Servidores = (function() { 'use strict'; function iniciar() { buscaServidores(); } function buscaServidores() { insereListaServidores() // ... } return { iniciar:iniciar }; }());
Finalmente o arquivo que controla quando cada módulo será executado, da maneira que preferir:
Arquivo carregaModulos.js
var projeto = projeto || {}; projeto.carregaModulos = (function($) { 'use strict'; function inicio() { if ( $('.grafico').size() ) { projeto.graficos.inicio(); } if ( window.location.pathname === '/servidores' ) { projeto.servidores.inicio(); } } return { inicio:inicio }; }(jQuery)); projeto.carregaModulos.inicio();
Código em apenas um idioma
Para facilitar a manutençao e a legibilidade do código, procure usar apenas um idioma, em nomes de funções, variáveis e comentários.
O mais importante é seguir um padrão, mantendo ele do início ao fim do projeto. Exceções, claro, sao libraries e plugins que utilizados.
Seja qual for a língua utilizada é importante ser claro, utilizando nomes descritivos, evitando ser prolixo nos nomes, funções e comentários, como abordado no tópico Seja compreensível abaixo.
'use strict';
Ao utilizar "use strict";
no início de códigos ou funções, declara-se uma mudança na sintaxe do Javascript, que lança mais exceções mostrando problemas no código. Os erros mais comuns são uso de palavras reservadas do javascript (ECMAScript 5 e 6), uso de variáveis não declaradas e uso de funções descontinuadas.
Para utilizar, basta inserir a string 'use strict';
antes do bloco de código a ser avaliado ou função. Exemplos:
"use strict"; idade = 17; // lança um erro de referência function soma(a, a, c){ // erro de sintaxe "use strict"; return a + b + c; // exibe o erro se a função é chamada }
Leia mais em MDN - Strict mode
Nomenclatura de pastas, arquivos, módulos e plugins
Um projeto pode ter muitos arquivos javascript, então é importante que estejam bem organizados desde o início, separados e bem nomeados.
A estrutura pode variar muito de projeto para projeto, mas as recomendações mínimas são:
- Nomes dos arquivos na mesma língua em que são escritos;
- Nomes sem camelCase;
- Pastas separadas por tipos (bibliotecas/frameworks/plugins);
- Plugins identificados por versão.
Exemplo:
── javascripts ├── libs │ ├── jquery.min.js │ └── underscore.min.js │ └── highcharts.min.js ├── plugins │ ├── jquery.validate-1.2.js │ ├── jquery.slider-3.1.js │ └── jquery.validate.addicional-methods-0.8.js ├── modules │ ├── charts │ | ├── servers.js │ | └── transfer.js │ ├── servers.js │ └── servers.forms.js ├── utils.js └── main.js
Seja compreensível
Use nomes de variáveis e funções auto explicativos e simples. Crie algum padrão e mantenha em todo o projeto.
No Locastyle utilizamos variáveis e funções camelCase, pela facilidade de leitura, escrita e praticidade ao se trabalhar.
Exemplos:
Variáveis com nomes ruins:
// Curtos, posicionamento no código e abreviações var x1; var campo1; var latXY; // Longos demais var valorEixoXGraficoConsumo; var campoTextoPrimeiroNome;
Funções com nomes ruins:
// Nomes que descrevem o código, nao o objetivo da função function maiorDeDezoitoAnos(idade){ return idade >= 18; } // É melhor descrever o objetivo function possuiMaioridade(idade){ return idade >= 18; }
É uma boa ideia criar um padrão para suas variáveis e funções, como por exemplo:
// Variáveis com $ no início são elementos/objetos jQuery var $header = $('#header'); var $menuItens = $('#menu a'); // Maiúsculas para constantes var PASTA_IMAGENS = '/assets/images/'; var NOME_CLIENTE = 'Fulano de Tal'; // _ no início para variáveis e funções privadas var _contador = 0;
Confira o padrão de nomenclatura do Douglas Crockford
Evite globais
No geral é uma péssima idéia, porque aumenta a chance de ter algo sobrescrito. Uma opção é utilizar closures e module pattern.
Seja consistente no estilo de código
É possível escrever seu código de muitas maneiras, mas procure manter o mesmo estilo em todo seu projeto. Mantendo um padrão nos nomes, identacões, patterns, etc.
Uma dica, utilize o jslint para validar seu código.
Escreva os comentários necessários
É comum ouvir "Um bom código não precisa de explicação", mas na prática em projetos maiores, procure explicar a finalidade do seu código. Muitas pessoas, de diferentes níveis, podem ter que trabalhar no seu código e nem sempre elas tem experiência, tempo ou conhecimento do negócio para entender tudo. Facilite o desenvolvimento e manutenção comentando, mas não explicando o que ele faz, mas qual a regra de negócio.
Exemplo:
// Ruim: verifica se é maior de 18 // Bom: menores de idade sao redirecionados if( idade >= 18 ){ ... }
Lembrando que comentários devem existir apenas na versão de desenvolvimento, devendo ser removidos no arquivo minificado que é entregue em produção.
Use testes automatizados
Estamos usando Jasmine para testar o Locaweb Style, assim como os comentários, é uma ótima forma de explicar o que seu código faz ou deveria fazer. Além de ter o dispositivo de alerta quando algum teste quebra, dá mais segurança para manutenções e inclusão de novas funcionalidades.
Evite misturar tecnologias
Na prática do dia a dia, algo bem simples: Estilize seu HTML com CSS, Não com JS.
Exemplo:
// Errado $('#nome-usuario').css({ 'border': '1px solid red', 'color': 'red' }); // Certo $('#nome-usuario').addClass('erro');
Crie os estilos que precisar (e animações, quando possível) no CSS, e no javascript controle quando os estilos são aplicados, em vez de aplicar propriedades.
Use sintaxe abreviada
Conheça as notações de variáveis e funções abreviadas e procure usá-las.
Exemplos:
// Use var cores = ['rosa', 'azul', 'verde']; // Em vez de var cores = new Array(); cores[0]='rosa'; cores[1]='azul'; cores[2]='verde'; // Use var margem = altura || 10; // Em vez de if(altura === undefined){ var margem = altura; } else { var margem = 10; } // Use var direcao = (x > 100) ? 1 : -1; // Em vez de var direcao; if(x > 100){ direcao = 1; } else { direcao = -1; }
Modularize seu código
Evite escrever funções, trechos de código muito longos, ou aninhados. Procure separar regras e evite códigos repetidos.
Exemplo:
// Em vez de $('#botao1').on('click', function(){ $('#resultado').load('ajax/lista-pessoas.html', function() { $('#formulario').slideUp() }); }) $('#botao2').on('click', function(){ $('#resultado').load('ajax/lista-empresas.html', function() { $('#formulario').slideUp() }); }) //... // Faça function ocultaFormulario(){ $('#formulario').slideUp(); } $('#botao1').on('click', function(){ $('#resultado').load('ajax/lista-pessoas.html', function() { ocultaFormulario(); }); }) $('#botao2').on('click', function(){ $('#resultado').load('ajax/lista-empresas.html', function() { ocultaFormulario(); }); }) // Ou melhor function ocultaFormulario(){ $('#formulario').slideUp(); } function carregaDados( elemento, url){ $(elemento).on('click', function(){ $('#resultado').load(url, function() { ocultaFormulario(); }); }) } carregaDados('#botao1', 'ajax/lista-pessoas.html'); carregaDados('#botao2', 'ajax/lista-empresas.html');
Configurações e internacionalização
Quando estiver criando a aplicação, pense em cada valor, texto, variável, se é o caso de deixá-la separada, permitindo alterá-la depois.
Exemplo:
// Em vez de $.ajax({ type : "POST", url : "api/usuarios", data : { limite: 10 }, success: function( dados ){ $('#formulario').hide(); $('#mensagem').text( 'Busca efetuada com sucesso' ); $('#lista').html( dados ).fadeIn('fast'); }, error : function(){ $('#mensagem').text( 'Erro na busca' ); } }); // Faça var configuracoes: { mensagens = { successo : 'Busca efetuada com sucesso', erro : 'Erro na busca' }, api: { usuarios: "api/usuarios" }, animacao: { velocidade: "fast" }, lista: { porPagina: 10 } } //... $.ajax({ type : "POST", url : configuracoes.api.usuarios, data : { limite: configuracoes.lista.porPagina} , success: function( dados ){ $('#formulario').hide(); $('#mensagem').text( configuracoes.mensagens.successo ); $('#lista').html( dados ).fadeIn( configuracoes.animacao.velocity ); }, error : function(){ $('#mensagem').text( configuracoes.mensagens.erro ); } });
Utilização correta de Eventos
Ao escrever seu código e registrar comportamentos e eventos na interação do usuário é importante ter cuidado na escolha correta dos eventos.
Um caso comum de erro é vincular uma função no clique do botão enviar de um formulário, em vez de chamar a ação no submit do formulário:
// Errado // se o usuário submeter o form teclando ENTER a função não é executada $('#meuForm').delegate('#enviar', 'click', function(event){ if( $('#meuForm').isValid() ){ // ... ação } }); // correto // a ação é executada no clique ou no ENTER $('#meuForm').on('submit', function(event){ if( $('#meuForm').isValid() ){ // ... ação } });
Também é preciso saber o uso correto e diferenças entre os métodos, no caso do jQuery, sabendo os corretos para cada situação e os que estão em desuso.
// Os seguintes foram descontinuados .die() .load() .error() .unload() .live() .toggle() // os seguintes métodos podem ser evitados .mousedown() .blur() .rezise() .keyup() .keypress() .mousenter() .change() .hover() .ready() .focusin() .mouseleave() .click() .scroll() .submit() .mouseup() .mouseout() .dblclick() .keydown() .focusout() .mouseover() .focus() .select() // e devem ser substitídos por $('#elemento').on('blur', function(){ ... }) $('#elementoPai').delegate('#elemento', 'blur', function(){ ...}); // de prefrência o .delegate() que tem melhor performance // por fazer a seleção dentro de um escopo ( '#elementoPai' ) $('#escopo').delegate('#elemento', 'click', function(){ ...});
Evite muitos aninhamentos
Facilite o entendimento e manutençao dos seus códigos. Utilize as práticas anteriores e evite um código do tipo:
$('#botao').on('click', function(){ $.ajax({ type: "POST", url: 'usuarios', success: function(data){ $('resultado').fadeIn('fast', function(){ $('#formulario').animate({ heigth : 0, opacity: 0 }, 300, function(){ $('#mensagem').animate({ heigth : 200, opacity: 1 }, 300, function(){ // ... }) } ) }) }, error: function(){ $('#mensagem').text(mensageErro); } }); });
Otimize loops
Existem várias maneiras de fazer loops, uma melhores que outras. Hoje, comum encontrar códigos usando loops do jquery ($.each
) onde loops nativos poderiam resolver. Uma técnica simples para melhorar ainda mais os loops for()
nativos é fazer cache de variáveis de comparação, evitando executar o .length
a cada iteração.
Exemplo:
// Use var cores = ['Azul', 'verde', 'rosa', 'vermelho']; for(var count=0, quantCores=cores.length; count < quantCores; count++){ // console.log( cores[i] ); } // em vez de var cores = ['Azul', 'verde', 'rosa', 'vermelho']; for(var count=0; i < cores.length; count++){ console.log( cores[i]) ; }
Para comparação, um teste de performance no jsperf analisando diversos tipos de loop. O loop otimizado, como acima, executou 159k vezes, contra 113k vezes do tradicional.
Minimize acessos ao DOM
Evite ao máximo acessar o DOM para buscar ou inserir informações. É lento e não se pode confiar no que está no DOM. Coisas simples podem minimizar o acesso, por exemplo, se alguma função precisa atualizar uma tabela no seu HTML, em vez de percorrer <td>
por <td>
alterando os valores, pode ser mais eficiente criar uma função que reescreva toda a tabela, e depois simplesmente troque a antiga pela nova. Outra simples é fazer cache de objetos jQuery, por exemplo:
// Em vez de $('#botaoEnviar').on( 'click', function(){ $(this).prop( 'disabled', true ); $('$formulario').addClass( 'enviando' ); $.post( 'ajax/contato', function( dados ) { $('#mensagem').html( dados ); $('$formulario').removeClass( 'enviando' ); $('#botaoEnviar').prop( 'disabled', false ); }); }); // Faça var $botaoEnviar = $('#botaoEnviar'); var $formulario = $('#formulario'); var $mensagem = $('#mensagem'); $botaoEnviar.on( 'click', function(){ $botaoEnviar.prop( 'disabled', true ); $formulario.addClass( 'enviando' ); $.post('ajax/contato', function( dados ) { $mensagem.html( dados ); $formulario.removeClass( 'enviando' ); $botaoEnviar.prop( 'disabled', false ); }); });
Adicione funcionalidades com javascript, não conteúdo
Se sua aplicação precisa criar e inserir muito HTML, códigos estão cheios de <div>
, <td>
e etc, analise e repense sua aplicação, veja se não é o caso de usar algum sistema de templates ( handlebars... ).
São códigos dificeis de manter, que podem ter problemas de desempenho. Caso precise, use templates separados do JS, ou carregue HTML do back-end com AJAX.
Não reinvente a roda
Use bibliotecas e frameworks estáveis, que possuem estrutura conhecida.
Antes de inserir libraries e plugins, verifique a real necessidade, o suporte e compatibilidade.
Código de desenvolvimento não é código de produção
Compacte, remova comentários, concatene os javascripts antes de mandar para produção. Existem diversas ferramentas para isso, inclusive online. Tenha uma versão de desenvolvimento, normal, comentada e uma para o site em produção.