LOCAWEB STYLE

Documentação Locaweb Style

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:

  1. Consistência de código;
  2. Melhores práticas;
  3. Escalabilidade;
  4. Fácil manutenção;
  5. 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.