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.