Organize seu código Javascript de maneira fácil!

Published:

Ao longo desses anos nesta indústria vital já participei de inúmeros projetos... e foram tantos que os dedos das mãos seriam insuficientes para contá-los. Alguns deles me orgulho, outros nem tanto (afinal como você, eu também já produzi muita coisa de qualidade duvidosa). Ainda assim acredito na máxima que as pessoas sempre evoluem, uma vez que estas buscam sempre o melhor dentro da missão de cada uma.

Organizar código Javascript para quem está iniciando nem sempre é fácil, pois na ótica do programador novato a linguagem é o mesmo que um monte de blocos de código (leia funções) que este foi criando ou até mesmo copiando para resolver os problemas do dia-a-dia. Esse pensamento, somado a inúmeras más práticas vão tornando o código feito com Javascript um tanto que caótico.

Entre as principais (más práticas), eis algumas que a meu ver atrapalham e irritam grande parte dos membros de um time de desenvolvimento (ah e faz a mãe do programador, ser lembrada):

  • falta de indentação
  • falta de comentários
  • variáveis espalhadas
  • variáveis globais
  • funções e variáveis criadas sem propósito

Mas o objetivo deste artigo não é só citar as partes ruins do desenvolvimento, e sim propor uma forma elegante de resolver isso. Então vamos a uma solução simples para organização dos seus códigos feitos com JavaScript.

Funções nomeadas x funções anônimas

Todo mundo aqui, deve saber o que são funções nomeadas e anônimas em JavaScript. Se não sabe, eis alguns exemplos:

1
2
3
4
// função nomeada
function common() {
    return true;
}

Acima vemos uma função nomeada e, a seguir, uma função anônima:

1
2
3
4
// função anônima
var anonymous = function() {
 return true;
}

Observando os dois códigos, vê-se pouca diferença, não é mesmo? Os dois quando executados retornam a mesma coisa (true), porém são padrões sintáticos bem distintos.

No primeiro, temos uma função definida com o nome common, e é através desse nome que será possível invocar essa função, desse jeito:

1
2
common();
// => true

No segundo, note que a função não possui um nome, então ela precisou ser atribuída a variável anonymous para que pudesse ser invocada. Neste caso a chamada seria idêntica ao exemplo anterior:

1
2
anonymous();
// => true

Entendeu o conceito? Em linhas gerais, desculpe a ambiguidade, a função nomeada chama-se assim porque tem um nome. A função anônima, por sua vez não possui um nome, então a única forma de utilizá-la é atribuindo-a algo (uma variável, uma propriedade em um objeto, etc). Perdi esse tempo, explicando isso, pois acho que vai valer a pena para você entender o que vamos fazer a seguir.

Começando a organização

Vamos iniciar os trabalhos, criando uma função anônima:

1
function() {}

Se você salvar esse código e tentar e xecutá-lo em sua página, ou mesmo rodá-lo no console do seu navegador tomará um SyntaxError. E isso tem um motivo óbvio, pois se a função não tem nome como eu vou chamá-la? Então teoricamente eu seria obrigado a atribui-la a uma variável ou algo do tipo. Neste caso, vamos fazer diferente:

1
(function() {})

Colocando esse código entre parênteses, o SyntaxError que estava rolando até então sumirá. Mágica? Magia negra? Não! Simplesmente os parênteses agruparam a nossa função, formando uma espécie de expressão. Esse código servirá de embrulho (wrapper) para tudo que vamos fazer daqui pra frente. Farei um pequeno teste agora, colocando um simpático console.log dentro da nossa função e, se tudo der certo, o resultado será a exibição do texto "less is more!":

1
2
3
(function() {
 console.log("less is more!");
})

Se você executou esse código na sua página, e deu uma olhadinha no console, vai perceber que não houve retorno algum. Ué, o que que houve? Ocorre que apesar de termos uma função que implementa uma única linha de código, ele só está definida, porém não foi executada. Para fazer isso, basta adicionar parênteses no final da expressão, desta forma:

1
2
3
(function() {
 console.log("less is more!");
})();

Bingo! A partir de agora, toda vez que a página for carregada, esse função será executada "automagicamente"! Então temos a base necessária para organização de nossos códigos.

Organizando seu código

Você já deve ter ouvido falar, que toda variável precisa ser declarada usando a palavra chave var. Não usar o var, custa caro, pois uma variável definida sem ele vai parar no escopo global, então para seguir a régua de boas práticas vamos sempre usá-lo.

1
2
3
4
5
6
(function() {
    console.log("less is more!");

    var box = {};

})();

No exemplo acima, defini uma variável box que armazena um objeto vazio. A grande vantagem dessa abordagem é que box não está disponível em nenhum outro escopo a não ser o definido pela função wrapper que fizemos. Então se algum espertinho tentar ir no console e digitar:

1
2
console.log(box);
// => ReferenceError: box is not defined

Vai tomar um belo de um ReferenceError no meio da cara! Bom né? Não sujamos o escopo global e ainda ganhamos privacidade! :D

Uma vez que temos o objeto box, podemos adicionar propriedades e métodos a este objeto. Imagine agora que precisamos criar um controle bem simples, para organizar uma fila com nomes de automóveis. A ideia é começar com a fila vazia, ir adicionando os veículos e retornar a lista no final. Para isso, vamos usar uma propriedade chamada queue e dois métodos, que vamos chamar de addItem e getQueue:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
(function() {
    console.log("less is more!");

    // criando o objeto (vazio) box
    var box = {};

    // adicionando a propriedade queue (fila)
    box.queue = [];

    // adicionando o métodos addItem (adicionar item)
    box.addItem = function() {

    };

    // adicionando o métodos getQueue (recuperar fila)
    box.getQueue = function() {

    };
})();

Como podemos perceber, a propriedade queue é um array vazio. Então nosso método addItem precisa receber um veículo como parâmetro e adicioná-lo nesta fila. Por sua vez, o método getQueue apenas retorna uma string com os veículos que temos na fila, separados por um hífen.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
(function() {
    console.log("less is more!");

    // criando o objeto (vazio) box
    var box = {};

    // adicionando a propriedade queue (fila)
    box.queue = [];

    // adicionando o métodos addItem (adicionar item)
    box.addItem = function(car) {
        return box.queue.push(car);
    };

    // adicionando o métodos getQueue (recuperar fila)
    box.getQueue = function() {
        return box.queue.join(" - ");
    };
})();

Feito isso, se você rodar esse código, deve estar se perguntando: como diabos vou invocar os métodos addItem e getQueue do objeto box que acabei de implementar, visto que ele não está acessível no escopo global? A resposta é simples, basta fazer com que nossa função wrapper retorne o objeto box (forma simples) ou podemos configurar exatamente o que vai ser retornado. Vamos ao primeiro exemplo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var GLOBALCAR = (function() {
    console.log("less is more!");

    // criando o objeto (vazio) box
    var box = {};

    // adicionando a propriedade queue (fila)
    box.queue = [];

    // adicionando o métodos addItem (adicionar item)
    box.addItem = function(car) {
        return box.queue.push(car);
    };

    // adicionando o métodos getQueue (recuperar fila)
    box.getQueue = function() {
        return box.queue.join(" - ");
    };

    return box;
})();

Como você deve ter percebido, além de retornar o objeto box, eu atribui o nosso wrapper a uma variável global chamada GLOBALCAR (mesmo com a palavra chave var, como GLOBALCAR não está dentro de uma função, ela fica visível no escopo mais amplo do Javascript, podendo ser acessada de qualquer local). Dessa forma, GLOBALCAR tornou-se um objeto. Para testar nossa implementação podemos executar (eu incentivo você neste momento a testar via console):

1
2
GLOBALCAR;
// => Object {queue: Array[0], addItem: function, getQueue: function}

Viu isso? GLOBALCAR é um objeto contendo queue, addItem e getQueue. Então podemos executar o método addItem para adicionar os carros a nossa fila:

1
2
3
4
5
6
GLOBALCAR.addItem("Gol");
// => 1
GLOBALCAR.addItem("Palio");
// => 2
GLOBALCAR.addItem("Corsa");
// => 3

E o método getQueue para retornar os itens que estão na fila:

1
2
GLOBALCAR.getQueue();
// => "Gol - Palio - Corsa"

Legal né? Mas ai se você souber um pouquinho mais de JavaScript vai se perguntar porque é possível acessar a propriedade queue diretamente!? Isso tem tudo a ver com a forma que retornamos o objeto box. Se você quiser esconder essa propriedade, deixando disponível apenas os métodos você pode fazer isso:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
var GLOBALCAR = (function() {
    console.log("less is more!");

    // criando o objeto (vazio) box
    var box = {};

    // adicionando a propriedade queue (fila)
    box.queue = [];

    // adicionando o métodos addItem (adicionar item)
    box.addItem = function(car) {
        return box.queue.push(car);
    };

    // adicionando o métodos getQueue (recuperar fila)
    box.getQueue = function() {
        return box.queue.join(" - ");
    };

    // retornando um objeto personalizado (só com o necessário)
    return {
        add: box.addItem,
        get: box.getQueue
    };
})();

Agora um novo teste no console revelará:

1
2
GLOBALCAR;
// => Object {add: function, get: function}

Note que ninguém, a não ser você saberá da existência da propriedade queue. O mesmo vale para outros métodos que você queira implementar, mais que não fazem sentido serem expostos.

Com base na nova implementação, o método addItem virou apenas add, e o getQueue virou um simples get. Para adicionar itens a nossa fila, usaremos então:

1
2
3
4
5
6
GLOBALCAR.add("Gol");
// => 1
GLOBALCAR.add("Palio");
// => 2
GLOBALCAR.add("Corsa");
// => 3

E para retornar a fila:

1
2
GLOBALCAR.get();
// => "Gol - Palio - Corsa"

Elegante não? Agora você já tem uma base sólida para começar a organizar os seus projetos em JavaScript. O mais bacana, é que você pode entender a timeline do processo para chegarmos nesse objetivo. Aproveite o momento de satisfação, e divulge para a galera que agora você aprendeu a usar um pattern para organização do seu código, conhecido por aí como "Module Pattern". Chique não?

Este artigo foi originalmente publicado no site http://blog.caelum.com.br/organize-seu-codigo-javascript-de-maneira-facil/



Bye bye Grunt.js, hello Gulp.js!

Published:

No ano passado, um enxurrada de projetos baseados em JavaScript apareceu no mercado. Grande parte disto deve-se ao fato da linguagem não estar restritamente ligada apenas ao ambiente dos navegadores, e sem dúvidas a plataforma do Node.js contribuiu demais para a expansão de todo esse ecossistema.

No balanço da onda, surgiram alternativas para ferramentas tradicionais de build (Maven, Ant e afins) e entre estas a que mais se destacou foi sem dúvidas o Grunt.js - e você pode acompanhar o beabá desse cara num excelente post publicado pelo Sérgio Lopes no blog da Caelum.

O Grunt.js é uma ferramenta fantástica, mais requer do desenvolvedor alguns conhecimentos que nem todo mundo está verdadeiramente por dentro, como por exemplo o perfeito entendimento da sintaxe literal para construção de objetos que é a base da organização do Grunt.

A idéia do Gulp.js é abstrair esse conhecimento e trabalhar de uma forma muito mais digamos "jQuery like", onde você vai precisar conhecer meia dúzia de métodos e ele por trás faz a abstração dos detalhes necessários para a geração do build.

Começando com o Gulp.js

Assim como o Grunt, o Gulp é uma ferramenta de automação de tarefas feita em JavaScript e rodando em cima do Node.js. Como o core da execução é o Node, precisamos começar nossos trabalhos definindo o arquivo de vai gerenciar os módulos Gulp que você utilizará no seu projeto. Para isto, você deve criar na raiz do mesmo o arquivo package.json.

A forma mais fácil de fazer isso, é utilizando a seguinte instrução no terminal:

1
$ npm init

Esse comando questionará o desenvolvedor sobre uma série de detalhes do projeto (nome, versão, descrição, repositório no git, palavras chaves, autor, licença, etc). Você pode fornecer os detalhes ou ir pressionando Enter e deixar que gerenciador faça as definições baseado em algumas sugestões para as chaves indicadas. No final, o gerenciador exibirá uma amostra do JSON que foi definido e solicitará a sua confirmação para gravação do package.json:

1
2
3
4
5
6
7
8
9
10
11
{
  "name": "gulpteste",
  "version": "0.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}

Ai basta digitar um "yes" e ser feliz!

Instalando os módulos

Essa parte é a mais fácil. Baseada nas tarefas que você gostaria de automatizar, será necessário instalar os módulos do Gulp. No nosso exemplo, faremos um procedimento simples, de minificação de arquivos em JavaScript utilizando o Uglify. Então além dos módulos que farão o gulp funcionar (gulp e gulp-util), você precisará instalar o módulo da minificação (gulp-uglify) e de quebra instalaremos também um módulo para monitorar alterações nos arquivos .js e rodará a tarefa da minificação (gulp-watch)

1
2
3
4
$ npm install gulp --save-dev
$ npm install gulp-util --save-dev
$ npm install gulp-uglify --save-dev
$ npm install gulp-watch --save-dev

Note que para cada módulo instalado, utilizamos o parâmetro --save-dev. Isto modificará nosso arquivo package.json adicionando ao mesmo os módulos que a partir de agora são dependências para rodarmos as tarefas do Gulp. Se você for curioso, pode expiar o conteúdo do package.json, ele deve estar assim:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
  "name": "gulpteste",
  "version": "0.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "gulp": "~3.3.0",
    "gulp-util": "~2.2.9",
    "gulp-uglify": "~0.1.0",
    "gulp-watch": "~0.3.3"
  }
}

A grande vantagem de instalar os módulos desta maneira, é a fácil reinstalação de todas as dependências. Imagine que inadvertidamente alguém apagou a pasta node_modules do seu projeto. Ou mesmo, imagine que você gostaria de compartilhar este projeto com outro desenvolvedor. Não há a mínima necessidade de versionar a pasta node_modules. Basta ter o package.json atualizado e uma única instrução fará que com que tudo volte ao normal. A título de teste, exclua agora sua pasta node_modules... da até uma tristeza né, afinal você terá que reinstalar tudo novamente. A boa notícia é que agora tudo será bem mais facil, bastando rodar um:

1
$ npm install

Pronto! Tudo voltou a ordem que estava anteriormente.

Configurando suas tarefas

Para rodar o Gulp.js, você vai precisar configurar as tarefas que serão executadas. A título de exemplo, vamos criar 2 pastas, uma chamada src e outra chamada build, na raiz do projeto.

Dentro da pasta src, vamos criar uma pasta js onde ficarão todos os arquivos originais de desenvolvimento.

Com a automatização, será criada automaticamente uma pasta js dentro de build, com os arquivos js que estão na pasta src, porém estes últimos estarão minificados pelo Uglify. Para essa mágica toda acontecer, vamos criar na raiz o arquivo de configuração das tarefas, o gulpfile.js.

Abra o arquivo e mãos a obra. O primeiro passo é instanciar os módulos que vamos utilizar:

1
2
3
4
5
// instanciando módulos
var gulp = require('gulp');
var gutil = require('gulp-util');
var uglify = require('gulp-uglify');
var watch = require('gulp-watch');

A primeira tarefa que vamos definir, é que vai minificar os arquivos da pasta src/js para pasta build/js. Para isto, utilizaremos o método gulp.task(), que recebe 2 parâmetros. O primeiro é o nome da nova tarefa e o segundo uma função callback com os steps que devem ser executados a partir desta:

1
2
3
gulp.task('scripts', function() {
    // corpo da tarefa
});

Para configurar a minificação, precisamos definir 3 ações:

  1. recuperar os arquivos fonte na pasta de origem;
  2. aplicar a minificação (uglify);
  3. colocar o resultado na pasta de destino.

O código ficará assim:

1
2
3
4
5
6
7
gulp.task('scripts', function() {
    // corpo da tarefa 
    return gulp
            .src(['src/js/**/*.js'])
            .pipe(uglify())
            .pipe(gulp.dest('build/js'));      
});

Pronto! Para rodá-la, podemos ir ao terminal e digitar:

1
$ gulp scripts

E conferir se os arquivos foram minificados em build/js.

Para deixar a coisa mais legal, vamos configurar a tarefa que vai monitorar alterações na pasta src/js e rodar a tarefa 'scripts', responsável pela minificação. Começamos da mesma forma:

1
2
3
gulp.task('watch', function() {
   // corpo da tarefa 
});

Dentro da nova task, vamos utilizar o método gulp.watch() que recebe 2 parâmetros. No primeiro, definimos a pasta onde estão os nossos arquivos fonte e um função callback que será executada toda vez que um desses arquivos for modificado:

1
2
3
4
gulp.task('watch', function() {
    // corpo da tarefa 
    gulp.watch('src/js/**/*.js', function(event) {});
});

Agora no callback, o método gutil.log() exibirá informações do arquivo alterado, e através do método gulp.run() definimos qual tarefa será executada. Isso acontecerá toda vez que um arquivo presente em src/js for alterado:

1
2
3
4
5
6
7
gulp.task('watch', function() {
    // corpo da tarefa 
    gulp.watch('src/js/**/*.js', function(event) {
        gutil.log('File '+event.path+' was '+event.type+', running tasks...');
        gulp.run('scripts');
    });
});

Vamos conferir, no terminal execute:

1
$ gulp watch

Ai basta alterar qualquer arquivo .js da pasta fonte e ver o resultado na pasta build/js.

Indo além

Agora que temos uma boa noção como tudo funciona, basta repetir o ciclo. Para cada nova tarefa que for automatizar, siga estes passos:

  1. instale o novo módulo usando npm install;
  2. instancie o novo módulo no gulpfile.js;
  3. defina a nova task e o que a mesma executará;
  4. teste e divirta-se!

Conheça mais sobre o projeto, visitando o site oficial. Dê uma olhada no link plugins, lá você vai descobrir uma série de módulos para utilizar com o Gulp.js

Se você quiser saber mais sobre o processo de automatização de tarefas de front-end (utilizando Grunt.js), não deixe deixe de conhecer o treinamento de Programação JavaScript da Caelum . Te vejo por lá!

Este artigo foi originalmente publicado no site http://blog.caelum.com.br/bye-bye-grunt-js-hello-gulp-js/