Nodejs (2) Capitulo

Fazer download em pdf ou txt
Fazer download em pdf ou txt
Você está na página 1de 33

William Bruno Moraes

Novatec
© Novatec Editora Ltda. 2015.

Todos os direitos reservados e protegidos pela Lei 9.610 de 19/02/1998. É proibida a reprodução desta
obra, mesmo parcial, por qualquer processo, sem prévia autorização, por escrito, do autor e da Editora.

Editor: Rubens Prates


Assistente editorial: Priscila A. Yoshimatsu
Revisão gramatical: Marta Almeida de Sá
Capa: Carolina Kuwabata
Editoração eletrônica: Carolina Kuwabata

ISBN: 978-85-7522-456-4

Histórico de impressões:
Outubro/2015 Primeira edição

Novatec Editora Ltda.


Rua Luís Antônio dos Santos 110
02460-000 – São Paulo, SP – Brasil
Tel.: +55 11 2959-6529
Email: [email protected]
Site: www.novatec.com.br
Twitter: twitter.com/novateceditora
Facebook: facebook.com/novatec
LinkedIn: linkedin.com/in/novatec
capítulo 1

Introdução

Foi em 2009 que Ryan Dahl apresentou o NodeJS na JSConf Europa. O


NodeJS (https://nodejs.org/) é uma poderosa plataforma para aplicações,
para construir fácil e rapidamente aplicações de rede escaláveis, e que
utiliza a engine JavaScript open source de alta performance do navega-
dor Google Chrome, o motor V8 (https://developers.google.com/v8/), que é
escrita em C++.
Diferente da arquitetura do PHP (http://php.net) em que temos o PHP como
linguagem server-side e o Apache como servidor web, com NodeJS nós
escrevemos o nosso servidor web e também programamos a aplicação
server-side, tudo em JavaScript.
O NodeJS utiliza uma arquitetura orientada a eventos e um modelo I/O
não bloqueante que faz com que seja leve e eficiente. Essas são caracte-
rísticas perfeitas para solucionar os problemas de intenso tráfego de rede
e aplicações em tempo real, que são frequentemente os maiores desafios
das aplicações web hoje em dia.
Por ter sido projetado para construção de aplicações de rede, é possível
desenvolvermos para qualquer protocolo: DNS, FTP, HTTP, HTTPS, SSH,
TCP, UDP ou WebSockets. Neste livro abordaremos o protocolo HTTP.
Vemos o NodeJS ser frequentemente utilizado para construir aplicações
web, mas as suas aplicações vão além, como escrever ferramentas de linha
de comando, e utilizando o projeto NW.js (https://github.com/nwjs/nw.js),
antigamente chamado de Node Webkit, podemos também desenvolver
aplicativos nativos para o sistema operacional, compilando um mesmo
código-fonte para Windows, Linux ou OS X.

15
16 Construindo Aplicações com NodeJS

1.1 Instalação e configuração do Node


Siga as instruções referentes ao seu sistema operacional.

• Todas
http://nodebr.com/instalando-node-js-atraves-do-gerenciador-de-pacotes/
• Mac OS X
http://udgwebdev.com/node-js-para-leigos-instalacao-e-configuracao/

Se você estiver utilizando Windows, lembre-se de reiniciar a máquina


após a instalação.

Verifique a versão do NodeJS instalada com o comando a seguir no seu


terminal:
$ node --version && npm --version

v4.0.0
2.14.2

1.2 Programação síncrona e assíncrona


Um procedimento síncrono é aquele em que uma operação ocorre após o
término de outra. Imagine um procedimento que demore dois segundos
para ser executado. A linha de código após esse procedimento não será
executada até que o procedimento da linha anterior tenha terminado de
executar. Esse é o comportamento previsível ao qual já estamos acos-
tumados quando trabalhamos com outras linguagens de programação.

 Arquivo sincrono.js
console.log('1');
t();
console.log('3');
function t() {
console.log('2');
}
Capítulo 1 ■ Introdução 17

A saída no terminal será a ordem natural.


$ node sincrono.js
1
2
3

Um procedimento assíncrono não bloqueia a execução do código. Se um


procedimento leva certo tempo para ser encerrado, a linha após esse pro-
cedimento será executada antes de o procedimento assíncrono terminar, e
isso geralmente é uma das maiores dificuldades na hora de programadores
de outras linguagens tentarem escrever JavaScript.

 Arquivo assincrono.js
console.log('1');
t();
console.log('3');
function t() {
setTimeout(function() {
console.log('2');
}, 10);
}

A saída será 1, 3 e 2, pois a linha abaixo da invocação do console.log('3')


não irá aguardar a execução da função t().
$ node assincrono.js
1
3
2

No NodeJS, é preferível que programemos nossas rotinas de forma assín-


crona, para não bloquear a main thread. É por isso que todas as funções
e os métodos, que fazem algum tipo de acesso a disco ou rede, têm como
parâmetro uma função de callback.
18 Construindo Aplicações com NodeJS

1.3 Orientação a eventos


Basicamente, eventos são avisos de que algo aconteceu. Dentro do browser,
existem vários eventos, como o clique em um elemento, o envio de um
formulário, o pressionar de uma tecla, o carregar de um documento etc.
No NodeJS, os eventos podem ser: um erro ao conectar em um banco de
dados, o recebimento parcial de um conteúdo em um stream, um aviso
de que tudo já foi recebido etc.
Extrapolando em abstração, quero que você pense em eventos como
sendo uma forma do JavaScript de implementar nativamente o design
pattern Observer, que diz que um objeto (Subject) contém uma lista de
dependências (Observers) e os notifica automaticamente quando alguma
mudança de estado ocorre. Quando um usuário clica em um link dentro
de uma página web em um browser, o navegador é o subject e o nosso
script que estava escutando esse evento é o observer.
Além de utilizar os eventos do ambiente e dos módulos que importar-
mos, podemos registrar nossos próprios eventos, desacoplando assim o
nosso código.

1.4 Orientação a objetos


A linguagem JavaScript possui suporte à orientação a objetos. Entenda
que orientação a objetos é muito diferente de orientação a classes, que é
o que a maioria dos programadores faz por aí. O fato de a linguagem não
conter classes (até a ECMA5, pois na ECMA6 foi introduzido o operador
class, que funcionará apenas como um syntatic sugar1) não significa que
não implemente ou não seja possível aplicar os conceitos e as boas práticas
de OO que já conhecemos. Muito pelo contrário, já que alguns patterns,
por exemplo, são até mais facilmente implementados em JavaScript do
que em outras linguagens que contêm a famigerada herança clássica.
A herança no JavaScript é baseada em prototype, e os objetos podem
tanto ser feitos a partir de uma função construtora quanto a partir de
um JSON. É importante saber trabalhar corretamente com os objetos

1 syntactic sugar é uma sintaxe alternativa da linguagem de programação que torna mais
concisa uma declaração.
Capítulo 1 ■ Introdução 19

do JavaScript; vale a pena estudar as diferenças com leituras comple-


mentares como o livro Princípios de Orientação a Objetos em JavaScript
(http://www.novatec.com.br/livros/orientacaoobjetosjavascript/).
Existem apenas seis tipos primitivos no JavaScript: String, Number, Boolean,
null, undefined e Symbol. Porém, exceto por null e undefined, os outros tipos pri-
mitivos podem ser transformados em objetos implícita ou explicitamente,
conforme o contexto e a forma de uso.
Tire um tempinho e leia este texto do Douglas Crockford: JavaScript:
A Menos Entendida Linguagem de Programação do Globo! (http://javascript.
crockford.com/pt/javascript.html).

1.5 Programação funcional


JavaScript, além de ser uma linguagem de processamento assíncrono,
orientada a eventos, possui suporte à orientação a objetos e também é
uma linguagem que implementa programação funcional. Está bom ou
quer mais?
O paradigma de programação funcional trata a computação como uma
avaliação de funções matemáticas, permitindo um código mais legível,
conciso e evitando efeitos colaterais. Funções são objetos de primeira clas-
se e podem ser retornadas, injetadas como argumentos ou até atribuídas
a variáveis. Uma linguagem funcional segue alguns preceitos:

• Estruturas de controle e operações abstraídas como funções.


• Funções podem abstrair outras funções.
• Variáveis e estados imutáveis, para evitar efeitos colaterais (side
effects).
• Funções retornam valores.
• Funções são objetos de primeira importância, ou seja, podem ser
parâmetros, valores ou retornos de outras funções.

Para melhor entender o paradigma funcional, veja alguns conceitos im-


portantes:
20 Construindo Aplicações com NodeJS

Closures
O escopo do JavaScript é baseado em funções. O fato de uma função
interna acessar variáveis e parâmetros de um escopo acima do seu
próprio é chamado de closure. Utilizamos closures em JavaScript para
proteger o escopo simulando o que é conhecido como membro privado.

Callback
Um callback é uma função passada como parâmetro para outra função,
para ser executada mais tarde, quando algum processo acabar.

Currying
Esta é uma técnica que consiste em transformar uma função de n
argumentos em outra com menos ou com argumentos mais simples.

Monads
Um design pattern que descreve computação como uma série de etapas.
Trabalha como composição de funções.

Memoization
Memoization (http://addyosmani.com/blog/faster-javascript-memoization/)
é um pattern que serve basicamente para cachear valores já retornados,
fazendo com que a resposta seja mais rápida. Dentre os problemas
que esse pattern resolve, podemos citar cálculos matemáticos recur-
sivos, cache de algum algoritmo ou qualquer problema que possa ser
expressado como chamadas consecutivas a uma mesma função com
uma combinação de argumentos.

Métodos forEach, map, reduce, filter, every, some, sort


São métodos do objeto Array do JavaScript muito úteis que lhe permi-
tem escrever loops de uma maneira mais explícita, concisa e objetiva.

1.6 NPM (Node Package Manager)


O npm ( https://www.npmjs.com ) é um serviço (grátis para pacotes
públicos) que possibilita aos desenvolvedores criar e comparti-
lhar módulos com outros desenvolvedores, dessa forma facilitando
Capítulo 1 ■ Introdução 21

a distribuição de códigos e ferramentas escritas em JavaScript


(https://docs.npmjs.com/getting-started/what-is-npm).
Por exemplo, se você quiser utilizar o ExpressJS (http://expressjs.com),
que é um framework rápido e minimalista para construção de rotas em
NodeJS, não é necessário entrar no GitHub do projeto, fazer download
do zip ou clonar o repositório para depois instalar em um diretório da
sua máquina. Basta digitar no seu terminal:
$ npm install express

Com esse comando, o módulo será baixado para uma pasta chamada
node_modules no mesmo diretório em que você digitou o comando.
Você pode criar uma conta no site www.npmjs.com, caso pretenda publi-
car algum módulo no diretório. É possível também configurar na sua
máquina local os seus dados no npm (https://docs.npmjs.com/cli/config).
Se você estiver em um Linux ou MacOSx, existirá um arquivo .npmrc na
raiz do seu usuário:
$ cat ~/.npmrc
init.author.name=William Bruno
[email protected]
init.author.url=http://wbruno.com.br

No Windows, o arquivo npmrc estará localizado em outro diretório.


Dentro da pasta npm da instalação do NodeJS em C:\Program Files\nodejs\
node_modules\npm\npmmrc ou em C:\Users\<user>\AppData\Roaming\npm.

1.6.1 Lado bom


O lado “bom” do npm é que existem muitos módulos à disposição. En-
tão, sempre que você tiver que fazer alguma coisa em NodeJS, pesquise
no https://www.npmjs.com para saber se já existe algum módulo que faça
o que você quer, ou uma parte do que você precisa, agilizando, assim, o
desenvolvimento da sua aplicação.
22 Construindo Aplicações com NodeJS

1.6.2 Lado ruim


O lado “ruim” do npm é que existem muitos módulos à disposição! Opa,
mas não acabei de falar que esse era o lado bom? Pois é, acontece que, por
ser completamente público e colaborativo (open source), você encontrará
diversos módulos com o mesmo propósito ou que realizam as mesmas
tarefas. Cabe a nós escolhermos aquele que melhor nos atenda. Para isso,
é importante seguir alguns passos:

• Procure um módulo que esteja sendo mantido (com atualizações


frequentes, olhe a data do último commit).
• Verifique se outros desenvolvedores estão utilizando o módulo
(olhe o número de estrelas, forks, issues abertas e fechadas etc.).
• É importante que ele contenha uma boa documentação dos mé-
todos públicos e da forma de uso, assim você não precisará ler o
código-fonte do projeto para realizar uma tarefa simples. Ler o
código-fonte pode ser interessante quando você tiver um tempo
para isso ou precisar de alguma otimização de baixo nível.
• Procure testes de performance em que são comparados módulos
alternativos.

Assim você foca a sua aplicação e o desenvolvimento da regra de negócio.

1.7 Fork io.js


No momento em que escrevia este livro, o fork chamado io.js
(https://iojs.org/en/index.html) foi incorporado dentro de um repositório
oficial do NodeJS, sob governança do Node.js Foundation. O io.js, que
nasceu em janeiro de 2015, baseado no NodeJS, foi criado com a intenção
de lançar mais versões com mais frequência, assim como se manter mais
atualizado com as novas especificações da ECMA e consequentemente
com a última versão do motor V8.
Capítulo 1 ■ Introdução 23

O movimento para a unificação dos dois projetos (o NodeJS que estava


sob governança da Joyent e o io.js) teve início em 15 de março de 2015 e
foi feito em um novo repositório (https://github.com/nodejs/node). Por um
tempo, o NodeJS e o io.js continuaram a coexistir e a lançar novas versões
até que o Node Foundation tivesse terminado de realizar a unificação
dos dois projetos.
Hoje em dia, temos o NodeJS na versão 4.0 para que não conflitasse com
as versões antigas do io.js.

1.8 Gerenciando instalações do NodeJS ou do io.js


Após instalar o NodeJS pela primeira vez, você pode atualizar para
novas versões, reinstalar uma versão antiga ou alternar entre NodeJS e
io.js com ajuda de módulos disponíveis no npm. Existem dois módulos
que fazem esse trabalho, o n (https://www.npmjs.com/package/n) e o nvm
(https://www.npmjs.com/package/nvm).
Você instala um deles via npm e depois controla a versão do NodeJS que
quer utilizar.
$ npm install n -g

ou
$ npm install nvm -g

A flag -g indica que estamos instalando esse módulo globalmente. Com


isso, ele estará disponível para executarmos diretamente via linha de
comando a partir de qualquer diretório na máquina. Alguns comandos
disponíveis para instalar versões são:
$ n stable # instala a versão estável disponível
$ n latest # instala a última versão disponível (beta)
$ n 0.12.7 # instala exatamente a versão especificada

E com o comando n conseguimos alternar entre as versões instaladas.


24 Construindo Aplicações com NodeJS

1.9 Variáveis de ambiente


Variáveis de ambiente são usadas para fornecer informações adicionais ao
nossos scripts, como o ambiente em que o sistema está rodando, algum
dado de configuração do banco de dados ou o caminho até o arquivo de
log, por exemplo. Pertencem ao sistema operacional que estamos utilizan-
do e ficam armazenadas em um espaço da memória RAM da máquina.
Se você estiver trabalhando em Unix (OS X ou Linux), use o seguinte
comando para criar ou alterar o valor de uma variável de ambiente:
$ export NODE_ENV=development

Mas, se você estiver trabalhando em uma máquina Windows, use set no


lugar de export:
$ set NODE_ENV=development

Para verificar o valor de uma variável de ambiente, podemos imprimi-la,


colocando o símbolo $ na frente do nome da variável:
$ echo $NODE_ENV
development

1.10 Console do NodeJS (REPL)


O NodeJS tem uma forma de acessarmos as propriedades e funções utili-
zando o terminal do sistema operacional sem que seja necessário escrever
e salvar códigos em um arquivo. Isso é muito útil para testarmos pequenos
trechos de código e entendermos como as coisas funcionam. Este é o REPL
(https://nodejs.org/api/repl.html), também chamado de console.
Apesar de ser possível utilizar o próprio prompt cmd do Windows, existe
um alternativa chamada Git Bash (https://git-scm.com/downloads), que lhe
dará uma experiência mais próxima do terminal de sistemas com base Unix.
Caso você esteja em um OSx ou com alguma distribuição Linux, o
terminal-padrão já oferece todas as ferramentas necessárias.
Para entrar no console do NodeJS, basta digitar node no terminal.
$ node
Capítulo 1 ■ Introdução 25

O console aceita qualquer expressão JavaScript válida, como:


> 4 + 2;
6

Na próxima linha, será mostrado o resultado ou o retorno da expressão


que escrevermos. Podemos, por exemplo, visualizar as variáveis de am-
biente que definimos com export, ao acessar process.env.NODE_ENV.

Sempre que eu iniciar uma linha de exemplo com >, entenda que estamos
dentro do terminal do NodeJS.

> process.version;
'v4.0.0'
> process.versions.v8;
'4.5.103.30'
> process.env.NODE_ENV;
'development'

O objeto process é um objeto global do NodeJS que podemos utilizar,


inclusive em nossos scripts.
Imagine uma situação em que você precise testar uma expressão regular
e não queira criar um arquivo só para isso. Você pode utilizar o console
do NodeJS direto no terminal:
$ node
> /rato/g.test('O rato roeu a roupa');
true

Comecei simples, como qualquer boa regex. O padrão /rato/ casou com
o texto 'O rato roeu a roupa'. E se mudássemos de rato para gato, teríamos
um false:
> /rato/g.test('O gato roeu a roupa');
false

Se quisermos que a regex case tanto a frase 'O rato roeu a roupa' quanto a
frase 'O gato comeu a roupa', basta alterarmos para:
26 Construindo Aplicações com NodeJS

> /(g|r)ato (ro|com)eu/g.test('O gato comeu a roupa');


true
> /(g|r)ato (ro|com)eu/g.test('O rato roeu a roupa');
true

Poderíamos testar alguns scripts para somar os números de um array:


> var arr = [1,2,3,4,5,6], sum = 0;
undefined
> for(var i=0, max=arr.length; i<arr; i++) { sum += arr[i]; }
21
> sum
21

Não ficou legal. Que tal utilizarmos o .forEach()?


> var arr = [1,2,3,4,5,6], sum = 0;
undefined
> arr.forEach(function(n){ sum += n; });
undefined
> sum
21

Ou a função reduce()?
> var arr = [1,2,3,4,5,6], sum = 0;
undefined
> sum = arr.reduce(function(prev, curr){ return prev + curr; });
21

Após o var arr =… aparece um undefined porque essa linha não tem nenhum
retorno ou saída.
Para escrevermos na saída do console, utilizamos a função console.log():
> console.log('Darth Vader');
Darth Vader
undefined
Capítulo 1 ■ Introdução 27

A primeira linha após a chamada da função console.log() com a mesma


string que passamos como argumento é a saída, e a linha após esta com
o undefined é o retorno.
É bem parecido com o console dos navegadores – quem é frontend vai
entender isso –, onde também podemos fazer todas essas coisas. Veja que
estamos no NodeJS, então os objetos globais do navegador não existem:
> window
ReferenceError: window is not defined
at repl:1:1
at REPLServer.defaultEval (repl.js:132:27)
at bound (domain.js:254:14)
at REPLServer.runBound [as eval] (domain.js:267:12)
at REPLServer.<anonymous> (repl.js:279:12)
at REPLServer.emit (events.js:107:17)
at REPLServer.Interface._onLine (readline.js:214:10)
at REPLServer.Interface._line (readline.js:553:8)
at REPLServer.Interface._ttyWrite (readline.js:830:14)
at ReadStream.onkeypress (readline.js:109:10)

Retornou um ReferenceError quando tentei acessar um objeto chamado


window, que é o root, por assim dizer, do JavaScript quando ele roda dentro
de um navegador. Qualquer variável, função ou objeto que criarmos no
escopo global será descendente de window. No NodeJS, o objeto root se
chama global.
> global.process.env.NODE_ENV
'development'

E, da mesma forma que podemos ocultar window quando invocarmos


algum objeto do escopo global, também podemos ocultar o global no
NodeJS. Caso você queira ver tudo o que tem no objeto global, basta
digitar global no console:
> global
{ DTRACE_NET_SERVER_CONNECTION: [Function],
DTRACE_NET_STREAM_END: [Function],
DTRACE_NET_SOCKET_READ: [Function],
28 Construindo Aplicações com NodeJS


global: [Circular],
process:

addListener: [Function],
on: [Function],
removeListener: [Function] }

1.11 Node Inspector


O projeto Node Inspector (https://github.com/node-inspector/node-inspector)
permite debugar nossos códigos NodeJS, de forma bem similar ao que
fazemos com outras linguagens e com JavaScript client-side, colocando
breakpoints, inspecionando valores de variáveis e objetos.
Não que seja totalmente necessário, pois um projeto com uma boa cober-
tura de testes deve eliminar a necessidade de um debug nesse nível, mas
vale a pena mencionar, então, está aí. Não use como muleta.

1.12 Tenha um bom editor de código


Para trabalhar com NodeJS você pode utilizar qualquer editor que goste
e com o qual já esteja acostumado, seja o VIM (VI Improved), Sublime
Text, Notepad++, IntellijIDEA etc.
Irei no entanto citar três dicas bem interessantes aqui.

1.12.1 Arquivo de preferências do Sublime Text 3


O Sublime Text (https://www.sublimetext.com) é um ótimo editor de códigos
que tem sido muito utilizado nos últimos anos por desenvolvedores web,
e é uma ótima opção para você programar NodeJS. Será utilizado para
escrever os códigos de exemplo deste livro.
Por meio do menu Preferences > Settings – User, você personaliza o Sublime
(Figura 1.1).
Capítulo 1 ■ Introdução 29

Figura 1.1 – Menu Preferences > Settings – User. Tela do Sublime Text.

Veja a seguir o meu arquivo pessoal.

 Arquivo Preferentes.sublime-settings
{
"color_scheme": "Packages/Color Scheme - Default/Monokai.tmTheme",
"ensure_newline_at_eof_on_save": true,
"file_exclude_patterns":
[
".DS_Store",
"*.min.*"
],
"folder_exclude_patterns":
[
".git",
".tmp",
"coverage",
"node_modules",
"vendor"
],
"font_size": 13,
"highlight_modified_tabs": true,
"ignored_packages":
30 Construindo Aplicações com NodeJS

[
"Vintage"
],
"open_files_in_new_window": false,
"save_on_focus_lost": true,
"scroll_speed": 0,
"side_bar_width": 210,
"smart_indent": true,
"tab_size": 2,
"translate_tabs_to_spaces": true,
"trim_trailing_white_space_on_save": true,
"word_wrap": true
}

Ele contém algumas configurações bacanas, como:

• Inserir uma nova linha ao final do arquivo.


• Ignorar arquivos temporários do OS X e *.min.* na listagem e na busca.
• Ignorar diretórios que não precisamos ver enquanto estamos pro-
gramando.
• Desabilitar o Modo VIM, que permite utilizar o Sublime Text como
se fosse o VIM.
• Salvar arquivos ao perder o foco.
• Indentar com dois espaços.
• Remover espaços desnecessários ao salvar.
• E quebrar linhas para se ajustarem à área visível do editor.

1.12.2 EditorConfig.org
O EditorConfig.org (http://editorconfig.org) é um projeto open source que
ajuda equipes de desenvolvedores que utilizam diferentes IDEs e editores
de códigos a manter um estilo consistente no projeto, como por exemplo
utilizar dois espaços para indentar, não permitir espaços desnecessários,
inserir uma nova linha ao final do arquivo etc.
Capítulo 1 ■ Introdução 31

O EditorConfig contém plugins para diversos editores como: Atom,


Emacs, IntellijIDEA, NetBeans, Notepad++, Sublime Text, Vim, Visual
Studio e WebStorm.
Basta ter um arquivo .editorconfig na raiz do projeto e cada desenvolvedor
instalar o plugin correspondente do editorconfig para o seu editor ou IDE.
Vou dar uma sugestão de .editorconfig para você utilizar com a sua equipe:

 Arquivo .editorconfig
# EditorConfig is awesome: http://EditorConfig.org
root = true

[*]
indent_style = space
indent_size = 2

end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

[*.md]
trim_trailing_whitespace = false

Se houver o arquivo .editorconfig na raiz do projeto, as configurações


pessoais do editor serão sobrescritas e naquele projeto o editor usará as
configurações definidas neste arquivo.

1.12.3 Opções de IDE (Integrated Development Enviroment)


IDEs são poderosos editores de código com diversas funcionalidades,
como auto complete, atalhos para objetos, funções ou arquivos, debug
integrado etc.
Não utilizo IDEs, mas sim um editor simples, o Sublime Text. Caso você
queira utilizar uma IDE, duas opções são:

• Visual Code – https://www.visualstudio.com/en-us/products/code-vs.aspx


• WebStorm – https://www.jetbrains.com/webstorm/
32 Construindo Aplicações com NodeJS

Ambos têm um suporte bacana à linguagem JavaScript e algumas features


legais que são habilitadas por meio de pequenas configurações, por isso
são boas opções para programar NodeJS.

1.13 Plugin para visualização de JSON


Os navegadores atuais ainda não sabem como renderizar o conteúdo de um
JSON, então, para facilitar a visualização, seja no Firefox ou no Chrome, po-
demos instalar plugins que formatem o texto de forma identada e colorida.
Basta pesquisar por JSON View na loja de aplicativos do Chrome (Chrome
Web Store) ou do Firefox (Complementos).

1.14 JavaScript básico


JavaScript é uma linguagem de programação de alto nível, dinâmica,
não tipada e interpretada. Originalmente desenvolvida pelo Mestre Jedi
Brendan Eich. O JavaScript completou 20 anos em 2015 e tem sido uma
das linguagens mais utilizadas no mundo desde então, o que impulsionou
o seu crescimento e amadurecimento principalmente nos últimos anos.
A sintaxe do JavaScript foi baseada na linguagem C, enquanto a semântica
e o design vieram da Self e da Scheme.

1.14.1 Variáveis
A linguagem JavaScript apresenta seis tipos de dados primitivos:

• Boolean – True ou false.


• Number – Um número, tanto inteiro quanto float são Number.
• String – Caracteres de texto.
• Symbol – Tipo de dado em que as instâncias são únicas e imutáveis.
• null – Palavra-chave especial para um valor nulo.
• undefined – Indica um valor não definido.
• Object, Function, Array, Date e RegExp.
Capítulo 1 ■ Introdução 33

Destes, apenas null e undefined não têm métodos; todos os outros podem
ser utilizados como objetos.
Para declarar uma nova variável, basta indicar o nome após a palavra re-
servada var, let ou const e atribuir um valor com um símbolo de igualdade.
var creator_name = 'George Lucas';

• var – Declara uma variável com escopo de função.


• let – Declara uma variável com escopo de bloco.
• const – Declara uma constante, que é algo que não pode ter seu
valor alterado.

Essa forma de escopo do JavaScript de trabalhar permite o comporta-


mento conhecido por closure, por meio do qual as variáveis definidas em
um escopo acima também são acessíveis num escopo mais específico.
> (function(){
var arr = [];
function something(){ console.log(arr); }
arr.push(1);
arr.push(2);
something();
})();
[ 1, 2 ]

Nesse exemplo, a função something() teve acesso à variável arr, que foi de-
clarada um escopo acima do seu. E o let que tem escopo por bloco cria
novas referências para cada bloco:
> var out = 'May 25, 1977';
> let out2 = 'Jun 20, 1980';
> if(true) { var out = 'May 25, 1983'; let out2 = 'May 19, 1999'; }
> out;
'May 25, 1983'
> out2
'Jun 20, 1980'
34 Construindo Aplicações com NodeJS

O fato interessante a notar é que a variável out teve o seu valor alterado fora
do bloco do if, enquanto a out2 não. Ela permaneceu com o valor inicial
declarado fora do bloco, enquanto outro espaço de memória foi alocado
para a out2 de dentro do bloco do if. Desse comportamento temos que
let não faz hoisting, enquanto o var faz.

1.14.2 Comentários
A linguagem aceita comentários de linha e de bloco, que são instruções
ignoradas pelo interpretador, que têm como função destacar ou explicar
um trecho de código ao programador que tiver lendo o código-fonte.
> //comentário de linha

>
> /*
comentário de bloco
*/

1.14.3 Funções
Funções no JavaScript podem ser declaradas, atribuídas, passadas por
referência ou retornadas, por isso dizemos que elas são objetos (cidadãos)
de primeira classe.
Existem algumas formas diferentes de declarar uma função, dentre elas,
as duas a seguir são as mais comuns:
> var foo = function() {};
>
> function bar(){}

Ou atribuindo uma função sem nome a uma variável, ou declarando uma


função com um nome.
Tendo em vista a possibilidade de criar uma função sem nome e o escopo
baseado em funções da linguagem, podemos criar uma closure com uma
função anônima autoexecutável (IIFE – Immediately-Invoked Function
Expression).
Capítulo 1 ■ Introdução 35

> (function(){
var princess = 'Leia';
})();
> console.log(princess);
ReferenceError: princess is not defined

A variável princess não existe fora da IIFE, mas a IIFE pode acessar qual-
quer variável que tenha sido declarada fora dela.

Entendendo .bind(), .call() e .apply()


A função .bind() basicamente retorna uma função, alterando o escopo da
função-alvo para aquele que você passar como argumento.
Dada uma função StormtropperController, podemos passar como argumento
da função .bind() o próprio controller.
StormtropperController.getAll.bind(StormtropperController);

fazendo com que o this apontasse para ele mesmo.


Isso foi necessário porque, ao colocar uma função como callback, ela
assume o escopo em que foi invocada, e não mais o escopo original dela.
Com o .bind() nós corrigimos esse comportamento.
Digamos que eu tenha uma função assim:
> function sith() { console.log(this); }

Ao invocar:
> sith()
{ DTRACE_NET_SERVER_CONNECTION: [Function],
DTRACE_NET_STREAM_END: [Function],
DTRACE_NET_SOCKET_READ: [Function],
DTRACE_NET_SOCKET_WRITE: [Function],
DTRACE_HTTP_SERVER_REQUEST: [Function],
DTRACE_HTTP_SERVER_RESPONSE: [Function],
DTRACE_HTTP_CLIENT_REQUEST: [Function],
DTRACE_HTTP_CLIENT_RESPONSE: [Function],
global: [Circular],
36 Construindo Aplicações com NodeJS

process:
{ title: 'node',

_: [Circular] }

O this aponta para o objeto root do NodeJS que é o global. No browser


acontece o mesmo, mas o objeto root é window.
Com o .bind(), podemos alterar o escopo desta função:
> var lordSith = sith.bind({ name: 'Darth Bane' });
undefined
> lordSith()
{ name: 'Darth Bane' }
undefined

O this agora foi o objeto que passei como argumento da função .bind(). Pode-
ríamos enviar qualquer coisa como argumento (String, Number, Object etc.):
> var lordSith = sith.bind('Darth Bane'); //String
undefined
> lordSith()
[String: 'Darth Bane']
undefined
> var lords = sith.bind(19); //Number
undefined
> lords()
[Number: 19]
undefined

Fica claro que o .bind() retorna uma nova função com um novo escopo.
A função .call() não retorna. Ela executa a função no momento em que
for chamada:
> function sith() { console.log(this); }
undefined
> sith.call({ name: "Darth Maul" });
{ name: 'Darth Maul' }
undefined
Capítulo 1 ■ Introdução 37

Por isso que, conforme o contexto e o momento em que queremos que


algo seja executado, utilizamos o .bind() no lugar do .call(). A função
.apply() tem o mesmo comportamento do .call():

> sith.apply({ name: "Darth Vader" });


{ name: 'Darth Vader' }
undefined

A verdadeira diferença entre .call() e .apply() está no segundo argumento.


Enquanto a .call() recebe uma lista de argumentos que será repassada
como argumentos da função em que foi chamada:
> function sith(arg1, arg2, arg3, arg4) { console.log(this); console.
log(''+ arg1 + arg2 + arg3 + arg4); }
undefined
> sith.call({ name: "Darth Sidious" }, 1,1,3,8);
{ name: 'Darth Sidious' }
1138
undefined

a função .apply() recebe um array.


> function sith(arg1, arg2, arg3, arg4) { console.log(this); console.
log(''+ arg1 + arg2 + arg3 + arg4); }
undefined
> sith.apply({ name: "Darth Vectivus" }, [1,1,3,8]);
{ name: 'Darth Vectivus' }
1138
undefined

Arrow functions
Arrow function ou Fat Arrow function é uma sintaxe alternativa à decla-
ração das funções com a palavra reservada function, além de fazer a
mudança de contexto que o .bind() faz. Por exemplo, a seguinte função
para contar a quantidade de caracteres de cada item do array pode ser
escrita desta forma:
> var studios = ['20th Century Fox', 'Warner Bros.', 'Walt Disney Pictures'];
undefined
38 Construindo Aplicações com NodeJS

> studios.map(function(s) { return s.length; });


[ 16, 12, 20 ]

Ou utilizando arrow functions, assim:


> var studios = ['20th Century Fox', 'Warner Bros.', 'Walt Disney Pictures'];
undefined
> studios.map(s => s.length);
[ 16, 12, 20 ]

Utilizando o comando abaixo, a lista de flags e o estado de cada funcio-


nalidade serão listados.
$ nodejs --v8-options|grep "harmony"

1.14.4 Objetos
Podemos declarar novos objetos criados a partir de uma função constru-
tora ou objetos literais, declarados explicitamente.
> function Droid(){}
> var r2d2 = new Droid();
> r2d2
Droid {}

Ou então em formato de JSON, onde teremos sempre uma mesma ins-


tância, ou seja, um Singleton.
> var BattleDroid = {};
> BattleDroid
{}

Entendendo o prototype
Todos os objetos no JavaScript são descendentes de Object, e todos os ob-
jetos herdam métodos e propriedades de Object.prototype. Esses métodos
e essas propriedades podem ser sobrescritos. Dessa forma, conseguimos
simular o conceito de herança, além de outras características interessantes
do prototype.
Capítulo 1 ■ Introdução 39

Observe o objeto criado com a função construtora Droid.


> function Droid() {}
> var c3po = new Droid();
> Droid.prototype.getLanguages = function() { return this.languages; };
> Droid.prototype.setLanguages = function(n) { this.languages = n; };
> c3po.setLanguages(6000000);
> c3po.getLanguages();
6000000

Podemos setar métodos ou propriedades no prototype de Droid, e as


instâncias desse objeto herdarão essas propriedades mesmo que tenham
sido instanciadas antes de o método ter sido definido. Assim como as
novas instâncias também herdarão estes métodos:
> var r2d2 = new Droid();
> r2d2.setLanguages(1);
> r2d2.getLanguages();
1

E para usar o prototype para herdar de outros objetos, basta atribuir uma
instância do objeto-base no prototype do objeto onde queremos receber
os métodos e as propriedades:
> function BattleDroid() {}
> BattleDroid.prototype = Object.create(Droid.prototype);
> var b1 = new BattleDroid();
> b1.setLanguages(1);
> b1.getLanguages();
1

Entendendo o objeto literal


Cada vez que invocamos o operador new para uma função construtora,
obtemos uma nova instância de um objeto, ou seja, uma nova referência
de memória.
40 Construindo Aplicações com NodeJS

Mas, para objetos criados com a notação literal, teremos sempre a mesma
instância, a mesma referência de memória.
> var BattleDroid = {}
> BattleDroid.attack = function() {};
[Function]
> BattleDroid
{ attack: [Function] }

Para atribuir novos métodos, basta colocar o nome do objeto seguido de


um ponto e ao nome do método atribuir uma função anônima. A forma
de uso é estática:
> BattleDroid.attack();

1.14.5 Operações
Os operadores numéricos são +, -, *, / e %, para adição, subtração, multi-
plicação, divisão e resto, respectivamente. Os valores são atribuídos com
um operador de igualdade. O operador + também concatena strings, o
que é um problema, pois podemos tentar somar números com strings e
obter resultados esquisitos.
> 1 + 3 + '2'
'42'

Comparações são feitas com dois ou três sinais de igualdade. A diferença


é que == compara valores, fazendo coerção de tipo, podendo resultar que
42 em string seja igual a 42 number.
> '42' == 42
true

Entretanto, com três operadores de igualdade, o interpretador não converte


nenhum dos tipos e faz uma comparação que só responde true caso sejam
idênticos tanto o valor quanto o tipo.
> '42' === 42
false
Capítulo 1 ■ Introdução 41

Da mesma forma que o operador ponto de exclamação, ou negação (!),


inverte o valor, ele pode ser usado para comparar se uma coisa é diferente
da outra, comparando != ou !==. É recomendado que sempre se utilize
=== ou !==.

Dois operadores de negação convertem um valor para o seu booleano.


> !!''
false
> !!'a'
true

Podemos também combinar a atribuição com os operadores +, -, *, / e %.


Tornando possível resumir uma atribuição e alteração de valor numa
sintaxe mais curta.
> var one = 1;
> one = one + 1;
2
> var one = 1;
> one += 1;
2

Ou, então, com duplo ++ ou --, para incremento ou decremento.


> var one = 1;
> one++
1
> one = one++;
2

Ainda existem os operadores de bit &, |, ^, ~, <<, >> etc., mas não vou explicá-
-los profundamente aqui. É possível utilizá-los em conjunto, por exemplo,
utilizando dois operadores OU (||), podemos resumir a expressão deste
if ternário:
> var noTry = false ? 'do' : 'do not';
> noTry
'do not'
42 Construindo Aplicações com NodeJS

Em:
> var noTry = false || 'do not';
> noTry;
'do not'

1.14.6 Controles de fluxo


Já vimos de outras linguagens, if, else, switch/case. A sintaxe no JavaScript
é idêntica à da linguagem C.
> var noTry;
> if (false) {
noTry = 'do';
} else {
noTry = 'do not';
}
'do not'

Um bloco switch/case basicamente serve como uma cadeia de if, elseif, else.

1.14.7 Laços de repetição


Existem diversas opções – for, for in, for of, while, do while – e os métodos
de arrays forEach, map, filter etc. Apesar de ser praticamente possível escrever
qualquer tipo de loop com for ou while, com experiência você achará qual
deles é o mais adequado à situação.

 Loop for
> var arr = [1,2,3,5,7,11];
> for (var i = 0, max = arr.length; i < max; i++) {
console.log(arr[i]);
}
Capítulo 1 ■ Introdução 43

 Loop while
> var arr = [1,2,3,5,7,11];
> var i = 0;
> var max = arr.length;
> while(i < max) {
console.log(arr[i]);
i++;
}

 map
> var arr = [1,2,3,5,7,11];
> arr.map(function(x) {
console.log(x);
});

 Loop for in
> var arr = [1,2,3,5,7,11];
> for (x in arr) {
console.log(x);
}

1.14.8 Arrays
Arrays são estruturas de dados que lhe permitem colocar uma lista de
valores em uma única variável. Os valores podem ser qualquer tipo de
dado, seja String, Number ou Objeto, ou misto.

 Array de Number
> var arr = [];
> arr.push(1);
> arr.push(2);
> arr.push(3);
> arr
[ 1, 2, 3 ]
44 Construindo Aplicações com NodeJS

 Array de String
> var arr = [];
> arr.push('a');
> arr.push('b');
> arr.push('c');
> arr
[ 'a', 'b', 'c' ]

 Array de objeto
> var arr = [];
> arr.push({ name: 'William' });
> arr.push({ name: 'Bruno' });
> arr
[ { name: 'William' }, { name: 'Bruno' } ]

1.14.9 Promise
Uma promise (https://promisesaplus.com) é uma representação eventual de
uma operação assíncrona, algo que ainda não está completo, mas estará
no futuro. É uma alternativa aos callbacks e nos devolve algumas coisas
básicas da linguagem como try/catch e return. Utilizando corretamente,
conseguimos diminuir o nível de encadeamento, tornando o nosso código
mais legível.
Para declarar uma promise, usamos a função construtora Promise.
> new Promise(function(resolve, reject) {});
Promise { <pending> }

O retorno é um objeto promise que contém os métodos .then() e .catch().


Quando a execução retornar algum resultado, o .then() será invocado
(resolve) ou um erro, o .catch(), será invocado (reject).
Qualquer exceção disparada pela função que gerou a promise ou pelo
callback dentro do .then() será capturada pelo método.catch(). Além disso,
é possível retornar valores síncronos para continuar encadeando novos
métodos .then(), mais ou menos assim:
Capítulo 1 ■ Introdução 45

> p1
.then(cb1)
.then(cb2)
.then(cb3)
.catch(cbError);

O método Promise.all() recebe como argumento um array de promises, e o


seu método .then() é executado quando todas elas retornam com sucesso.

1.14.10 Template strings


Não há diferença entre uma string declarada entre aspas duplas e aspas sim-
ples, apesar de por convenção sempre utilizarmos apenas uma dessas duas
formas. Ainda assim, a interpolação de strings com variáveis era algo bem
custoso, antes do advento da ECMAScript 6 (https://nodejs.org/en/docs/es6/).
> var name = 'Padmé';
> var message = 'Oi, ' + name + ' !';
> message
'Oi, Padmé !'

Com a chegada dos template strings, utilizamos crases para interpolar


variáveis desta forma:
> var name = 'Padmé';
> var message = `Oi, ${name} !`;
> message
'Oi, Padmé !'

Sem nenhum operador de adição.


> var name = 'Qui-Gon Jinn'
> `Oi, ${name} !`;
'Oi, Qui-Gon Jinn !'

Outra facilidade proporcionada é que strings de múltiplas linhas podem


ser escritas sem termos que concatenar ou escapar linha a linha.

Você também pode gostar