língua portuguesa 2018. esferas da linguagem, FTDDescrição completa
2016
Node.js Uma nova abordagem da linguagem Javascript De estudante para estudante Material do grupo SoftwareOriginSP
Fundado do grupo SoftwareOriginSP: Guilherme Guerra
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
O grupo SoftwareOriginSP, é um grupo independente fundado no fim do ano de 2015, sem iniciativa do gorvenamental. Seu principal interesse é seguimenta da criação de jogos para diversas plataformas tanto desktop ou móbile. Entretanto, adquirimos alguns conceitos fundamentais que são estabelecidos para fazer ferramentas para as pessoas, entre elas são: ferramentas para web, sendo sites dinâmicos, e entre outras que podam contribuir na comunicação e da evolução do aspecto humano. As tecnologias feitas pelo grupo são de alta telecomunição, para que as pessoas possam se comunicar em qualquer lugar e a qualquer momento. Usamos ferramentas totalmente aceitas no mercado tecnológico.
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Telefone: (11) 2037-1232
1° Capítulo Instalando e cofigurando o Node.Js no ambiente Windows Sobre o node.js O node.js nasceu em meados de 2009 sendo o seu criador Ryan Dahl e uma equipe de 14 desenvolvedores que pretendiam criar uma plataforma para facilitar a criação de aplicações real-time e de alta escalabilidade. O objetivo principal do node.js é "fornecer uma maneira fácil de criar programas de rede escaláveis" solucionando o principal problema das aplicações atuais feitas em Java, PHP ou .NET, pois essas aplicações iniciam um processo para cada conexão que é acompanhado de aproximadamente de 2MB de memória, desta forma limitando o número de acessos pela quantidade de memória da máquina. Para contornar este problema são criados vários servidores para tornar a aplicação mais escalável. O node.js veio para solucionar este problema mudando a forma que é feito as conexões no servidor, pois ao invés de iniciar um encadeamento em cada conexão será criado um processo que não necessita que o bloco de memória fique alocado a este processo.
Algumas característica do node.js Threads não bloqueantes: uma das melhores características do node.js onde não há um controle de concorrências entre os recursos disponíveis para os processos. Nas linguagens tradicionais como JAVA e .NET gerenciam as suas threads colocando em fila de execução onde os processos que necessitam utilizar o mesmo recurso esperam a sua vez par usufruir do recurso. Programação Orientada à eventos I\O: existem diversas bibliotecas para se trabalhar com os protocolos HTTP, HTTPS, Web Sockets (para aplicações real-time), entre outros protocolos. Mecanismo V8: node.js roda Javascript no lado do servidor utilizando o mecanismo V8 (mecanismo Javascript do Google utilizado no Chrome). Podendo utilizar todo o poder da ECMAScript5 Módulos: contém vários módulos para facilitar o desenvolvimento de aplicações como o módulo de sockets, manipulação de arquivos, url, banco de dados NoSQL, entre outros. Grupo SoftwareOriginSP
Página 3
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Sobre o socket.io O socket.io é um modulo do node.js que permiti fazer comunicação bidirecional utilizando as APIs de transporte nativas dos browsers. Resumidamente este modulo que irá proporcionar o real-time criando uma conexão com o servidor permitindo que haja a troca de informações entre Client e Server sem a necessidade de solicitar um novo request. A troca de informações entre Server e Client é feitas através de eventos onde cada ponta deve monitorar um determinado evento que será disparado. Na parte de exemplos irei detalhar com maiores informações como funciona o disparo dos eventos entre o Server e Client.
Pré-requisitos Executável do Node.js v.0.8.6 (Última versão se encontra no site.) Python v.3.2.3 (Última versão se encontra no site.) Servidor Web. Estarei utilizando o WAMP como servidor Web local para demonstração do exemplo, caso não tenha um servidor Web local e queira instalar o WAMP pode efetuar a instalação e configuração utilizando este poste sobre Instalando e configurando o WAMP.
Instalando e configurando o node.js A instalação do node.js no Windows 7 é muito tranquila, pois segue o padrão das instalações for Windows os passos NNF (Next Next Finish) ou PPR (Próximo próximo e reiniciar). Baixe o executável do node.js através do site oficial. A versão v0.8.6 se encontra do site e poderá baixa e executa em direfentes versões do sistemas operacionais do Windows.
Grupo SoftwareOriginSP
Página 4
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Após o download execute o aplicativo de instalação. Nesta instalação será instalado o node.js, alguns módulos nativos e seu gerenciador de pacotes o NPM (Node Package Manager). Este gerenciador servirá para a isntalação dos demais módulos para a utilização no node.js como o nosso caso o socket.io. Vamos aos passos da instalação.
1º passo - Tela de instalação do node.js: esta tela informa sobre a instalação do node.js para continuar clique no botão "Next"
2º passo - Licença de uso do node.js: nesta tela está descrito a licença do node.js para instalação deve ser selecionado a opção "I accept the terms in the License Agreement" e clique Grupo SoftwareOriginSP
Página 5
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
no botão "Next" caso apareça uma mensagem solicitando a confirmação da instalação selecione a opção para continuar com o wizard.
3º passo - Conclusão da instalação do node.js: se tudo der certo a imagem abaixo deve ser exibida assim confirmando a instalação do node.js. Clique no botão "Finish" para finalizar o wizard.
4º passo - Permissão de acesso: O node.js será instalado no diretório padrão dos aplicativos do Windows. No meu caso este será instalado em "C:\Program Files\nodejs". Precisamos dar permissão para esta pasta para a instalação dos demais módulos do node.js no nosso caso o socket.io. Para dar permissão a pasta vá no diretório de instalação do node.js e clique com o botão direito do mouse na pasta "nodejs" e vá na opção "Propriedades": Grupo SoftwareOriginSP
Página 6
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Clique na aba "Segurança" no campo "Nome de grupos ou de usuários" você deverá dar permissão ao Grupo de Usuários, então clique no botão "Editar" sendo exibida a tela conforme imagem abaixo:
Selecione a opção "Usuários (NOME_DO_COMPUTADOR\Usuários)" e abaixo no campo "Permissões para Usuários" clique na coluna "Permitir" no checkbox "Controle total" sendo marcado o campo "Modificar" automaticamente. Clique botão "OK" retornando para a tela Grupo SoftwareOriginSP
Página 7
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
das propriedades da pasta, clique novamente em "Ok" sendo fechado a tela o retornado para a pasta de instalação do node.js
Instalando o Python Quando necessitamos instalar algum módulo do node.js como o nosso caso o socket.io o gerenciador de pacotes do Node.js (NPM) utiliza na versão Windows o Visual Studio ou o Python, desta forma antes de instalar o módulo socket.io devemos instalar o Python. Baixe o executável para a sua versão através deste link. Após o download execute o arquivo baixado (python-3.2.3.msi). 1º passo - Permitindo execução: caso seja solicitado a execução deste aplicativo clique em "Executar":
2º passo - Selecionando o tipo de instalação: nesta tela é informado se esta instalação será para todos os usuários ou somente para o usuário que este instalando o aplicativo. Deixe a opção "Install for all users" e clique em "Next":
Grupo SoftwareOriginSP
Página 8
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
3º passo - Selecionando diretório de instalação: neste passo você poderá informar um diretório default para a instalação do Python, no meu caso deixarei o default "C:\Python32\". Caso queira alterar escolha a sua pasta e clique no botão "Next" para prosseguir:
4º passo - Customizando a instalação: neste passo você poderá informar o que deseja ou não na sua instalação. Em minha instalação deixei as opções como estão. Após a customização clique em "Next":
Grupo SoftwareOriginSP
Página 9
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
5º passo - Finalizando a instalação: Neste ponto o sistema está instalando o aplicativo caso seja informado uma mensagem para execução do aplicativo clique em "Sim". A tela de finalização da instalação é exibida clique na opção "Finish" para finalizar a instalação.
Reiniciando o Windows Este não é um passo que gostaria de estar colocando neste tutorial, mas em minhas últimas três instalações tudo ocorreu corretamente só após reiniciar o Windows. Então antes de continuar reinicie o seu SO para que as devidas configurações sejam efetivadas.
Instalando o socket.io Com a instalação do node.js o seu gerenciador de pacotes (NPM) já está instalado e pronto para ser usado assim podemos instalar os demais módulos para a utilização do node.js. Por padrão as instalações dos módulos eu utilizo a mesma pasta onde efetuei a instalação do node.js que no meu caso foi em "C:\Program Files\nodejs". Assim abra o prompt do Windows (WinKey + R e digite cmd) e navegue até a pasta da sua instalação utilizando os comandos: cd .. para voltar uma estrutura e cd NOME_DA_PASTA para acessar uma pasta. A instalação do módulo será feita através do comando "npm install socket.io". Vá no prompt e digite o comando acima e pressione ENTER, se tudo der certo deverá ser informado a mensagem do sucesso da instalação.
Grupo SoftwareOriginSP
Página 10
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Fique atento as mensagens que são exibidas no prompt, pois caso ocorra algum erro será demonstrada uma mensagem. Neste momento temos tudo que precisamos para utilizar o node.js com socket.io. Então vamos aos exemplos para testarmos os aplicativos instalados. Botando a mão na massa Nesta parte do tutorial iremos ver através dos exemplos como podemos utilizar o node.js e o socket.io. Os arquivos de exemplos serão executados pelo console (prompt do Windows) utilizando o executável "node". Para demonstração irei salvar os arquivos de exemplos na pasta de instalação do node.js que no meu caso está em "C:\Program Files\nodejs". Então vamos os exemplos. O famoso "Hello world": este exemplo que foi tirado do site oficial do node.js irá exibir o texto "Hello word" no browser do usuário conectado no servidor, porém fiz algumas alterações para demonstrar de uma forma mais detalhada o funcionamento de cada parte do código. Crie um arquivo com o nome "hello_world.js" na pasta de instalação do node.js com o conteúdo abaixo:
// Requisitando os módulos var http = require('http'); /** * Função que irá ser chamada a cada conexão com o Node.js * @param {http.ServerRequest} req Instância do request da requisição * @param {http.ServerResponse} resp Instância do response da requisição */ var onRequest = function (request, response) { // Informando o tipo do cabeçalho da página response.writeHead(200, {'Content-Type': 'text/plain'}); // Escrevendo um texto no response response.write('Hello World\n'); Grupo SoftwareOriginSP
Página 11
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
// Encerrando o response response.end(); }; // Criando o servidor informando a função de callback para o request var server = http.createServer(onRequest); // Iniciando o servidor na porta 1337 server.listen(1337, '127.0.0.1');
// Informando no console (prompt do Windows) a informação console.log('Server running at http://127.0.0.1:1337/');
Com o arquivo criado abra o prompt do Windows e navegue até a pasta da instalação (ou a pasta onde se encontra o arquivo). Digite o comando "node hello_world.js" e pressione ENTER, se tudo der certo será exibido o texto no prompt "Server running at http://127.0.0.1:1337/" conforme imagem abaixo:
Detalhando o código: Linha #2: nesta linha é carregado o módulo HTTP do node.js utilizando o método require. Este método irá retornar o Object referente ao módulo solicitado. Linhas #9 a #18: criada a função que será executada a cada conexão no servidor. Esta função irá receber dois parâmetros um sendo o request da conexão e outro o response da requisição. Através do response podemos exibir as informações no browser do usuário.
Grupo SoftwareOriginSP
Página 12
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Linha #11: nesta linha utilizando o método writeHead para informar o status code da página (no nosso caso 200) e informando no segundo parâmetro o content type (tipo do conteúdo) da página. Linha #14: após informar qual será o content type da página será escrito o texto "Hello world" utilizando o método write do Objeto response no browser do usuário. Linha #17: o método end do Objeto response deve ser chamado em todo request. Caso desejamos informar algo na página podemos informar no primeiro parâmetro o texto a ser exibido. Seguindo o mesmo raciocínio do método write. Linha #21: aqui que a mágica acontece. Nesta linha estamos criando o nosso servidor utilizando o método createServer do módulo HTTP. Este método irá receber como parâmetro uma função que será chamada (executada) no evento request do servidor, ou seja, em toda requisição feita para o servidor. Linha #24: com a referência do servidor criado podemos informar a porta que este irá escutar, assim utilizando o método listen informo a porta de conexão e o hostname do servidor. Linha #27: uma forma de se debugar o código pelo console (prompt do Windows, Shell Linux, etc) é utilizando a função console.log() onde esta deve receber como parâmetro o valor a ser exibido no console. No nosso caso será somente exibida uma mensagem informando que o servidor está rodando no hostname e porta informada no método listen. Utilizando socket.io: irei demonstrar um exemplo simples, mas detalhado de como utilizar o socket.io. Antes de demonstrar o exemplo gostaria de relembrar uma das características informadas no início deste post, sobre "Programação Orientada à eventos I\O (POE)". Quando estamos programando orientado a eventos temos que sempre estar pensando em callbacks, ou seja, uma forma de interagir com o sistema quando um certo evento é disparado. Será desta forma que iremos trabalhar com o socket.io. "Então como funciona os eventos com o scoket.io?" Eventos serão disparados pelas duas pontas da aplicação (Server side e Client side) através do socket que foi aberto. Nos dois lados teremos um monitoramento (listeners) dos eventos que podem ser disparados.
Grupo SoftwareOriginSP
Página 13
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Note pela imagem acima que o evento "userconected" é disparado pelo Client side informando ao Server que o usuário foi conectado, desta forma temos no server um monitoramento para este evento, onde através da função de callback será realizado uma interação com o sistema. Da mesma forma o Server side emite o evento "showmessage" para o Client, onde o socket deste possui um monitoramento do evento utilizando uma função de callback que irá interagir com a aplicação. Quem já programa algum tempo com Javascript esta familiarizado com este tipo de programação, principalmente quem utiliza jQuery, ExtJS, Prototype, entre outros. Então vamos ao nosso exemplo para demonstrar os conceitos informados acima.
1º Passo - Criando a estrutura no Server side: como no primeiro exemplo iremos criar um arquivo na pasta de instação do node.js com o nome "socketio_exemplo.js" com o conteúdo abaixo: /** * Função que irá ser chamada a cada conexão com o node.js * @param {ServerRequest} request Instância do request da requisição * @param {ServerResponse} response Instância do response da requisição */ var onRequest = function (request, response) { // Informando o tipo do cabeçalho da página response.writeHead(200, {'Content-Type': 'text/html'}); // Mensagem exibida no console há cada requisição console.log('Usuário conectado no Server!!!'); // Encerrando o response response.end(); }; // Requisitando os módulos var http = require('http').createServer(onRequest), io = require('socket.io').listen(http); // Informando a porta para ser monitorada pelo Server http.listen(8088, '127.0.0.1'); /** * Evento "connection" que ocorre quando um usuário conecta no socket.io * @param {SocketObject} socket Objeto do socket conectado */ io.sockets.on('connection', function(socket){ /** * Evento "userconected" que ocorre quando a página é carregada. */ socket.on('userconected', function(){ Grupo SoftwareOriginSP
Página 14
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
// Enviando a mensagem só para o socket atual socket.emit('showmessage', 'Usuário conectado no socket!!!'); // Servidor responde o mesmo resultado via broadcast. socket.broadcast.emit('showmessage', 'Outro usuário foi conectado'); }); /** * Evento "disconnect" emitido quando o usuário recarregar ou sair da página */ socket.on('disconnect', function(){ // Resposta do servidor via broadcast. socket.broadcast.emit('showmessage', 'Um usuário saiu ou recarregou à página!!!'); }); });
Detalhando o código: Linhas #6 a #15: é informado a função de callback que será chamada em toda requisição ao node.js na porta 8088. Linhas #18 e #19: são requisitados os dois módulos http e o socket.io. Sendo criado o servidor do node.js utilizando a função "onRequest" e informando para o socket.io escutar a conexão do módulo http. Linha #22: informando para o servidor qual a porta e IP que este irá ser executado. Linhas #28 e #47: este bloco será executado a toda conexão efetuada com o socket.io através do Client. Note que informamos utilizando o método "on" o nome do evento a ser monitorado e a função de callback que será executada quando esse evento for disparado pelo Client. Na função de callback utilizando o parâmetro do socket passado na conexão é atachado dois eventos no socket ("userconected" e "disconnect"), desta forma o Server irá monitorar esses dois eventos que serão emitidos pelo Client. Linhas #32 a #38: é informado através do método "on" o monitoramento do evento "userconected" onde este utilizando o método "emit" do socket será enviado uma mensagem para o usuário referente ao socket passado via parâmetro para o evento "connection". Já na linha #37 será enviada uma mensagem para todos os usuários conectados, menos o usuário do socket corrente. O método "broadcast" envia uma mensagem para todos os demais canais/usuários que estão conectados no Server menos o usuário do socket atual. Linhas #43 a #46: é informado através do método "on" o monitoramento do evento "disconnect" onde este irá emitir uma mensagem para os demais usuários conectados informando que o usuário referente ao atual socket saiu da página. Pelo código conseguimos ver como utilizar o monitoramento dos eventos utilizando os métodos: - on: para informar o evento a ser monitorado e a função de callback que será executada quando o evento for disparado pelo Client. Note que nenhuma das funções recebem parâmetros pelo Client, mas podemos também informar parâmetros para o Server assim como informamos para o Cliente (linha #34 é um exemplo disso). - emit: este método irá emitir o evento para o Client/Server. Nele devemos informar no primeiro parâmetro o nome do evento e os demais itens informados serão os parâmetros que Grupo SoftwareOriginSP
Página 15
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
o Client/Server irá receber na função de callback. - broadcast: este atributo utilizando em conjunto com o método "emit" irá emitir o evento para todos os usuários conectados menos o atual usuário. 2º Passo - Criando a estrutura no Client side: neste passo iremos criar uma página HTML para demonstrar o monitoramento dos eventos disparados pelo Server side. Como mencionado no início do poste irei utilizar um servidor Web local para a demonstração do exemplo com socket.io. Então vamos a configuração. - Crie uma pasta no DOCUMENT_ROOT do seu servidor Web com o nome "socketio_exemplo" crie um arquivo HTML com o nome "socket.html" e com o conteúdo abaixo: Exemplo utilizando socket.io <meta charset="utf-8" /> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <script type="text/javascript" src="http://localhost:8088/socket.io/socket.io.js"> <script type="text/javascript"> // Criando a conexão com o Socket.io var socket = io.connect('http://localhost:8088',{ 'reconnect': true ,'reconnection delay': 500 ,'max reconnection attempts': 1000 ,'secure': false }); // Emitindo o evento para o Server socket.emit('userconected'); // Listeners /** * Monitorando o evento "showmessage" que será disparado pelo Server * @param {String} msg Mensagem a ser exibida */ socket.on('showmessage', function(msg){ var messageEl = document.getElementById('message-info'); // Exibindo a mensagem enviada messageEl.innerHTML += msg; Grupo SoftwareOriginSP
Página 16
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
});
Mensagens do socket.io
Detalhando o código: Neste ponto irei detalhar somente as linhas de código que fazem a mágica acontecer, pois mais é só HTML e CSS. Linha #17: nesta linha estamos solicitando o arquivo JS do socket para o Server. Note que utilizamos o mesmo IP e porta que foi informado na configuração do socket no Server. Linhas #20 a #25: neste bloco que estamos criando a conexão com o socket no Server, neste momento que é disparado o evento "connection". Há algumas opções que podemos passar para a criação do socket. Abaixo a informação de cada atributo: - reconnect (bool): que irá informar se o socket irá tentar se conectar caso perca a conexão com o Server. - reconnection delay (int): informa o tempo em milissegundos que será o delay da tentativa de conexão. - max reconnection attemps (int): informa o valor máximo de tentativas de conexão com o Server. - secure (bool): informando se a conexão com o Server irá utilizar criptografia (conexões HTTPS). Linha #28: utilizando o método "emit" dispara o evento "userconnected" para o Server. Linhas #36 a #41: neste bloco utilizando o método "on" é informado o monitoramento do evento "showmessage" que será disparado pelo Server passando como parâmetro a mensagem a ser exibida no browser do usuário. Linha #37: utilizando o método "getElementById" do JS resgatamos a referência da DIV onde iremos inserir o texto da mensagem enviada pelo Server. Linha #40: utilizando a propriedade "innerHTML" do elemento inserimos o texto que é informado pelo Server. no
3º Passo - Rodando o exemplo: para executar o nosso exemplo primeiro temos que iniciar o serviço do node.js através do nosso arquivo "socketio_exemplo.js", assim pelo prompt do Windows vá na pasta de instalação do node.js e execute o comando "node socketio_exemplo.js" (sem aspas) e pressione ENTER.
Grupo SoftwareOriginSP
Página 17
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Veja a mensagem escrita no console (prompt do Windows) informando que o socket.io foi iniciado. Agora iremos acessar o nosso arquivo "socket.html" pelo servidor Web no browser digite http://localhost/socketio_exemplo/socket.html exibindo o conteúdo conforme abaixo:
O texto "Usuário conectado no socket!!!" é exibido através do monitoramento feito nas linhas #36 a #41 do arquivo "socket.html".
Grupo SoftwareOriginSP
Página 18
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
A imagem acima demonstra as mensagens que estão sendo exibidas no prompt do Window informando o usuário conectado. Note no texto grifado em vermelho. Neste trecho o socket está emitindo uma mensagem para o Client utilizando o evento "showmessage" passando como parâmetro a mensagem a ser exibida no DIV. Abra outro navegador e digite http://localhost/socketio_exemplo/socket.html será exibido a mensagem "Outro usuário foi conectado" no primeiro browser aberto e no último será exibido a mensagem "Usuário conectado no socket!!!".
Se você olhar novamente para o prompt do Windows verá as mensagens enviadas para os dois sockets conectados
Pela imagem podemos ver que a mensagem "Usuário conectado no socket!!!" é disparada duas vezes uma para o primeiro browser (Chrome) e outra para o segundo browser (Firefox), Grupo SoftwareOriginSP
Página 19
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
já a mensagem "Outro usuário foi conectado" foi enviada somente para o socket já conectado (Chrome).
2° Capítulo Instalando o node.js no ambiente Linux e em um servidor Ubuntu 14.04 Introdução O Node.js é uma plataforma Javascript para programação do lado servidor, que permite aos usuários construírem aplicações de rede rapidamente. Ao levar o Javascript tanto ao front-end quanto ao back-end, o desenvolvimento pode ser mais consistente e ser projetado dentro do mesmo sistema. Neste guia, vamos mostrar a você como começar com o Node.js em um servidor Ubuntu 14.04. Se você estiver buscando criar um ambiente de produção Node.js, confira este link How To Set Up a Node.js Application for Production. Como Instalar a Versão Distro-Stable
O Ubuntu 14.04 contém uma versão do Node.js em seus repositórios padrão que pode ser utilizada para fornecer facilmente uma experiência consistente entre múltiplos servidores. A versão nesses repositórios é a 0.10.25. Esta não é a última versão, mas deve ser bastante estável. Para obter esta versão, temos apenas que utilizar o gerenciador de pacotes apt. Devemos atualizar nosso índice de pacotes e, então, instalar através dos repositórios: sudo apt-get update sudo apt-get install nodejs
Grupo SoftwareOriginSP
Página 20
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Se o pacote no repositório satisfaz suas necessidades, isto é tudo que você precisa fazer para ter o Node.js configurado. Em muitos casos, você vai querer também instalar o npm, que é o gerenciador de pacotes do Node.js. Você pode fazer isto digitando: sudo apt-get install npm Isto o permitirá instalar facilmente módulos e pacotes para utilizar com o Node.js. Devido a um conflito com outro pacote, o executável dos repositórios do Ubuntu é chamado nodejs em vez de node. Tenha isso em mente quando estiver executando software. Abaixo, discutiremos alguns métodos mais flexíveis de instalação. Como Instalar Utilizando um PPA Uma alternativa que pode lhe trazer a versão mais recente do Node.js é adicionar um PPA (arquivo de pacotes pessoais) mantido pelo NodeSource. Isto terá provavelmente versões mais atualizadas do Node.js do que os repositórios oficiais do Ubuntu. Primeiro, você precisa instalar o PPA de modo a obter acesso ao seu conteúdo: curl -sL https://deb.nodesource.com/setup | sudo bash O PPA será adicionado à sua configuração e o seu cache local de pacotes será atualizado automaticamente. Depois de executar o script de configuração do nodesource, você pode instalar o pacote Node.js da mesma forma que você fez acima: sudo apt-get install nodejs O pacote nodejs contém o binário nodejs bem como o npm, de forma que você não precisa instalar o npm separadamente. Contudo, para que alguns pacotes do npm funcionem (tais como aqueles que requerem compilação do fonte), você precisará instalar o pacote build-essentials: sudo apt-get install build-essential Como Instalar Utilizando o NVM
Uma alternativa para instalação do Node.js através do apt é usar uma ferramenta especialmente projetada, chamada nvm, que significa "Node.js version manager" ou "Gerenciador de Versão do Node.js". Usando o nvm você pode instalar múltiplas versões, auto-contidas do Node.js que o permitirá controlar seu ambiente mais facilmente. Ele dará a você acesso sob demanda às mais novas versões do Node.js, mas também o permitirá apontar versões prévias que suas aplicações podem depender. Para começar, precisaremos obter os pacotes de software do nosso repositório Ubuntu, que nos permitirão compilar pacotes de fontes. O script nvm aproveitará estas ferramentas para construir os componentes necessários: Grupo SoftwareOriginSP
Página 21
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
sudo apt-get update sudo apt-get install build-essential libssl-dev Uma vez que os pacotes requeridos estejam instalados, você pode baixar o script de instalação do nvm da página do projeto GitHub. O número de versão pode ser diferente, mas em geral, você pode baixar e o instalar com a seguinte sintaxe: curl https://raw.githubusercontent.com/creationix/nvm/v0.16.1/install.sh | sh Isto irá baixar o script e o executar. Ele irá instalar o software dentro de um subdiretório do seu diretório home em ~/.nvm. Ele irá adicionar também as linhas necessárias ao seu arquivo ~/.profile para utilizar o arquivo. Para obter acesso à funcionalidade do nvm, você precisará sair e se logar novamente, ou você pode varrer o arquivo ~/.profile de modo que sua sessão atual saiba sobre as alterações: source ~/.profile Agora que você tem o nvm instalado, você pode instalar versões isoladas do Node.js. nvm ls-remote ... v0.11.6 v0.11.7 v0.11.8 v0.11.9 v0.11.10 v0.11.11 v0.11.12 v0.11.13 Como você pode ver, a versão mais recente no momento da redação deste artigo é a v0.11.13. Você pode instalá-la digitando: nvm install 0.11.13 Usualmente, o nvm irá utilizar a versão mais recente instalada. Você pode dizer explicitamente ao nvm para utilizar a versão que acabamos de baixar digitando: nvm use 0.11.13 Quando você instala o Node.js utilizando o nvm, o executável é chamado node. Você pode ver a versão atualmente sendo utilizada pelo shell digitando: node -v
Grupo SoftwareOriginSP
Página 22
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
v.0.11.13 Se você tiver múltiplas versões do Node.js, você pode ver o que está instalado digitando: nvm ls Se desejar tornar padrão uma das versões, você pode digitar: nvm alias default 0.11.13 Esta versão será automaticamente selecionada quando uma nova sessão for iniciada. Você também pode referenciá-la pelo apelido desta maneira: nvm use default Cada versão do Node.js irá manter o controle de seus próprios pacotes e tem npm disponível para gerenciá-los. Você pode ter pacotes de instalação do npm para o diretório ./node_modules do projeto Node.js utilizando o formato normal: npm install Express Se você deseja instalá-lo globalmente (disponível para outros projetos utilizando a mesma versão de Node.js), você pode adicionar o flag -g: npm install -g Express Isto instalará o pacote em: ~/.nvm/node_version/lib/node_modules/package_name Instalando globalmente permitirá a você executar comandos através da linha de comando, mas você terá que vincular o pacote em sua esfera local para exigi-lo de dentro de um programa: npm link Express Você pode aprender mais sobre as opções disponíveis com o nvm digitando: nvm help
Grupo SoftwareOriginSP
Página 23
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
3° Capítulo Primeiros passos com node.js O que é Node.js?
Node.js é uma plataforma para desenvolvimento de aplicações server-side baseadas em rede utilizando JavaScript e o V8 JavaScript Engine, ou seja, com Node.js podemos criar uma variedade de aplicações Web utilizando apenas código em JavaScript. Em uma primeira análise essa informação pode não parecer tão interessante, uma vez que existem diversas outras maneiras em que esses tipos de serviços podem ser implementados. Mas se pensarmos um pouco mais sobre as demandas de aplicações na internet e o modo em que o código em JavaScript pode ser estruturado, vamos nos deparar com uma gama de novas possibilidades para desenvolvimento Web, e provavelmente nos juntar à crescente comunidade que tem adotado essa plataforma. Embora Node.js tenha muito em comum com os servidores tradicionais como Apache e IIS, podemos entender melhor essa relação se considerarmos as diferenças. Node.js é extremamente simples, por isso pode não ser a solução ideal para todos os casos. Enquanto os servidores tradicionais são mais robustos e preparados para situações mais complexas, e isso invariavelmente vai consumir mais recursos das máquinas do que Node.js.
Grupo SoftwareOriginSP
Página 24
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
O web server ‘Olá mundo!’ Ok, então vamos construir alguma coisa. Nosso primeiro exemplo é um servidor que retorna a string ‘Olá mundo’ para qualquer requisição. Para fazer isso utilizando Node você vai precisar de criar um arquivo JavaScript que pode ser chamado olanode.js e de três minutos do seu tempo. Escreva o seguinte código no seu arquivo:
1. 2. 3. 4. 5. 6.
var http = require('http'); http.createServer(function(req,res) { res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' }); res.end('Olá mundo!'); }).listen(3000); console.log('Servidor iniciado em localhost:3000. Ctrl+C para encerrar…');
Para executar o seu programa Node basta o seguinte comando no seu terminal: 1. $ node olanode.js Para testar seu servidor você pode acessar localhost:3000 no seu navegador ou utilizar linha de comando com o comando curl (em uma nova instância do terminal) como mostrado a seguir: 1. $ curl http://0.0.0.0:3000/ 2. > Olá mundo! Caso você prefira retornar algum html válido para o navegador, basta alterar 'text/plain' para 'text/html' no código acima e utilizar uma tag html legal como
'); }).listen(3000); console.log('Servidor iniciado em localhost:3000 . Ctrl+C para encerrar…’);
Orientado a eventos e não obstrutivo Orientado a eventos Vamos aproveitar este momento de euforia após a construção do seu primeiro servidor para aprender um pouco mais sobre Node.js. Quando estamos desenvolvendo com Node.js devemos utilizar uma abordagem orientada a eventos, isso quer dizer que o desenvolvedor precisa conhecer os eventos que serão emitidos em diferentes momentos da execução e também saber como ouvi-los para executar as operações necessárias. Grupo SoftwareOriginSP
Página 25
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Um bom exemplo de orientação a eventos está na construção de interfaces de usuário. Muitas vezes utilizamos elementos como por exemplo os botões que ao serem clicados emitem um evento do tipo click ao qual podemos ouvir e executar alguma operação. No nosso exemplo anterior utilizamos esse conceito quando chamamos método listen do objeto do tipo web server e passamos como parâmetro a porta 3000, com isso fizemos que a nossa aplicação ouvisse ao evento que é emitido sempre que alguém faz uma requisição no localhost:3000 e a nossa resposta foi servir a string ou a página html. Este evento é chamado request. Para ilustrar estes conceitos, podemos escrever o nosso exemplo anterior em uma sintaxe alternativa da seguinte forma: 1. 2. 3. 4. 5. 6. 7. 8.
var http = require('http'); var server = http.createServer(); server.on('request', function(req,res) { res.writeHead(200, { 'Content- Type': 'text/html; charset=utf-8' }); res.end('
Olá mundo!
'); }); server.listen(3000); console.log('Servidor iniciado em localhost:3000. Ctrl+C para encerrar…’);
Dessa forma podemos ver claramente a maneira em que o Node.js opera para servir a sua página. Utilizamos o método on do nosso objeto server para ouvir ao evento request e fazer as operações. E definimos que estamos servindo na porta 3000. Não obstrutivo Todos os recursos presentes no Node.js e também a maioria das bibliotecas feitas para ele adotaram um padrão não obstrutivo de escrever código, isso quer dizer que em Node.js você geralmente vai conseguir estruturar seu código de uma maneira que operações que não dependem de nada que está sendo executado possam ser executadas de forma independente. Para mostrar um pouco como isso funciona, vamos um programa que escreve duas frases no terminal, porém uma dessas frases precisa ser carregada da memória antes de ser impressa. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13.
var frase; carregaFrase = function (callback) { setTimeout(function() { //Simula leitura da frase no banco de dados. frase = "Minha frase obstrutiva"; callback(); }, 3000) } imprimeFrase = function () { console.log(frase); } carregaFrase(imprimeFrase); console.log(“Olá");
Grupo SoftwareOriginSP
Página 26
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Nesse exemplo foi criada uma função chamada carregaFrase cujo objetivo é ler uma determinada frase de uma fonte de dados, e uma outra função chamada imprimeFrase que imprime o valor de uma determinada variável no console. Como dependemos da leitura da frase na fonte de dados para imprimir o valor, passamos a função que imprime como parâmetro para a função de leitura para que possamos executar essa função quando a leitura for concluída. Esse tipo de função que é passada como parâmetro dessa maneira é chamada de callback. Ao executar este exemplo com Node.js ou qualquer mecanismo JavaScript você vai perceber que a frase “Olá” será impressa antes da outra frase mesmo estando posicionada depois no código, isso se deve ao fato de sua execução não depender de nada enquanto a execução da outra frase depende de uma operação que leva 3 segundos. Este é um exemplo extremamente simples de como criar um código não obstrutivo, portanto use sua imaginação para imaginar cenários em que isso pode ser útil. Observe que no nosso primeiro exemplo com Node.js tanto a função on quanto a função createServer podem receber uma função de callback.
4° Capítulo Uma previa introdução sobre a plataforma node.js
Já tenho o Node instalado, e agora o que fazer? Uma vez instalado, agora você tem acesso a um novo comando chamado node. Você pode usar o comando node de duas formas diferentes. A primeira é sem argumentos. Isto irá abrir um shell interativo (REPL: read-eval-print-loop), onde você pode executar código JavaScript puro.
$ node > console.log('Hello World'); Hello World undefined
No exemplo acima eu digitei console.log('Hello World') dentro do shell e apertei enter. O Node vai então executar o código e nós podemos ver nossa mensagem registrada. Ele também imprime undefined pelo fato de sempre mostrar o valor de retorno de cada comando, e console.log não retorna nada.
Grupo SoftwareOriginSP
Página 27
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
A outra forma de rodar o Node é fornecendo a ele um arquivo JavaScript para execução. Isto será na maioria das vezes a maneira como você irá utilizá-lo.
hello.js console.log('Hello World'); $ node hello.js Hello World Neste exemplo, eu movi o comando console.log() para dentro de um arquivo e então passei este arquivo para o comando node como um argumento. O Node então roda o JavaScript contido neste arquivo e imprime "Hello World".
Fazendo Algo Útil
Rodar código JavaScript é divertido e tal, mas não é muito útil. Ai é onde o Node.js também inclui um poderoso conjunto de bibliotecas (módulos) para se fazer coisas reais. No primeiro exemplo eu vou abrir um arquivo de registros e analisá-lo. example-log.txt 2013-08-09T13:50:33.166Z A 2 2013-08-09T13:51:33.166Z B 1 2013-08-09T13:52:33.166Z C 6 2013-08-09T13:53:33.166Z B 8 2013-08-09T13:54:33.166Z B 5 O que esses dados registrados significam não importa, mas basicamente cada mensagem contém uma data, uma letra e um valor. Eu quero somar os valores para cada letra.
A primeira coisa que nós precisamos fazer é ler o conteúdo do arquivo.
my-parser.js
// Carregando o módulo fs (filesystem) var fs = require('fs'); // Leia o conteúdo do arquivo para a memória fs.readFile('example-log.txt', function ( err, logData ) { // Se um erro ocorrer, será lançada uma // exceção, e a aplicação irá ser encerrada if ( err ) throw err; Grupo SoftwareOriginSP
Página 28
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
// logData é um Buffer, converta-o para string var text = logData.toString(); }); Felizmente o Node.js faz a entrada e saída (I/O) do arquivo facilmente com o módulo embutido filesystem (fs). O módulo fs tem uma função chamada readFile que pega o caminho de um arquivo e um callback. O callback vai ser invocado quando o arquivo for lido por completo. O dado do arquivo vem na forma de um Buffer, que é basicamente um array de bytes. Nós podemos convertê-lo para uma string usando a função toString(). Agora vamos adicionar o parsing (analisador).
my-parser.js // Carregando o módulo fs (filesystem) var fs = require('fs'); // Leia o conteúdo do arquivo para a memória fs.readFile('example-log.txt', function ( err, logData ) { // Se um erro ocorrer, será lançada uma // exceção, e a aplicação irá ser encerrada if ( err ) throw err; // logData é um Buffer, converta para string var text = logData.toString(); var results = {}; // Quebrando o arquivo em linhas var lines = text.split( '\n' ); lines.forEach(function ( line ) { var parts = line.split( ' ' ); var letter = parts[ 1 ]; var count = parseInt( parts[ 2 ] ); if ( !results[ letter ] ) { results[ letter ] = 0; } results[ letter ] += parseInt( count ); }); console.log( results ); // { A: 2, B: 14, C: 6 } }); Grupo SoftwareOriginSP
Página 29
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Agora vamos passar este arquivo como um argumento para o comando node e ele vai imprimir o resultado e sair.
$ node my-parser.js { A: 2, B: 14, C: 6 } Eu uso muito o Node.js para scripts como este. É uma alternativa muito mais simples e poderosa que os scripts bash. Callbacks Assíncronos Como você viu no exemplo anterior, o padrão típico do Node.js é o uso de callbacks assíncronos. Basicamente você está dizendo a ele para fazer algo e quando isso estiver terminado ele irá chamar sua função (callback). Isto porque o Node é de thread única. Enquanto você está esperando pelo disparo do callback, o Node pode fazer outras coisas ao invés de bloquear até que a requisição esteja terminada. Isso é especialmente importante para servidores web. Isto é muito comum em aplicações web modernas para acessar banco de dados. Enquanto você espera pelo retorno do banco de dados, o Node pode processar mais requisições. Isso permite que você manipule milhares de conexões conjuntas com pequenos acréscimos, comparado a criar uma thread separada para cada conexão.
Fazendo Algo Útil - Servidor HTTP Como disse anteriormente, o Node não faz nada por si só. Um dos módulos embutidos tornam a criação de servidores HTTP simples muito fácil, que é o exemplo na página inicial do Node.js.
my-web-server.js
var http = require('http'); http.createServer(function ( req, res ) { // req = requisição, res = resposta res.writeHead( 200, { 'Content-Type': 'text/plain' } ); res.end( 'Hello World\n' ); }).listen( 8080 ); console.log( 'Servidor rodando na porta 8080' );
Quando eu digo básico, quero dizer básico mesmo. Este não é um servidor HTTP completo. Ele não pode servir qualquer arquivo HTML ou de imagem. De fato, não importa sua requisição, ela vai retornar 'Hello World'. No entanto, você pode rodar isto e verá em seu navegador no endereço http://localhost:8080 o texto "Hello World". Grupo SoftwareOriginSP
Página 30
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
$ node my-web-server.js Você pode ter percebido uma coisa diferente agora. Sua aplicação node.js não fechou. Isto acontece pois você criou um servidor e sua aplicação node vai continuar rodando e respondendo as requisições até que você mesmo mate o processo. Se você quiser ter um servidor web completo, você terá que fazer este trabalho. Você deve checar o que foi requisitado, ler os arquivos apropriados e enviar o conteúdo de volta. Pessoas já fizeram este trabalho duro para você.
Fazendo Algo Útil – Express Express é um framework que torna a criação de sites normais muito simples. A primeira coisa que você tem que fazer é instalá-lo. Juntamente com o comando node, você também tem acesso a um comando chamado npm. Esta ferramenta permite que você acesse uma enorme coleção de módulos criados pela comunidade, e um deles é o Express.
$ cd /my/app/location $ npm install express
Quando você instala um módulo, ele vai ser colado em uma pasta chamada node_modules dentro do diretório da sua aplicação. Você pode agora requisitar (require) este módulo como um módulo embutido. Vamos criar um arquivo estático básico usando o Express.
Agora você tem um servidor de arquivos estáticos bastante eficiente. Tudo que você colocar dentro da pasta /public poderá ser requisitado pelo seu navegador e será mostrado. HTML, imagens, enfim, tudo. Por exemplo, se você colocar uma imagem chamada my-image.png dentro da pasta public, você pode acessá-la usando seu navegador no endereço http://localhost:8080/my-image.png. Claro que o Express tem vários outros recursos, mas você pode olhá-los a medida que continua desenvolvendo.
Grupo SoftwareOriginSP
Página 31
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
NPM Nós usamos um pouco o NPM nas seções anteriores, mas eu quero enfatizar o quão importante esta ferramenta se faz no desenvolvimento para Node.js. Existem milhares de módulos disponíveis que resolvem quase todos os problemas típicos que você encontra. Lembre-se de checar o NPM antes de re-inventar a roda. Não é inédito para uma aplicação Node ter dezenas de dependências. No exemplo anterior nós instalamos o Express manualmente. Se você tiver muitas dependências, essa não será uma forma muito interessante de instalá-las. É por isso que o NPM faz uso do arquivo package.json.
package.json.
{ "name" : "MyStaticServer", "version" : "0.0.1", "dependencies" : { "express" : "3.3.x" } } Um arquivo package.json contém um resumo da sua aplicação. Existem vários campos disponíveis, sendo este apenas o mínimo. A seção dependencies (dependências) descreve o nome e a versão dos módulos que você gostaria de instalar. Neste caso eu vou aceitar qualquer versão do Express 3.3. Você pode listar quantas dependências quiser nesta seção. Agora, ao invés de instalar cada dependência em separado, nós podemos rodar um simples comando e instalar todas elas.
$ npm install Quando você roda este comando, o npm vai verificar na pasta atual pelo arquivo package.json. Se ele encontrar um, então irá instalar cada dependência listada.
Organização do Código Até agora só usamos um único arquivo, que não é muito sustentável. Na maioria das aplicações, seu código vai ser dividido em vários arquivos. Não existe nenhuma norma ou organização imposta dizendo para onde os arquivos vão. Isto não é Rails. Não há conceitos de views e controllers acontecendo aqui. Você pode fazer o que quiser. Vamos refatorar o script de análise de registros (log parsing). Ele será muito mais testável e manutenível se nós separarmos a lógica de análise (parsing) dentro de um arquivo próprio.
Grupo SoftwareOriginSP
Página 32
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
parser.js
// Construtor Parser var Parser = function () { }; // Analisa o texto especificado Parser.prototype.parse = function ( text ) { var results = {}; // Quebra o arquivo em linhas var lines = text.split('\n'); lines.forEach(function ( line ) { var parts = line.split( ' ' ); var letter = parts[ 1 ]; var count = parseInt( parts[2] ); if ( !results[ letter ] ) { results[ letter ] = 0; } results[ letter ] += parseInt( count ); }); return results; }; // Exportando o construtor Parser neste módulo module.exports = Parser; O que eu fiz foi criar um novo arquivo para conter a lógica da análise dos registros. Isto é apenas JavaScript puro e existe várias formas de se encapsular este código. Eu escolhi por definir um novo objeto JavaScript pois assim é mais fácil de se fazer testes unitários. A parte importante para isso é a linha module.exports. Isso diz ao Node que você está exportando deste arquivo. Neste caso exportei um construtor, então os usuários podem criar instâncias do meu objeto Parser. Você pode exportar qualquer coisa que quiser.
Agora vamos ver como importar este arquivo e fazer uso do novo objeto Parser.
my-parser.js
Grupo SoftwareOriginSP
Página 33
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
// Requisitando o arquivo parser.js var Parser = require('./parser'); // Carregandoo módulo fs (filesystem) var fs = require('fs'); // Lendo o conteúdo do arquivo para a memória fs.readFile('example-log.txt', function ( err, logData ) { // Se um erro ocorrer, irá ser lançada // a exceção e a app será encerrada if ( err ) throw err; // logData é um Buffer, converta-o para string var text = logData.toString(); // Criando uma instância do objeto Parser var parser = new Parser(); // Chame a função parse console.log( parser.parse( text ) ); // { A: 2, B: 14, C: 6 } }); Arquivos são incluídos da mesma forma que os módulos, exceto que você inclui um caminho ao invés de um nome. A extensão .js é implícita, então você pode omití-la se quiser. Tendo sido exportado um construtor, é isso que vai ser retornado da declaração require. Eu posso agora criar instâncias do meu objeto Parser e usá-las.
Grupo SoftwareOriginSP
Página 34
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
6° Capítulo Conceitos Básicos sobre Criação de uma Aplicação Cordova Instale o Cordova em seu sistema local para encapsular uma aplicação HTML5 como aplicação móvel nativa com o NetBeans IDE. Você usará npm, o gerenciador de pacote NodeJS, para instalar e atualizar o Cordova. Confirme também se o Git está instalado em seu sistema local e configurado corretamente. O Cordova usa o Git para recuperar em um repositório qualquer arquivo de código-fonte Cordova quando você encapsula a aplicação como móvel nativa. A maioria das etapas a seguir deste exercício serão executadas na janela do terminal. 1. Faça download e instale o Node.js, se não estiver instalado. Você pode fazer o download do instalador no site do Node.js. 2. Abra uma janela de terminal. 3. Execute o comando a seguir para confirmar se o Node.js está instalado. $ node -v Se o node.js estiver instalado, você verá uma versão impressa na janela do terminal. Grupo SoftwareOriginSP
Página 35
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Observações.
Se estiver protegido por proxy, você precisará configurar o node.js para usar o proxy no acessar à rede. Execute os comandos a seguir para configurar o proxy, substituindo http://proxy:8080 pelo seu proxy.
$ sudo npm config set proxy http://proxy:8080 $ sudo npm config set https-proxy http://proxy:8080 Execute o seguinte comando para exibir as definições de configuração atuais. $ npm config list
Para obter informações adicionais sobre a configuração do node.js, consulte https://npmjs.org/doc/config.html. Execute o comando a seguir para instalar o Cordova. $ npm install -g cordova Observações.
Confirme se a configuração do proxy está correta, caso veja uma mensagem de erro na janela do terminal quando executar o comando de instalação.
Você pode executar o seguinte comando que atualiza o Cordova para a versão mais recente. $ npm update -g cordova Execute o seguinte comando para confirmar se o Cordova está instalado e exibir a
versão. $ cordova --version Se o Cordova estiver instalado, você verá uma versão impressa na janela do terminal. Faça o download e instale o sistema de controle de versão Git se não estiver instalado. Você pode fazer o download do instalador no site do Git. Observação. Adicione o Git à variável de ambiente Path. Execute o comando a seguir para confirmar se o Git foi instalado. $ git --version Grupo SoftwareOriginSP
Página 36
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
1. Se o Git estiver instalado, você verá uma versão impressa na janela do terminal. Observações.
Se estiver protegido por proxy, você precisará configurar o Git para usar o proxy no acessar à rede. Execute os comandos a seguir para configurar o proxy, substituindo http://proxy:8080 pelo seu proxy.
$ git config --global http.proxy http://proxy:8080 $ git config --global https.proxy http://proxy:8080 Execute o seguinte comando para exibir as definições de configuração atuais. $ git config --list
Para obter informações adicionais sobre a configuração do Git, consulte as instruções de configuração em http://git-scm.com/book/en/Getting-Started-First-Time-Git-Setup.
Agora você tem todas as ferramentas de que precisa para desenvolver e encapsular uma aplicação móvel nativa no IDE. No próximo exercício, você usará o assistente de Novo Projeto para criar a aplicação.
Criando uma Aplicação Cordova
Neste exercício, você usará o assistente de Novo Projeto no IDE para criar uma nova aplicação Cordova. Crie uma aplicação Cordova selecionando o modelo Hello World do Cordova como modelo de site no assistente de Novo Projeto. O Cordova é uma aplicação HTML5 com algumas bibliotecas e arquivos de configuração adicionais. Se você tiver uma aplicação HTML5 existente, poderá usar a janela Propriedades do Projeto no IDE para adicionar os códigos-fonte do Cordova e outros arquivos exigidos para encapsular a aplicação como Cordova. Neste tutorial, você criará um projeto HTML5 bem básico que tem um arquivo index.html e alguns arquivos JavaScript e CSS. Você selecionará algumas bibliotecas jQuery JavaScript quando criar o projeto no assistente. 1. Selecione Arquivo > Novo Projeto (Ctrl-Shift-N; ⌘-Shift-N no Mac) no menu principal para abrir o assistente Novo Projeto.
Grupo SoftwareOriginSP
Página 37
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
2. Selecione a categoria HTML5 e, em seguida, selecione Aplicação Cordova. Clique em Próximo.
3. Digite CordovaMapApp como Nome do Projeto e especifique o diretório no seu computador onde você quer salvar o projeto. Clique em Próximo. 4. Na Etapa 3. Modelo de Site, confirme se Fazer Download do Modelo On-line está selecionado e se o modelo Cordova Hello World está selecionado na lista. Clique em Próximo.
Grupo SoftwareOriginSP
Página 38
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
1. Observação: Você deve estar on-line para criar um projeto baseado em um dos modelos on-line da lista. 2. Na Etapa 4. Arquivos JavaScript, selecione as bibliotecas JavaScript jquery e jquery-mobile no painel Disponível e clique no botão de seta para a direita ( > ) para mover as bibliotecas selecionadas para o painel Selecionado do assistente. Por default, as bibliotecas são criadas na pasta js/libraries do projeto. Para este tutorial, você utilizará as versões "minimizadas" das bibliotecas JavaScript. Você pode usar o campo de texto no painel para filtrar a lista de bibliotecas JavaScript. Por exemplo, digite jq no campo para ajudá-lo a encontrar as bibliotecas jquery. Você pode usar Ctrl-clique nos nomes das bibliotecas para selecionar várias bibliotecas.
Grupo SoftwareOriginSP
Página 39
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
1. Observações.
Você pode clicar no número de versão da biblioteca na coluna Versão para abrir uma janela pop-up que permite selecionar a ordem das versões de biblioteca. Por default, o assistente exibe a versão mais recente.
As versões minimizadas das bibliotecas JavaScript são versões compactadas e o código não é abrangente quando exibido em um editor. Na Etapa 5. Suporte a Cordova, use os valores padrão. Clique em Finalizar para concluir o assistente. Quando você clicar em Finalizar, o IDE criará o projeto e exibirá um nó para o projeto na janela Projetos,e abrirá o arquivo index.html no editor
Grupo SoftwareOriginSP
Página 40
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Se você expandir a pasta js/libs na janela Projetos, poderá ver que as bibliotecas JavaScript que você especificou no assistente do Novo Projeto foram automaticamente adicionadas ao projeto. Você pode remover uma Biblioteca JavaScript de um projeto clicando com o botão direito do mouse no arquivo JavaScript e escolhendo Deletar no menu pop-up. Para adicionar uma biblioteca JavaScript a um projeto, clique com o botão direito do mouse no nó do projeto e escolha Propriedades para abrir a janela Propriedades do Projeto. Você pode adicionar bibliotecas no painel Bibliotecas JavaScript da janela Propriedades do Projeto. Como alternativa, é possível copiar um arquivo JavaScript que está no sistema local diretamente na pasta js. Agora você pode testar a execução do projeto e ver se ele foi implantado no emulador do seu dispositivo móvel de destino. 7. Clique no ícone de seleção do browser na barra de ferramentas e confirme se o seu emulador de dispositivo móvel de destino está selecionado na coluna Cordova da tabela. Na coluna Cordova, você pode selecionar o Emulador do Android ou do iOS (exige OS X e XCode). Grupo SoftwareOriginSP
Página 41
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
8. Clique no ícone Executar na barra de ferramentas. Quando você escolhe Executar, o IDE implanta a aplicação Cordova no emulador.
Observação. Se você estiver implantando no Simulador do iOS, o simulador deverá ser aberto automaticamente. Se estiver implantando a aplicação em um emulador do Android, você precisará configurar e iniciar o emulador antes de executar a aplicação. Para ver um screencast que demonstre a implantação de uma aplicação Cordova no emulador do Android, assista ao Vídeo de Conceitos Básicos sobre Desenvolvimento Cordova. Modificando a Aplicação Neste exercício, você editará os arquivos index.html e index.js. Você substituirá o código gerado pelo modelo Cordova Hello World pelo código de exibição de um mapa do local atual na aplicação. Além disso, modificará a configuração padrão do Cordova para remover os plugins do Cordova que não são necessários na aplicação.
Editando o Arquivo HTML
Neste exercício, edite o arquivo HTML no editor de código-fonte para adicionar referências a bibliotecas e arquivos CSS e para adicionar os elementos da página. Grupo SoftwareOriginSP
Página 42
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
1. Abra index.html no editor (se ainda não estiver aberto). No editor, você pode ver que o IDE gerou um código baseado no modelo Cordova Hello World. 2. No editor, adicione referências a bibliotecas JavaScript e arquivos CSS do jQuery que foram adicionados quando você criou o projeto. Adicione o seguinte código (em negrito) entre as tags de abertura e fechamento . 3. 4.
Adicione o link a seguir à API JavaScript do Google Maps entre as tags . <script type="text/javascript" src="http://www.google.com/jsapi"> Observação. Este é um link para a v2 obsoleta da API JavaScript. Este JavaScript funcionará para fins de demonstração neste tutorial, mas você deve usar a versão mais nova em uma aplicação real. Grupo SoftwareOriginSP
Página 43
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Remova todo o código entre as tags , exceto os seguintes links para os arquivos JavaScript index.js e cordova.js.
O arquivo index.js foi gerado automaticamente quando você criou o projeto. O arquivo pode ser visto sob o nó js na janela Projetos. Você modificará o código em index.js posteriormente no tutorial. O cordova.js não é visto na janela Projetos porque é gerado na criação da aplicação Cordova. 5. Adicione o seguinte código (em negrito) entre as tags body. 6. 7. 8.
Modificando a Configuração do Cordova Neste exercício, você modificará a lista de plug-ins do Cordova que são instalados na aplicação. 1. Na janela Projetos, clique com o botão direito do mouse no nó do projeto e escolha Propriedades no menu pop-up. 2. Selecione Cordova na lista de categorias.
Grupo SoftwareOriginSP
Página 45
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
1. Use a guia Aplicação para exibir e editar os detalhes de configuração do Cordova sobre a aplicação que são especificados em config.xml. 2. Clique na guia Plug-ins do painel Cordova. A guia Plug-ins contém dois painéis. O painel Disponível exibe uma lista dos plug-ins do Cordova que estão disponíveis atualmente. O painel Selecionado exibe uma lista dos plug-ins que são instalados na aplicação. Por padrão, todos os plug-ins são instalados quando você usa o modelo Cordova Hello World para criar a aplicação. A maioria das aplicações não exige todos os plug-ins. Você pode usar a guia Plug-ins na janela Propriedades do Projeto para remover os plug-ins que não são exigidos pela aplicação. Observação. É possível também editar os plug-ins instalados editando o arquivo nbproject/plugins.properties no editor. 3. Remova todos os plug-ins exceto API do Dispositivo, Caixas de Diálogo (Notificações) e Localização Geográfica. Clique em OK.
Grupo SoftwareOriginSP
Página 46
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Editando o Arquivo JavaScript Neste exercício, você removerá o código JavaScript gerado pelo modelo e adicionará alguns métodos simples para exibir o mapa da sua localização atual. 1. Abra o index.js no editor. O IDE gerou um código padronizado em index.js quando você criou o projeto. Nessa aplicação, você pode remover todo o código gerado. 2. Substitua o código gerado pelo seguinte código. Salve as alterações. 3. var map; 4. var marker; 5. var watchID; 6. 7. $(document).ready(function() { 8.
64. } 65. 66. function gotPosition(position) { 67.
map.setCenter(new
google.maps.LatLng(position.coords.latitude,
position.coords.longitude)); Grupo SoftwareOriginSP
Página 49
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
68. 69.
var
point
=
new
google.maps.LatLng(position.coords.latitude,
position.coords.longitude); 70.
if (!marker) {
71.
//create marker
72.
marker = new google.maps.Marker({
73.
position: point,
74.
map: map
75.
});
76.
} else {
77.
//move marker to new position
78.
marker.setPosition(point);
79.
}
80. } E depois de editado o código é só executar para ver o resultado.
Grupo SoftwareOriginSP
Página 50
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
7° Capítulo Conceitos finais JavaScript tornou-se, nos últimos anos, o herói subestimado do desenvolvimento de aplicativos da web. Esse reconhecimento vem sendo encarado com surpresa por desenvolvedores de software acostumados a considerar JavaScript uma "linguagem de brinquedo". Embora haja linguagens mais populares (no sentido de que desenvolvedores clamam sua lealdade a elas), o status exclusivo de JavaScript como a linguagem de script padrão, independente de navegador, fez com que ela pudesse perdurar. Para desenvolvimento na web no lado do cliente, é provavelmente a linguagem mais usada no mundo. JavaScript também tem lugar em scripts do lado do servidor, e esse nicho está crescendo. Embora tenha havido tentativas de JavaScript no lado do servidor no passado, nenhuma gerou tanta animação quanto Node.js, ou Node. Projetado para auxiliar desenvolvedores a criar programas de rede escaláveis, Node é um ambiente de programação do lado do servidor que praticamente reinventou JavaScript para uma nova geração de desenvolvedores. Para muitos desenvolvedores Java, a principal atração do Node é sua abordagem nova para a simultaneidade de software. Embora a plataforma Java continue evoluindo em sua abordagem da simultaneidade (com melhorias muito esperadas em Java 7 e Java 8), o fato é que Node atende uma necessidade, e faz isso da forma mais leve que muitos desenvolvedores Java adotaram recentemente. Como os scripts do lado do cliente com JavaScript, os scripts do lado do servidor no ambiente Node são bons porque funcionam, e funcionam em uma área na qual muitos desenvolvedores Java enfrentam desafios atualmente.
Grupo SoftwareOriginSP
Página 51
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Apresento a revolução dos scripts do lado do servidor que é o Node. Começaremos com uma visão geral arquitetural do que torna o Node especial, e em seguida eu mostrarei como desenvolver rapidamente um aplicativo da web escalável que usa MongoDB para persistência de dados. Você verá em primeira mão como o Node é divertido, e como ele pode ser usado rapidamente para montar um aplicativo da web funcional.
A simultaneidade conduzida por evento de Node
Node é um ambiente de E/S escalável, conduzido por evento, desenvolvido com base no mecanismo JavaScript V8 do Google. Google V8 na verdade compila JavaScript em código de máquina nativo antes da execução, o que resulta em desempenho extremamente rápido no tempo de execução — algo que não é geralmente associado a JavaScript. Dessa forma, Node permite desenvolver rapidamente aplicativos de rede extremamente rápidos e altamente simultâneos. E/S conduzida por evento pode soar estranho para um desenvolvedor Java, mas não é tão novo assim. Em vez do modelo de programação multiencadeado com o qual estamos acostumados na plataforma Java, a abordagem de Node para simultaneidade tem um único encadeamento, com a adição de um loop de eventos. Esse desenvolvimento do Node permite E/S sem bloqueio ou assíncrona. No Node, chamadas que geralmente bloqueariam, como espera por resultados de consulta de banco de dados, não bloqueiam. Em vez de esperar que atividades caras de E/S sejam concluídas, um aplicativo Node emite um retorno de chamada. Quando um recurso é retornado, o retorno de chamada anexado é chamado de forma assíncrona. A simultaneidade simplesmente funciona em programas Node. Se eu quisesse executar o cenário anterior na plataforma Java, poderia escolher entre abordagens complexas e demoradas — de encadeamentos tradicionais a bibliotecas mais novas em Java NIO e até o pacote java.util.concurrent melhorado e atualizado. Embora a simultaneidade de Java seja eficiente, pode ser difícil de entender — o que se traduz em código! Em comparação, o mecanismo de retorno de chamada do Node está integrado na linguagem; não são necessários desenvolvimentos especiais como synchronized para fazê-la funcionar. O modelo de simultaneidade do Node é extremamente simples, e isso o torna acessível para um público maior de desenvolvedores.
Grupo SoftwareOriginSP
Página 52
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
A sintaxe de JavaScript do Node também economiza bastante digitação. Com pouco código é possível desenvolver um aplicativo da web rápido e escalável, capaz de lidar com diversas conexões simultâneas. Isso pode ser feito na plataforma Java, mas é necessário mais linhas de código e diversas bibliotecas e desenvolvimentos adicionais. E não precisa se preocupar em navegar um ambiente de programação novo: Node é fácil de aprender se você sabe um pouco de JavaScript, e eu aposto que você sabe. Por que escolher Node.js? A abordagem da plataforma Java para simultaneidade ajudou a estabelecer sua posição de liderança no desenvolvimento corporativo, e isso não deve mudar. Estruturas como Netty (e Gretty; consulte Recursos) e bibliotecas de núcleo como NIO, além de java.util.concurrent, tornaram a JVM uma escolha preferencial para simultaneidade. O que o Node tem de especial é que se trata de um ambiente de desenvolvimento moderno especificamente projetado para resolver os desafios da programação simultânea. O paradigma de programação conduzido por eventos de Node faz com que bibliotecas adicionais não sejam necessárias para fazer a simultaneidade funcionar, e isso é uma boa notícia para desenvolvedores de olho em hardware com diversos núcleos. Express cuida de JSON JavaScript e JSON são parentes próximos, o que torna o gerenciamento de Json no Express o mais simples possível. Nesta seção, iremos incluir um pouco mais de código no aplicativo esqueleto da Listagem 2, para que possamos obter um documento JSON recebido e imprimi-lo na saída padrão. Após isso, iremos persistir tudo em uma instância do MongoDB. O documento recebido será semelhante à Listagem 3 (observe que eu omiti informações de local devido ao espaço): Lista 3. Comida de graça no Freddie Fingers! { "deal_description":"free food at Freddie Fingers", "all_tags":"free,burgers,fries" } A Listagem 4 inclui funcionalidade para analisar o documento recebido: Lista 4. Express analisa JSON app.use(express.bodyParser()); Grupo SoftwareOriginSP
Página 53
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
app.put('/', function(req, res) { var deal = req.body.deal_description; var tags = req.body.all_tags; console.log("deal is : " + deal + " and tags are " + tags); res.contentType('json'); res.send(JSON.stringify({ status: "success" })); }); Observe que a Listagem 4 inclui uma linha que orienta o Express a usar bodyParser. Isso nos permitirá facilmente (e eu realmente quero dizer facilmente) obter atributos de um documento JSON recebido. Dentro do retorno de chamada put há código para obter os valores dos atributos deal_description e all_tags de um documento recebido. Observe a facilidade com que podemos capturar os elementos individuais de um documento solicitado: neste caso, req.body.deal_description obtém o valor de deal_description. Teste! Também é possível testar essa implementação. Basta parar sua instância do magnus-server e reiniciá-la, e em seguida usar um PUT de HTTP para enviar um documento JSON para seu aplicativo Express. Primeiro, você deve ver uma resposta de sucesso. Em seguida, o Express deve registrar na saída padrão os valores enviados. Com meu documento de Freddie Fingers, eu recebo esta saída deal is : free food at Freddie Fingers and tags are free, burgers, fries. Persistência com Node Temos um aplicativo funcional que recebe um documento JSON, analisa-o e retorna uma resposta. Agora precisamos incluir alguma lógica de persistência. Como sou fã do MongoDB (consulte Recursos), eu optei por persistir os dados por meio de uma instância do MongoDB. Para tornar as coisas ainda mais vivas, iremos usar a biblioteca de terceiros Mongolian DeadBeef, que usaremos para armazenar os valores de documentos JSON recebidos.
Grupo SoftwareOriginSP
Página 54
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Mongolian DeadBeef é uma de várias bibliotecas do MongoDB disponíveis para Node. Eu a escolhi porque o nome me agrada, e porque o espelhamento do driver MongoDB nativo me faz sentir em casa. Agora você já sabe que a primeira etapa para usar o Mongolian DeadBeef é atualizar o arquivo package.json, como mostra a Listagem 5: Lista 5. Incluindo análise de JSON { "name": "magnus-server", "version": "0.0.1", "dependencies": { "express": "2.4.6", "mongolian": "0.1.12" } } Como estamos conectando com um armazenamento de dados do MongoDB, também é preciso atualizar as dependências sólidas do projeto executando npm install. Para melhorar o desempenho do driver do MongoDB do Mongolian DeadBeef, poderíamos também instalar o analisador bson C++ nativo, algo no qual o NPM pode nos ajudar. Para começar a usar o Mongolian DeadBeef, incluímos outro require na implementação atual, e em seguida conectamos a instância MongoDB desejada (como mostra a Listagem 6). Para esse exemplo, iremos conectar a uma instância hospedada no MongoHQ, um provedor de nuvem do MongoDB. Lista 6. Incluindo Mongolian DeadBeef no magnus-server var mongolian = require("mongolian"); var db = mongolian("mongo://a_username:[email protected]:23034/magnus");
new
Dentro do retorno de chamada PUT, persistimos os valores do documento JSON recebido, como mostra a Listagem 7: Lista 7. Incluindo lógica de inserção do Mongolian app.put('/', function(req, res) { var deal = req.body.deal_description; Grupo SoftwareOriginSP
Página 55
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
O que é a NPM do Node.JS NPM é o nome reduzido de Node Package Manager (Gerenciador de Pacotes do Node). A NPM é duas coisas: Primeiro, e mais importante, é um repositório online para publicação de projetos de código aberto para o Node.js; segundo, ele é um utilitário de linha de comando que interage com este repositório online, que ajuda na instalação de pacotes, gerenciamento de versão e gerenciamento de dependências.. A NPM já conta com mais de 35 mil pacotes (Jul-2013), são bibliotecas e aplicações de código aberto, e muitas são adicionadas todos os dias. Estas aplicações podem ser encontradas através do portal de busca da NPM. Uma vez encontrado o pacote que você deseja instalar, ele pode ser instalado com uma única linha de comando. Digamos que você está trabalhando duro um dia para desenvolver sua Próxima Grande Aplicação. Então você se depara com um problema e decide que está na hora de usar aquela biblioteca legal que você tem ouvido a respeito - vamos usar a biblioteca assincrona de Coalan McMahon como exemplo. Felizmente a NPM é muito simples de usar, você só precisa usar o comando npm install async, e o módulo específico será instalado no diretório atual dentro da pasta ./node_modules/. Uma vez instalada sua pasta node_modules, você será capaz de usar require() nela como se fossem módulos internos do seu projeto. Para compreender melhor a função require() eu aconselho que você leia o artigo que explica como funciona a função require do Node.js. Vamos ver um exemplo de pacote instalado globalmente - vamos falar do coffee-script. O comando para o NPM é simples: npm install coffee-script -g. Para usuários do Linux este comando irá instalar o progama e colocar um link simbólico (symlink) na pasta /usr/local/bin. Isto permitirá você rodar o programa diretamente do console como qualquer outra ferramenta CLI (Command-Line Interface). Neste caso, rodar o comando coffee te permitirá usar o REPL do coffee script. Outro importante uso da NPM é o gerenciamento de dependências. Quando você tem um projeto Node com um arquivo package.json, você pode rodar o comando npm install na pasta raiz do seu projeto e a NPM vai isntalar todas as dependências listadas no package.json. Isto faz a instalação de projetos Node de repositórios Git muito mais fáceis! Vamos usar o exemplo do vows, um dos frameworks de testes do Node, ele pode ser instalado através do Git, e suas dependências: eyes, diff e glob podem ser automáticamente instaladas utilizando uma única linha de comando da NPM. Grupo SoftwareOriginSP
Página 56
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Observe os comandos para clonar o projeto vows do github utilizando o git, e instalar suas dependências listadas no arquivo package.json: git clone https://github.com/cloudhead/vows.git cd vows npm install Após rodar estes comandos, você vai ver a pasta node_modules contendo todas as dependências especificadas no package.json. Como usar o util.inspect em NodeJS O NodeJS fornece uma útil função para propósitos de debugação que retorna uma representação em texto de um objeto. util.inspect pode ser um salva-vidas ao se trabalhar com propriedades de objetos grandes e complexos. O módulo util do NodeJS contém uma série de métodos utilizados para formatar texto, debugar, imprimir mensagens no console, checar objetos (util.isArray(objeto), util.isRegExp(objeto), util.isDate(objeto) e util.isError(objeto)) e um método de herança (util.inherits(construtor, superConstrutor)). Neste artigo veremos o método util.inspect. A seguir há um exemplo simples, util.inspect pode ser usado com qualquer objeto e uma boa demonstração pode ser usá-lo nos objetos embutidos do Node. Tente o código abaixo no REPL do Node. Caso você não conheça o REPL, ou não saiba como acessá-lo, eu te aconselho a ler nosso artigo explicando como usar o REPL do Node. > var util = require('util') undefined > util.inspect(console) '{ log: [Function],\n info: [Function],\n warn: [Function],\n error: [Function],\n dir: [Function],\n time: [Function],\n timeEnd: [Function],\n trace: [Function],\n assert: [Function] }' Como você pode se recordar, a cada comando passada para o REPL ele imprime o retorno do comando. O que nos interessa aqui é o retorno do comando util.inspect(console) que pode ser visto na linha abaixo deste comando. Podemos ver que o comando nos retornou uma string contendo todas as funções enumeráveis deste módulo. Então nós sabemos que a função util.inspect retorna uma string contendo as propriedades enumeráveis de qualquer objeto e também é válido acrescentar que a função console.dir é um envólucro em torno de util.inspect, utilizando seus argumentos padrões e imprimindo seu retorno na tela. Ao se imprimir o retorno do util.inspect utilizando a função console.log o console utilizará a formatação contida na string, como a quebra de linhas e a exibição de cores. > console.log(util.inspect(console)) { log: [Function], info: [Function], warn: [Function], error: [Function], dir: [Function], time: [Function], timeEnd: [Function], Grupo SoftwareOriginSP
Página 57
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
trace: [Function], assert: [Function] } undefined util.inspect pode também ser passada com vários argumentos opcionais, apresentados aqui com seus valores padrões: util.inspect(object, showHidden=false, depth=2, colors=false, customInspect=true); Por exemplo, util.inspect(meuObjeto, true, 7, true, false) poderia inspecionar meuObjeto, mostrando todos as propriedades ocultas e não ocultas até a profundidade 7, colorir a saída e a função inspect() definida no objeto não será chamada. O argumento opcional depth (profundidade em inglês) é o número de níveis de profundidade de um objeto que será feita a recursão, seu valor padrão é 2. Isso é útil para objetos grandes e complicados. Definindo este valor como null vai forçar a recursão até atingir o último nível de profundidade. Para observar seu comportamento você pode inserir os comandos a seguir no REPL do Node. var http = require('http'); console.log(util.inspect(http, false, 1)); console.log(util.inspect(http, false, 2)); O argumento opicional showHidden é um booleano que determina se vai ou não exibir as propriedades não-enumeradas de um objeto, seu valor padrão é false o que tende a resultar em saídas muito mais legíveis. Isso não é algo que um iniciante deve se preocupar na maior parte do tempo, mas é válido demonstrar rapidamente. Você poderá perceber a diferença executando os comandos a seguir no REPL do Node. console.log(util.inspect(console, false, 1)) console.log(util.inspect(console, true, 1)) O argumento opicional color também é um valor booleano que diz se a função deve, ou não, decorar a string de saída com códigos de cores ANSI, as cores são customizáveis. Basicamente ela faz com que, na janela do terminal, a string de retorno possa ser impressa em cores. Faça o teste utilizando o comando a seguir. var util = require('util'); console.log(util.inspect(console, false, 2, true)); Módulos do Núcleo do Node Primeiro eu recomendo que uma versão do node esteja instalada no seu computador. Um modo fácil para que isso aconteça e visitando nodejs.org e clique em Install. Node tem um pequeno grupo de módulos que vem com ele por padrão (comumente chamados como ‘núcleo do node’) eles são representados por suas API’s publicas e você utiliza elas para escrever os seu programas. Para trabalhar com sistema de arquivos existe o módulo fs e para redes os módulos são net (TCP), http, dgram (UDP). Em adição ao fs e os módulos de rede existem outros módulos na base do núcleo do node. Lá também temos um modulo assincrono para resolver consultas de DNS chamado dns, um módulo para pegar informações especificas do sistema, por exemplo o tmpdir e ele chama os, Grupo SoftwareOriginSP
Página 58
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
um outro para alocação de pedaços de binários na memória chamado buffer, varios módulos para análise de urls e caminhos (url, querystring, path) entre outros. A maioria dos módulos presentes no núcleo do node suportam os principais casos de uso dele: escrever rapidamente programas que conversem com os sistema de arquivos ou rede. Node lida com I/O com: callbacks, eventos, streams e módulos. Se você aprende como essas quatro coisas trabalham vocês esta habil para ir em quanlquer módulo do core do node a ter um entendimento básico sobre como a interface funciona com ele. Evoluindo de Forma Granular em Node Como todo boa ferramenta, o Node é bem adequado para certos casos de uso. Por exemplo: Rails, o popular web framework, é ótimo para modelar complexas lógicas de negócio, ex. usando código para representar a vida em um plano objetivado que vivemos físicamente como contas, empréstimos, itinerários e inventários. Embora técnicamente seja possível fazer o mesmo utilizando o Node, haveria desvantagens claras sabendo que o Node é projetado para resolver problemas de I/O e não saber sobre a parte da ‘lógica de negócio’. Cada ferramenta tem seu foco voltado a resolver diferentes problemas. Esperamos que este guia ajude-o a ganhar uma compreensão intuitiva dos pontos fortes do Node e você saberá quando ele será útil. O que está fora do escopo do Node? Fundamentalmente o Node é somente usado como ferramenta para gerenciar I/O ao redor do sitema de arquivos e redes, ele deixa outras funcionalidades mais bonitas com módulos de terceiros. Estas são algumas das coisas fora do escopo do Node:
Web frameworks Existe uma boa quantidade de web frameworks construidos em cima do Node (framework é um pacote que tenta resolver um problema de alto nível e problemas similares ao modelar a lógica de negócio), mas o Node não é um framework para web. Frameworks web são escritos para serem utilizado no Node e nem sempre tomam o mesmo tipo de decisões sobre a adição de complexidade, abstração e compreensão que o Node faz e podem ter outras prioridades.
Sintaxe da linguagem O Node usa JavaScript e não muda nada sobre isso. Felix Geisendörfer tem um belo conteúdo escrito sobre o ‘estilo de escrita do Node’ aqui.
Abstração da linguagem Quando possível o Node vai usar a maneira mais simples de escrever algo. Código mais ‘bonito’ faz do seu JavaScript mais complexo e compromissado com vantagens e desvantagens. Programar é difícil, especialmente em JS onde você tem 1000 soluções para o mesmo Grupo SoftwareOriginSP
Página 59
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
problema! Essa é a principal razão para o Node optar pela simplicidade sempre que possível e que pode ser uma opção universal. Se você está resolvendo um problema complexo e você está insatisfeito com o modo que o Node implementa as coisas com soluções originais do JS, sinta-se livre para resolver isso dentro do seu app ou módulo usando quaisquer abstrações que você preferir. Um grande exemplo de como o Node usa os callbacks. Logo no início foi experimentado a caracteristica chamada ‘promessas’ que adiciona algumas funcionalidades para fazer o código assíncrono parecer mais linear. Ele foi levado para o fora do núcleo do Node por algumas razões:
eles são mais complexos que callbacks ele podem ser implementados na userland (distriuído no npm como módulo de terceiros) Considerando uma das mais universais e básicas ideias que o Node faz: ler um arquivo. Quando você lê um arquivo e precisa saber onde os erros acontecem, como exemplo quando o disco rígido morre no meio da sua leitura. Se você tem promessas tudo que você terá é um código como esse: fs.readFile('movie.mp4') .then(function(data) { // faça algo com os dados }) .error(function(error) { // lide com o erro }) Isso adiciona uma complexidade, desnecessária. No lugar de duas funções separadas o Node somente usa uma única função de callback. Aqui temos as regras:
Quando não existir erros passe null como primeiro argumento Quando o existir erro, passar ele como primeiro argumento O restante dos argumentos são usados para qualquer coisa (usualmente dados ou respostas já que na maior parte do tempo o Node está lendo ou escrevendo coisas) Por isso, o Node usa o estilo de callback: fs.readFile('movie.mp4', function(err, data) { // lide com o erro, e faça algo com os dados }) Eventos em Node No node com o módulo de eventos você pode disparar eventos, também chamando de ‘emissor de evento’, até mesmo o node usa em todas as suas APIs para emitir coisas. Grupo SoftwareOriginSP
Página 60
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Eventos são padrões comuns de programação, para conhecer melhor procure por ‘observer pattern’ ou ‘pub/sub’ (publicar/assinar). Ao passo que callbacks são uma relação um-para-um entre algo que espera pelo callback e outra parte que chama o callback, eventos seguem o mesmo padrão com exceção de que eles são uma API de muitos-para-muitos. Aqui temos casos comuns de uso dos eventos ao invés de simples callbacks:
Uma sala de chat onde você tem um canal de mensagens com muitos ouvintes. Servidor de um jogo que necessita saber quando os players se ligam, desligam, movem-se, atiram ou pulam Conectores de bancos de dados podem precisar saber onde suas conexões estão abertar, fechadas ou enviando um erro
Se você tentar escrever um servidor de chat que se conecte usando apenas callbacks ele irá parecer com isso: var chatClient = require('my-chat-client') function onConnect() { // exibe a UI quando conectar-se } function onConnectionError(error) { // exibe erros para o usuario } function onDisconnect() { // avisa ao usuario que ele foi desconectado } function onMessage(message) { // exibe a mensagem na UI da sala } chatClient.connect( 'http://mychatserver.com', onConnect, onConnectionError, onDisconnect, onMessage ) Como você pode ver é realmente pesado escrever porque você tem que passar todas as funções em uma ordem especifica para a função .connect. Escrevendo isso com eventos irá se parecer com isso: var chatClient = require('my-chat-client').connect() chatClient.on('connect', function() { // exibe a UI quando conectar-se }) chatClient.on('connectionError', function() { Grupo SoftwareOriginSP
Página 61
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
// exibe erros para o usuario }) chatClient.on('disconnect', function() { // avisa ao usuario que ele foi desconectado }) chatClient.on('message', function() { // exibe a mensagem na UI da sala }) Esta abordagem é bastante similar a utilização com callbacks-puros mas essa abordagem introduz o método .on, onde é atrelado um callback ao evento. Isso significa que você escolhe o que tem que estar assinado para chatClient. Você é capaz de assinar o mesmo evento várias vezes com diferentes callbacks: var chatClient = require('my-chat-client').connect() chatClient.on('message', logMessage) chatClient.on('message', storeMessage) function logMessage(message) { console.log(message) } function storeMessage(message) { myDatabase.save(message) Callbacks em Node É um dos tópicos mais importantes para se entender se você tem que entender como utilizar o node. Quase tudo no node usa callbacks. Eles não foram inventados para o node, somente tem um uso particular com funções do JavaScript. Callbacks são funções que serão executadas de modo assincrono, ou posteriormente. Ao passo que o código for lido de cima para baixo de modo processual, programas assincronos possivelmente executam funções em tempos diferentes baseado na ordem e velocidade em que requisições http ou o trabalho de arquivamento do sistema acontecem. A diferença pode ser confusa sendo determinada quando uma função é assincrona ou não depende da execução de um grande contexto. Seque um simples exemplo de código assincrono: var myNumber = 1 function addOne() { myNumber++ } // define a função addOne() // executa a função console.log(myNumber) // mostra na saida padrão o numero 2 O código acima define uma função e na linha seguinte chama essa função, sem esperar por nada. Onde ela é chamada imediatamente adicionando 1 a váriavel myNumber, então depois que a função foi chamada você espera que o numero seja 2 na váriavel myNumber.
Grupo SoftwareOriginSP
Página 62
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Supondo que precisamos armazenar nosso numero em um arquivo chamado number.txt: var fs = require('fs') // require é uma função especial do node para requisitar os módulos var myNumber = undefined // não sabemos ainda qual valor esta armazenado no arquivo function addOne() { fs.readFile('./number.txt', function doneReading(err, fileContents) { myNumber = parseInt(fileContents) myNumber++ }) } addOne() console.log(myNumber) // mostra na saida padrão `undefined` Porque é que temos undefined quando pedimos para mostrar o numero dessa vez? Nesse código usamos o método fs.readFile, que acontece de módo assincrono. Usualmente você tem que se comunicar com os discos rigidos ou redes bem de modo assincrono. Se for somente para acesso a memória ou fazer algo na CPU isso é feito bem de modo sincrono. A razão pela qual fazer o I/O assincrono é porque o acesso ao disco é 100,000 vezes mais devagar do que comunicar-se com a memória (RAM). Onde você executa um programa onde as funções são imediatamente definidas, mas não executa elas imediatamente. Este é um conceito fundamental para entender sobre a programação assincrona. Onde addOne é chamada fora de fs.readFile e move-se para a proxima tarefa pronta para ser executada e não esperar o disco responder igual a fs.readFile. Se não tem nada para executar o node espera por pendências de operações fs/network terminarem ou pararem de executar saindo da linha de comando. Onde fs.readFile esta completa para ler o arquivo (talves isso demore millisegundos ou segundos e até mesmo minutos dependendo de quanto o disco é rápipdo) executando a função doneReading e dando um erro (claro que se algum tipo de erro acontecer) e o conteúdo do arquivo. A razão pela qual undefined foi mostrado no código acima é que ele é chamado dentro de console.log e fora de fs.readFile mostrando o valor anterior de myNumber e não o conteúdo do arquivo. Se você tem um pedaço de código que precisa ser executado várias e várias vezes ou um tempo depois, o primeiro passo é colocar esse pedaço de código dentro de uma função. Aonde você podera chamar sem precisar escrever ele em todas as partes que for necessário e nomeando da meneira que fique claro aquilo que esta sendo feito. Isso ajuda a dar nomes descritivos para as funções. Callback são somente funções que são executadas de forma tardia. A chave para a compreensão de callbacks é perceber que eles são utilizados quando você não sabe quando alguma operação assincrona estará completa, mas você sabe quando se completará - a ultima linha da função assincrona! A ordem de cima-para-baixo que você declara para callbacks isso não é necessariamnete importante, somente a ordem lógica/hierárquica de assentamento do código. Primeiro divida o código em funções e use callbacks para declarar se uma depende da outra função para encerrar. O método fs.readFile é fornecido pelo node no módulo fs, é assincrono e acontece quando se tem um tempo curto para finalizar. Considerando o que ele faz: ele vai até o sistema operacional, que por sua vez esta rodando em um disco rígido isso pode ou não estar girando a Grupo SoftwareOriginSP
Página 63
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
milhares de rotações por minuto. Então ele tem que usar o lazer para ler os dados e enviar atráves das camadas de comunição do sistema de volta para o seu programa em javascript. Você da ao fs.readFile uma função (conhecida como callback) que sera chamada depois de recuperar os dados do sistema de arquivos. Ela coloca os dados recuperados em uma variavel do javascript e passa para sua função (callback) com essa variavél, neste caso a variavel se chama fileContents porque ela contém o conteúdo do arquivo que foi lido. Pense no restaurante do exemplo do tutorial lá do inicio. Em muitos restaurantes você pega um número e coloca em sua mesa e espera por sua comida. Eles são como os callbacks. Isso deixa claro para o servidor a quem chamar quando o cheeseburger estiver pronto. Colocando console.log em uma função e passando ele para um callback. var fs = require('fs') var myNumber = undefined function addOne(callback) { fs.readFile('./number.txt', function doneReading(err, fileContents) { myNumber = parseInt(fileContents) myNumber++ callback() } } function logMyNumber() { console.log(myNumber) } addOne(logMyNumber) Agora a função logMyNumber que é passada como argumento se torna a variavél callback dentro da função addOne. Depois que fs.readFile terminar a variavél callback é chamada (callback()). Somente funções podem ser chamadas, então se você passar qualquer outra coisa diferente de uma uma função, causará um erro. Onde uma função é chamada no javascript o código dentro dessa função é imediatamente executado. Neste caso nosso log é executado onde callback é atualmente a função logMyNumber. Lembre-se, somente se você definiu uma função não significa que ela será executada. Você tem que chamar uma função para ela acontecer. Para quebrar esse exemplo em mais pedaços, aqui tem uma linha do tempo de eventos que acontecem quando o seu programa é executado:
1: o código é analisado, isso significa que se existir algum erro de sintaxe o pragrama quebrará e será apontado aonde isso aconteceu. 2: addOne será chamado, onde logMyNumber será passado com uma função chamada callback, que é o que precisa ser chamado quando addOne estiver completa. Imediatamante disparando o método assincrono fs.readFile. Essa parte do programa leva um tempo para terminar. 3: com nada para fazer, o node espera por um tempo até o fs.readFile encerrar a sua execução. 4: fs.readFile termina e chama o callback, doneReading, que incrementa o numero e imediatamente chama a função logMyNumber contida na variável callback.
Grupo SoftwareOriginSP
Página 64
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Talves a parte mais confusa de se programar com callbacks é que as funções são somente objetos armazenados em variáveis e passadas em todo o programa com diferentes nomes. Dando nomes simples e descritivos para suas variáveis faz seu código ser mais legível para outros. Geralmente falando em programas no node onde você enxerga uma variável como callback ou cb você assume ela como uma função. Você talves tenha escutado alguns termos como programação evencionada ou ciclo de eventos. Onde é referenciado da mesma maneira que fs.readFile foi implementada. Node primeiramente despacha a operação fs.readFile e espera por fs.readFile enviar um evento para concluir. Equanto a resposta é esperada o node vai buscando checar outras coisas. Dentro do node há uma lista de coisas a serem feitas mas não informaram ainda, então o ciclo do node acaba e retorna para a lista várias vezes checando se o que estava sendo processado terminou. Depois do termino ele pega o que foi ‘processado’, ex. callbacks que dependem desse termino são chamados. Aquie temos uma versão de um pseudocódigo do exemplo acima: function addOne(thenRunThisFunction) { waitAMinute(function waitedAMinute() { thenRunThisFunction() }) } addOne(function thisGetsRunAfterAddOneFinishes() {}) Imagine que tenha 3 funções assincronas a, b e c. Para cada uma leva-se 1 minuto de execução e depois de terminado elas chamam um callback (que é passado como primeiro argumento). Se você tem que falar para o node ‘comece executando a, depois b depois que a terminar, e executar c então b termina’ isso passa a ser: a(function() { b(function() { c() }) }) Onde esse código será executado, a é imediatamente executado, um minuto depois que ele teminar e chama b, outro minuto depois que ele teminar e chamar c e finalmente depois que 3 minutos se passaram o node para de executar desde que não tenha mais nada para fazer. Isso é definitivamente a maneira mais elegante para escrever o código acima, mas o ponto é esse, se você tem esse código que espera por uma porção de código assincrono terminar, onde é expressado essas dependencias colocando seu código em funções, isso é passado às outras funções como callbacks. A projeção do node requer que você pense de modo não-linear. Considerando essa lista de operações: ler um arquivo processar um arquivo Se você ingênuamente transforma-se isso em um pseudocódigo você teria este resultado: var file = readFile() processFile(file) Grupo SoftwareOriginSP
Página 65
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Esse tipo de linearidade no código (passo-a-passo, em ordem) não é o modo como o node trabalha. Se esse código fosse executado onde readFile e processFile estão sendo chamados ao mesmo tempo. Não faria o menor sentido porque readFile leva um tempo para completar sua execução. Ao passo que você precisa expressar processFile que depende do readFile completo. Esta a exata finalidade dos callbacks! E por causa da forma que o JavaScript trabalha você pode escrever dependencias de diferentes maneiras: var fs = require('fs') fs.readFile('movie.mp4', finishedReading) function finishedReading(error, movieData) { if (error) return console.error(error) // faça algo com os dados em movieData } Mas você támbem pode estruturar o código dessa maneira que irá funcionar: var fs = require('fs') function finishedReading(error, movieData) { if (error) return console.error(error) // faça algo com os dados em movieData } fs.readFile('movie.mp4', finishedReading) Ou até mesmo assim: var fs = require('fs') fs.readFile('movie.mp4', function finishedReading(error, movieData) { if (error) return console.error(error) // faça algo com os dados em movieData }) Depurando (Debugando) Node.js com o Depurador embutido. Se a versão web do seu aplicativo está funcionando, porém a versão de linha de comando está dando erro. O que está acontecendo de errado? Pode ser difícil de descobrir. Se a aplicação simplesmente terminou sem imprimir nada. Seus try/catch e manipuladores de evento não identificaram nenhum erro e a típica abordagem por console.log() não imprime nada. Então o melhor a se fazer é usar o depurador do Node.js para fazer a depuração do seu código. Uns até falam debugar o código, debugar é uma palavra derivada da palavra em Inglês debugging (ou simplesmente debug). O motor V8 da Google, onde o Node roda, vem com um extenso depurador acessível via protocolo TCP e o Node.js tem um cliente embutido para este depurador. Este é considerado o depurador (cliente de depuração) do Node. Este artigo ensina como usar o depurador do Node para depurar seu script expondo como colocar pontos de interrupção (breakpoints), rodar o REPL do Node no meio da depuração, percorrer pelo código e analizar as saídas do depurador.
Grupo SoftwareOriginSP
Página 66
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
O básico
O motor V8 da Google tem um depurador embutido e o Node tem um cliente para acessar este depurador O depurador pode ser iniciado usando executando o node com o argumento debug Na sua aplicação você pode definir pontos de interrupção usando a palavra reservada debugger Você pode adicionar vigilantes para acompanhar o valor de expressões durante a depuração
Agora vamos a cada etapa da debugação. Inserindo alguns pontos de interrupção Um ponto de interrupção (breakpoint) é um lugar em seu codigo onde você deseja que sua aplicação pare momentaneamente para que você possa analisar o estado do seu programa naquele ponto do código. Se você não usar um ponto de interrupção você vai ter percorrer o código passo-a-passo até chegar onde você deseja depurar. Então, basicamente, seu script você vai precisar colocar alguns pontos de interrupção em lugares próximos onde você acha que o seu sistema esteja se comportando de maneira indevida. Iniciando o depurador Para iniciar o depurador, rode seu script usando o argumento debug do node, no nosso exemplo: node debug depurar.js. Você vai ser levado ao REPL do depurador debug > e então você vai poder usar o comando help para ver a lista de comandos disponíveis e começar e debugar seu código. Quando você abre o depurador seu inicia pausado na primeira linha e você precisa usar o comando cont para rodar seu código até ser interrompido quando o depurador encontrar a palavra reservada debugger ou você usar o comando pause. $ node debug depurar.js < debugger listening on port 5858 connecting... ok break in /home/rafadev/git/depurador/depurar.js:1 1 x = 5; 2 setTimeout(function () { 3 debugger; debug> cont < Ola break in /home/rafadev/git/depurador/depurar.js:3 1 x = 5; 2 setTimeout(function () { 3 debugger; 4 console.log("mundo"); Grupo SoftwareOriginSP
Página 67
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
5 }, 1000); debug> next break in /home/rafadev/git/depurador/depurar.js:4 2 setTimeout(function () { 3 debugger; 4 console.log("mundo"); 5 }, 1000); 6 console.log("Ola"); debug> repl Press Ctrl + C to leave debug repl >x 5 > 2+2 4 debug> next < mundo break in /home/rafadev/git/depurador/depurar.js:5 3 debugger; 4 console.log("mundo"); 5 }, 1000); 6 console.log("Ola"); 7 debug> quit O comando next avalia a próxima linha do seu código e o comando repl permite você avaliar o estado atual do seu programa remotamente através do REPL do Node. Há também outros comandos disponíveis, lembrando que você pode digitar help para lembrar dos comandos disponíveis. Vigilantes (Watchers)
Você pode vigiar o valor de uma expressão ou de uma variável enquanto depura seu código. Em cada ponto de interrupção os vigilantes serão avaliados no contexto atual e seus valores serão impressos logo depois da origem da interrupção. Grupo SoftwareOriginSP
Página 68
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Para começar a vigiar uma expressão, insira watch("sua_expressao"). O comando watchers imprime os vigilantes ativos. Para deixar de vigiar uma expressão, insira unwatch("sua_expressao").
Movimentação com next, step e out O comando next, como já foi dito, prossegue a execução do programa avaliando a linha a atual e pausando na primeira expressão da próxima linha. Caso a linha a ser avaliada faça referência a algum método de outro módulo, ou até mesmo do core, você também pode depurar o funcionamento interno deste método, basta utilizar a função step que é o abreviamento para “step into this one” que pode ser traduzido para “entrar neste” que basicamente entra para depurar o método internamente. Quando você está depurando internamente o funcionamento deste método, você pode ir utilizando os comandos next e step para rastrear com detalhes o que está ocorrendo “por trás dos panos” depurando até o próprio core do Node, caso você deseje. Já o comando out serve para você voltar para o código que chamou aquele método e pausar para a próxima expressão. Lista de Comandos Movimentação
cont, c - Continuar a execução next, n - Próximo passo step, s - Entrar out, o - Sair
pause - Pausar a execução de um código (como o botão de pausar nas ferramentas de desenvolvimento)
Pontos de interrupção
setBreakpoint(), sb() - Setar um ponto de interrupção na linha corrente setBreakpoint(linha), sb(linha) - Setar um ponto de interrupção em uma linha específica setBreakpoint('fn()'), sb(...) - Setar um ponto de interrupção na primeira instrução no corpo da função setBreakpoint('script.js', 1), sb(...) - Setar um ponto de interrupção na primeira linha do script.js clearBreakpoint, cb(...) - Remover um ponto de interrupção
Também é possível inserir um ponto de interrupção em um arquivo (módulo) que ainda não foi carregado, por exemplo usando o comando setBreakpoint('mod.js', 23) adiciona um ponto de interrupção no módulo mod.js na linha 23. $ node debug main.js < debugger listening on port 5858 connecting to port 5858... ok break in /home/rafadev/git/depurador/main.js:1 Grupo SoftwareOriginSP
Página 69
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
1 var mod = require('./mod.js'); 2 mod.hello(); 3 mod.hello(); debug> setBreakpoint('mod.js', 23) Warning: script 'mod.js' was not loaded yet. 1 var mod = require('./mod.js'); 2 mod.hello(); 3 mod.hello(); debug> c break in /home/rafadev/git/depurador/mod.js:23 21 22 exports.hello = function() { 23 return 'hello from module'; 24 }; 25 debug> Informação
backtrace, bt - Imprime um registro de chamadas do quadro atual de execução list(5) - Lista o código fonte do script com 5 linhas de contexto (5 linhas antes e 5 depois) watch(expr) - Adiciona uma expressão para a lista vigiada unwatch(expr) - Remove uma expressão da lista vigiada watchers - Lista todos os vigiados e seus valores (atumaticamente listado em cada ponto de interrupção) repl - Abre o REPL de debugação para avaliação no contexto de depuração do script
Controle de execução
run - Executa o script (é automaticamente executado quando o depurador é iniciado) restart - Reinicia um script kill - Mata um script
Diversos
scripts - Lista todos os scripts carregados version - Imprime a versão da engine V8
Grupo SoftwareOriginSP
Página 70
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Vantagens de uso O depurador da V8 pode ser abilitado e acessado tanto iniciando o Node com o sinalizador (flag) de linha de comando --debug como sinalizando um processo existente em Node com SIGUSR1. Uma vez que o processo foi colocado em modo de depuração dessa forma ele pode ser conectado pelo depurador do node. Tanto conectado pelo pid quanto pelo URI para o depurador. A sintaxe é: node debug -p - Conecta com o processo via pid
node debug - conecta com o processo via URI tal como localhost:5858
Primeiros passos com Passport e Express em Node.js Passport é um middleware para Node.js que faz a implementação de autenticação em um aplicativo de maneira rápida e fácil. Em aplicações web modernas podem-se ter várias formas de autenticação. Tradicionalmente usuários se logam fornecendo um usuário e uma senha. Com o crescimento das redes socieais, logar-se com um provedor OAuth como o Facebook ou o Twitter tem se tornado métodos populares de autenticação. Passport reconhece que cada aplicação tem requisítos únicos de autenticação. Mecanismos de autenticação, conhecidos como estratégias, são empacotados como módulos individuais. As aplicações podem escolher qual estratégia empregar sem criar dependências desnecessárias. Sabendo que muitos usuários preferem se logar utilizando uma já conta existente em uma rede social, como o Facebook ou Twitter. Neste exemplo vai ser implementado o suporte para o usuário se logar com a sua conta do Twitter. Instalando as dependências $ npm install express $ npm install passport $ npm install passport-twitter $ npm install connect-ensure-login O exemplo consiste em uma aplicação Express simples, baseada na aplicação desenvolvida passo a passo do artigo anterior, então para melhor acompanhamento se certifique de ter compreendido os conceitos básicos já apresentados. Passport tem uma arquitetura modular que quebra o mecanismo de autenticação em estratégias (neste caso do Twitter) que são distribuídas separadamente, mantendo o núcleo leve. Também será usado o connect-ensure-login para proteger rotas exclusivas para usuários logados. Checklist da autenticação São necessários verificar três itens para implementar a autenticação.
Configurar o middleware de sessão Configurar as estratégias de autenticação Adicionar as rotas de autenticação
Estes tópicos serão detalhados a seguir, começando por: rotas para o middleware.
Grupo SoftwareOriginSP
Página 71
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Rotas para autenticação Vamos adicionar rotas para /account, que mostra para a pessoa detalhes da conta. app.get('/account', ensureLoggedIn('/login'), function(req, res) { res.send('Ola '+ href="/logout">Logout ');
Uma vez que você tenha as chaves, configure a estratégia de autenticação do Twitter assim: var TWITTER_CONSUMER_KEY = "INSIRA_SUA_KEY_AQUI"; var TWITTER_CONSUMER_SECRET = "INSIRA_SUA_SECRET_AQUI";
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
function(token, tokenSecret, profile, done) { // NOTA: Voce tera, provavelmente, que associar o usuario do Twitter //
com um registro do usuario no banco de dados da aplicacao.
var user = profile; return done(null, user); } )); Esta função fornecida para a estratégia é conhecida como “callback de verificação”. Callbacks de verificação recebem credenciais como argumentos (neste caso o token , tokenSecret e profile ), que são usados para localizar e retornar registros do usuário. A instància user retornada vai ser definida no request em req.user para identificar o usuário logado. Na maioria das aplicações, você vai precisar associar a conta do Twitter com um registro do usuário no banco de dados da aplicação. Isso permite, também, você associar outras contas (como as do Facebook) no mesmo usuário, permitindo eles se logarem usando ambos os serviços. Para manter este exemplo simples vai ser utilizado os dados do profile diretamente, dispensando o uso de associações com o banco de dados. O protocolo OAuth usado pelo Twitter envolve um processo de dois passos usando redirecionamentos para a troca e verificação dos tokens. Isto é bastante complicado, mas o middleware Passport faz esta tarefa fácil. Apenas adicione as seguintes rotas: app.get('/auth/twitter', passport.authenticate('twitter')); app.get('/auth/twitter/callback', passport.authenticate('twitter', { successReturnToOrRedirect: '/account', failureRedirect: '/login' })); A primeira rota vai começar a transação OAuth e redirecionar o usuário para o Twitter. Uma vez logado, o Twitter vai redirecionar o usuário de volta para nossa aplicação e Passaport vai trazê-lo de volta para a página original requisitada (ou /). Fácil de mais, mas ainda há mais uma coisa a se fazer. Configurando as sessões A fim de manter usuário logado em nosso sistema, uma aplicação precisa implementar suporte para sessões. Faça isso usando o cookie parser e o middleware de sessões embutidos no Express, e inicializando o Passport. app.use(express.static(__dirname + '/public')); app.use(express.cookieParser()); app.use(express.session({ secret: 'usado para calcular o hash' })); app.use(passport.initialize()); app.use(passport.session()); Grupo SoftwareOriginSP
Página 73
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Quando o usuário se logar o registro do usuário é armazenado na sessão a fim de manter o estado de logado enquanto ele nevega em seu site. Funções de serialização e deserialização são fornecidas para controlar este processo. passport.serializeUser(function(user, done) { done(null, user); });
passport.deserializeUser(function(obj, done) { done(null, obj); }); Como foi notado acima, se você está criando registros do usuário em seu próprio banco de dados, você pode serializar somente o ID do usuário para minimizar a quantidade de dados armazenados na sessão. Para manter este exemplo simples, todo o registro do usuário foi serializado. Rodando a aplicação Para rodar nossa aplicação basta savá-la em um arquivo, neste exemplo chamado app.js e executá-la passando o nome do arquivo como parâmetro para o node: $ node app.js Seridor Express iniciado na porta 3000 E para confirmar que tudo está funcionando você pode acessar a sua aplicação através de seu navegador pelo endereço http://localhost:3000. O código completo da aplicação Abaixo segue o código completo da aplicação, basta copiar e colar em um arquivo JavaScript, alterar as strings INSIRA_SUA_KEY_AQUI e INSIRA_SUA_SECRET_AQUI com os dados do seu registro no Twitter e tudo funcionará corretamente.
app.use(express.static(__dirname + '/public')); app.use(express.cookieParser()); app.use(express.session({ secret: 'usado para calcular o hash' })); Grupo SoftwareOriginSP
Página 74
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
var server = app.listen(3000); console.log('Seridor express iniciado na porta %s', server.address().port); Como funciona a função require do Node.js O Node.js segue a CommonJS, uma especificação de ecossistemas para o JavaScript, e a função embutida require é a maneira mais fácil de incluir módulos existentes em arquivos separados. O funcionamento básico do require é que ele lê o arquivo JavaScript, interpreta o script e em seguida retorna o conteúdo do objeto exports. Segue um exemplo de módulo para melhor compreensão:
console.log("Avaliando o exemplo.js");
Grupo SoftwareOriginSP
Página 76
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
var invisivel = function () { console.log("invisivel"); }
exports.mensagem = "Oi";
exports.falar = function () { console.log(exports.mensagem); } Podemos testar nosso módulo rodando através do REPL do node. Se formos na pasta onde salvamos nosso projeto exemplo.js com o prompt e abrirmos o REPL do node, para isso basta digitar node sem passar nenhum arquivo como parâmetro e ele abrirá em modo REPL, em seguida basta digitar require("./exemplo.js") para importar nosso módulo. Teremos a seguinte saída impressa: > require("./exemplo.js") Avaliando o exemplo.js { mensagem: 'Oi', falar: [Function] } Aqui é possível ver que o módulo foi importado com sucesso, e como foi dito a função require primeiro interpretou o script, imprimindo Avaliando o exemplo.js na primeira linha, e em seguida retornando o conteúdo do objeto exports , que contém apenas dois objetos { mensagem: 'Oi', falar: [Function] } . A função invisivel não foi importada pois não foi atribuída ao objeto exports . Porém caso quisermos utilizar a função falar de nosso módulo veremos que ela não estará disponível no contexto global, e a mensagem de erro dirá que falar não está definida. Para que seja possível utilizar as funções importadas de um módulo temos que importá-lo e atribuir o retorno da função require à uma variável. Se rodarmos no REPL o código var exemplo = require('./exemplo.js') ocorrerá que agora nosso script exemplo.js será avaliado e o objeto exemplo receberá { mensagem: 'Oi', falar: [Function] } , que é o retorno da função, assim é possível facilmente acessar sua função falar . Acompanhe o exemplo: > exemplo = require("./exemplo.js") Avaliando o exemplo.js { mensagem: 'Oi', falar: [Function] } > exemplo.falar() Oi
Grupo SoftwareOriginSP
Página 77
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
undefined Pode ser que em seu console não apareça a mensagem Avaliando o exemplo.js novamente, você seberá o motivo mais adiante. Se você quiser atribuir uma função ou um novo objeto para exports , então você terá que usar o objeto module.exports . Veja o exemplo do código exemplo2.js para compreender melhor. module.exports = function () { console.log("Ola mundo") }
require('./exemplo2.js')() Neste exemplo ao importar o módulo com require , primeiramente ele interpreta (avalia, executa) o código o que faz o módulo importar e chamar a sí próprio e ao final require retorna a função atribuida ao exports . Ao importar o módulo temos a saída esperada: > require('./exemplo2.js') Ola mundo [Function] A
última
linha
[Function]
confirma
que
o
valor
de
retorno
da
chamada
require('./exemplo2.js') é uma função e a linha Ola mundo confirma que o código foi interpretado antes de ser retornada esta função. É importante dizer, também, que o código é interpretado apenas na primeira vez que ele é importado, se você importá-lo mais de uma vez a função require irá reutilziar o objeto exports que já está em salvo cache. Para ilustrar este ponto acompanhe o exemplo abaixo: > require("./exemplo.js") Avaliando o exemplo.js { mensagem: 'Oi', falar: [Function] } > require("./exemplo.js") { mensagem: 'Oi', falar: [Function] } > require("./exemplo.js").mensagem = "Ola" 'Ola' > require("./exemplo.js") { mensagem: 'Ola', falar: [Function] } > require("./exemplo.js").falar() Ola undefined Grupo SoftwareOriginSP
Página 78
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Como pode ser visto no exemplo, exemplo.js é interpretado apenas na primeira vez e todas as chamadas posteriores à require() só invoca o módulo salvo em cache, em vez de ler o arquivo novamente. No exemplo é possível ver que isso pode causar efeitos colaterais caso utilizado indevidamente. As regras que o require usa para buscar os arquivos pode ser um pouco complexa, mas uma regra de ouro é que se o arquivo não inicia com ./ ou /, então ele é considerado um módulo do core e o endereço local do Node é verificado, ou uma dependência na pasta local node_modules. Se o arquivo começa com ./ então ele é considerado um arquivo relativo com o arquivo que chamou o require. Se o arquivo começa com / então ele é considerado pertencente ao endereço absoluto. Observação: - Você pode omitir o .js, o require vai automáticamente adicioná-lo caso for necessário. - Se o nome do arquivo passado para require é o nome de um diretório, a função vai primeiramente buscar por package.json no diretório e então carregar o arquivo referenciado na propriedade main. Caso contrário a função irá procurar por um arquivo chamado index.js dentro da pasta. Para informações mais detalhadas veja o artigo detalhado sobre módulos em Node.js. Como usar o REPL do Node.js O Node.js vem com um REPL (Read-Eval-Print Loop), que é um programa simples e interativo que lê expressões ou trechos de programa, avalia (ou executa) e imprime o resultado. Este é o consolo do Node.js e qualquer código JavaScript válido que pode ser escrito em um script pode ser passado para o REPL. Ele pode ser extremamente útil para experimentar o Node.js, debugar códgio, e descobrir alguns dos comportamentos mais excêntricos do JavaScript. Para rodar é simples, basta rodar o node no console sem inserir um nome de arquivo na frente: node. Lembrando que para rodar um servidor node você teria que passar o arquivo do servidor como parâmetro, por exemplo: node servidor.js. Executando o node usando o comando node leva você para um simples console > onde você pode escrever qualquer comando JavaScript que desejar. Como na maioria dos prompts você pode usar as setas para cima e para baixo para pelo seu histórico de comandos e modificar comandos anteriores. No REPL você também pode usar a tecla Tab para fazer o REPL tentar auto-completar o comando. Sempre que você digitar um comando o REPL vai imprimir o valor de retorno do comando. Se você quer reusar o resultado do comando anterior você pode usar a variável especial _. Por exemplo: node > 1+1 2 > _+1 3 Uma coisa importante para observar é onde os valores de retorno do REPL estão interessados: > x = 10 10 > var y = 5 >x 10 >y Grupo SoftwareOriginSP
Página 79
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
5 Quando a palavra-chave var não é usada o valor da expressão é armazenado e retornado. Quando a palavra-chave var é usada o valor da expressão é armazenado, mas não retornado. Se você precisa acessar qualquer módulo embutido, ou qualquer módulo de terceiros, ele pode ser acessado com a função require, assim como é feito num script Node. node > path = require('path') { resolve: [Function], normalize: [Function], join: [Function], dirname: [Function], basename: [Function], extname: [Function], exists: [Function], existsSync: [Function] } > path.basename("/a/b/c.txt") 'c.txt' Note novamente que sem a palavra-chave var, o conteúdo do objeto é retornado imediatamente e imprimido no stdout. O node possui alguns módulos embutidos, como o módulo console que contém funções úteis e pode ser usado sem precisar ser importado. Ao se digitar console no REPL do node ele retorna todas as funções públicas deste módulo. > console { log: [Function], info: [Function], warn: [Function], error: [Function], dir: [Function], time: [Function], timeEnd: [Function], trace: [Function], assert: [Function] } E ao se utilizar a função log deste módulo nós temos: > console.log("Imprima este texto") Imprima este texto undefined É interessante observar que o texto foi impresso no console, conforme o esperado, porém a função console.log("Imprima este texto") não tem valor de retorno, e por isso o REPL imprime undefined na linha seguinte. Com Node é fácil construir programas de rede escalonáveis No exemplo do servidor web “Olá Mundo” apresentado abaixo, muitas conexões de clientes podem ser tratadas simultaneamente. Node diz ao sistema operacional (através de epoll, Grupo SoftwareOriginSP
Página 80
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
kqueue, /dev/poll ou select) que ele deve ser notificado quando uma nova conexão é feita, e então ele volta a dormir quando completa o processamento. Se alguém se conecta no servidor, então o node executa a função de retorno (callback function) programada e cada conexão é se torna apenas uma pequena alocação de heap. var http = require('http'); http.createServer(function (req, res) { res.writeHead(200, {'Content-Type': 'text/plain'}); res.end('Olá Mundo\n'); }).listen(1337, "127.0.0.1"); console.log('Servidor rodando em http://127.0.0.1:1337/'); Isto está em contraste com o modelo de concorrência mais comum utilizado hoje em dia, onde threads do Sistema Operacional são empregadas para tratar as conexões. Sistemas de rede baseados em thread são relativamente ineficiente e muito difíceis de usar. Para entender melhor leia os artigos (em Inglês): The C10K problem e Scalable Network Programming. Node vai apresentar muito mais eficiência na utilização da memória sob altas cargas do que sistemas que alocam pilhas de threads com 2mb para cada conexão. Além disso, os usuários do Node estão livres de preocupações com processos em deadlock - não há bloqueios. Quase nenhuma função em Node executa diretamente E/S, então o processo nunca bloqueia. Porque nada bloqueia, programadores menos experientes são capazes de desenvolver sistemas eficiêntes. Node é semelhante em design e foi influenciado por sistemas como a Máquina de eventos do Ruby e o Twisted do Python. Node leva o modelo de eventos um pouco mais a diante - ele apresenta um laço de eventos como controle de fluxo (control flow) nativo, em vez de uma biblioteca. Em outros sistema sempre existe uma chamada bloqueante para iniciar o laço de eventos (event-loop), neles normalmente é definido o comprotamento através de callbacks no início do script e no final do script inicia-se o servidor através de uma chamada bloqueante como EventMachine::run(). No Node não há tal chamada para iniciar o laço de eventos, Node simplesmente entra no laço de eventos após executar o script de entrada e só sai do laço de eventos quando não há mais funções de retorno para serem executadas. Este comportamento é como acontece com o JavaScript do navegador - o laço de eventos é invisível ao usuário. O protocolo HTTP é de primeira classe em Node. A biblioteca HTTP do Node nasceu das experiências do autor em desenvolvimento e trabalhando com servidores web. Por exemplo, o stream de dados para a maioria dos frameworks web é impossível. O Node tenta corrigir este problema com seu parser HTTP e sua API. Juntamente com a infra-estrutura puramente orientada a eventos de Node, ele faz uma boa base para bibliotecas web ou frameworks. Mas o que ocorre com a concorrência em múltiplos processadores? Threads não são necessárias para escalonar programas para computadores multi-cores? No Node você pode iniciar um novo processo usando child_process.fork() e este processo vai ser processado em paralelo e para o balanciamento de carga de novas conexões através de múltiplos processos é utilizado o módulo cluster. Funções temporizadoras embutidas no JavaScript? setTimeout e setInterval Em JavaScript existem duas funções temporizadoras embutidas (built-in timer functions), setTimeout e setInterval que podem ser usadas para chamar funções de retorno (callback functions) após um determinado tempo. Veja um exemplo de uso abaixo: setTimeout(function() { console.log("setTimeout: Ja passou 1 segundo!"); }, 1000); setInterval(function() { console.log("setInterval: Ja passou 1 segundo!"); }, 1000); Grupo SoftwareOriginSP
Página 81
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Este código imprime a seguinte saída: setTimeout: Ja passou 1 segundo! setInterval: Ja passou 1 segundo! setInterval: Ja passou 1 segundo! setInterval: Ja passou 1 segundo! setInterval: Ja passou 1 segundo! ... Você pode ver que os parâmetros são os mesmos para as duas funções. O segundo parâmetro é um número que diz quanto tempo de espera, em milisegundos, vai passar antes de chamar a função passada como primeiro parâmetro. A diferença entre as duas funções é que setTimeout chama a função passada apenas uma vez, enquanto setInterval vai chamar a função passada indefinidamente sempre no intervalo de tempo passado. Você vai poder perceber que o laço de eventos(event loop) do Node vai sempre tentar chamar sua função no tempo pré-determinado por você, porém ele nem sempre vai acertar com precisão milimétrica, pois como o Node roda em apenas uma thread e seu contexto pode estar processando outra função no momento que ele deveria chamar sua função e ele vai precisar processar esta função até o final antes de chamar a próxima função do laço de eventos (event loop). Por isso é muito importante que você sempre escreva funções não bloqueantes para sua aplicação em Node, assim você estará aproveitando todas as vantagens do Node. Você deve ficar atento com a função setInterval porque ela pode causar alguns efeitos indesejados. Se, por exemplo, você precisar se certificar que seu servidor está rodando pingando ele todo segundo, você pode pensar em fazer algo assim: var recursiva = function () { console.log("Se passaram 1 segundo!"); setTimeout(recursiva,1000); } recursiva(); Como você pode ver este código chama a função recursiva que, quando processada, faz uma chamada para setTimeout(recursiva,1000); que chama recursiva de novo após 1 segundo, tendo assim quase o mesmo efeito de setInterval enquanto permanece mais resistente à erros não intencionais que podem ocorrer. Você também pode limpar os temporizadores com clearTimeout e clearInterval. O uso destas funções é bem simples: function nunca_chamada () { console.log("Voce nao deve executar esta funcao!"); } var timeout1 = setTimeout(nunca_chamada,1000); var interval1 = setInterval(nunca_chamada,1000); clearTimeout(timeout1); clearInterval(interval1);
Grupo SoftwareOriginSP
Página 82
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Como pode ser visto no código, se você guardar os valores de retorno dos temporizadores, você poderá facilmente desativá-los usando clearTimeout e clearInterval. O código assima não imprime nada no console pois os temporizadores são limpos antes que possam executar a função de retorno. O segredo final para as funções temporizadoras é que você pode passar parâmetros para a função de retorno (callback function) que será chamada apenas adicionando mais parâmetros para setTiemout e setInterval: setTimeout(console.log, 1000, "Esta funcao", "tem", 4, "parametros"); setInterval(console.log, 1000, "Esta funcao so tem um parametro"); O código acima imprime o seguinte retorno no console: Esta funcao tem 4 parametros Esta funcao so tem um parametro Esta funcao so tem um parametro Esta funcao so tem um parametro Esta funcao so tem um parametro ... Com estas funções temporizadores do JavaScript você pode facilmente executar tarefas de em segundo plano (background tasks) no seu servidor Node e o laço de eventos do Node vai cuidar de tudo muito bem. NPM, The Node Package Manager Olá pessoal! Este é o meu primeiro post aqui na comunidade NodeBR, e o assunto hoje será sobre o NPM (Node Package Manager). Apartir da versão Node.js 0.5.x que o NPM passou a ser integrado ao instalador do Node.js e isso simplificou a vida dos desenvolvedores, pois antes disso existia diversos gerenciadores de pacotes para o Node.js. Ele também mantém um repositório online NPM que também é mantido pela Joyent, atualmente ele contém mais de 30 mil módulos open-source! O objetivo desse post é apresentar os principais comandos para você gerenciar trabalhar com ele. Conhecendo os principais comandos
npm install nome_do_módulo - instala um módulo no projeto. npm install nome_do_módulo –save - instala o módulo e adiciona-o na lista de dependências do package.json do projeto. npm list - lista todos os módulos existentes no projeto. npm list -g - lista todos os módulos globais. npm remove nome_do_módulo - desinstala um módulo do projeto. npm update nome_do_módulo - atualiza a versão do módulo. npm -v - exibe a versão atual do npm. npm adduser nome_do_usuário - cria um usuário no site NPM para publicar seu módulo na internet. npm whoami - exibe detalhes do seu perfil público do npm (é necessário criar um usuário com o comando anterior). npm publish - publica o seu módulo, é necessário ter uma conta ativa no NPM.
Grupo SoftwareOriginSP
Página 83
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Subindo o node com express e entregando conteúdo dinâmico Este post vai ensinar como subir um aplicação Node utilizando o express, um framework web para Node, e também vai falar um pouco do package.json que guarda as informações de metadados do nosso projeto. Primeiramente crie a estrutura de diretórios, que nos será útil durante o desenvolvimento do aplicativo. / /static /static/css /static/css/style.css /static/javascript /static/javascript/app.js /views /views/index.html /package.json /myapp.js Definindo o projeto ‘package.json’ Precisamos criar o arquivo package.json de metadados de informações na raiz do projeto. Esse arquivo guarda informações do projeto e suas dependencias. Com ele fica fácil fazer deploy e o controle de dependência. o ideal é deixa o arquivo mais limpo possível. { "name": "server-web", "version": "0.4.0", "description": " Servidor http", "contributors": [ { "name": "Onezino Gabriel Moreira", "email": "[email protected]" } ], "keywords": [ "cli", "http", "server" ], "dependencies" : { "express" : "3.x", "consolidate" : "0.4.x", "swig": "0.12.x" } } As dependências que usamos são o renderizador de template e o framework web express. Pra instalar todas as dependências de uma vez é so digitar o comando na raiz do projeto que a ferramenta de pacote de módulos do node (NPM) se encarregará de todo o trabalho: npm install Escrevendo sua aplicação O próximo passo é escreve nossa aplicação, no myapp.js, a começar carregando os módulo que precissamos. var express = require('express'); var cons = require('consolidate');
Grupo SoftwareOriginSP
Página 84
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
De novidade temos o consolidate, que é um renderizador de template, tepois temos o express que é nosso objeto de estudo deste post. O express faz nosso wrapper com a biblioteca http então não precisamos carrega-las expressamente. app = express(); app.get('/',function(req,res){ res.send('Hello world!')}); app.listen(3000); console.log('app rodando na prota 3000'); A novidade está na forma de tratar o response, onde só precisamos chamar o método send. Mas isso não é muito útil, então vamos registrar nosso renderizador e entregar uma página mais elegante ao usuário. app.engine('html', cons.swig); app.set('view engine','html'); app.set('views',__dirname+'/views'); Nesse trecho dizemos ao express quem vai renderizar nossas páginas e a localização dos tempaltes. O express aceita uma lista grande de templates, no nosso exemplo estou utilizando o consolidate mas fique livre para utilizar qualquer um de sua preferência. E editamos a linha que entrega o conteúdo na rota /. /* app.get('/',function(req,res){res.send();}); */ app.get('/',function(req,res){ res.render('index')}); Pronto, agora só precisamos servir os arquivos estáticos (js, css, imagens). Utilizamos um middleware para tratar as chamadas que trata as chamadas get ao arquivos pesquisando de forma encadeada. app.use(express.static(__dirname + '/static')); Podemos agorar utilizar os arquivos /css/style.csse /javascript/app.js em nosso html. Você também pode prefixar o caminho dos arquivos estáticos. vamos utilziar o prefixo /js para nossos arquivos javascript. app.use('/js', express.static(__dirname+'/static/javascript')); Pronto o javascript pode ser utilizado apartir do caminho /js, exemplo /js/app.js. Conclusão Fizemos um setup básico para uma aplicação rodando em express. Essa foi uma um post de introdução funcional, outros posts irão explorar melhor as características do express e vão mostrar como este framework se comporta com aplicações mais elaboradas. Utilizando módulos em sua aplicação Node.js Pessoal, vamos trabalhar com módulos em nossa aplicação Node. Vou começar com o código abaixo. Grupo SoftwareOriginSP
Página 85
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
http = require('http'); Essa instrução carrega o módulo http, um módulo do sistema core module. O carregamento de módulos em Node é feito pela função require. Para quem vem do Python é como executar o código: import os as real_os Módulos são sub programas que executão tarefas bem definidas. Nós esperamos com nosso exemplo que o módulo http permita que utilizemos os protocolo http. Os módulos do sistema do Node têm suas funções documentadas na página da API do Node. Core Module São módulos embutidos no Node. Eles são carregados pela declaração sem prefixo do nome do módulo. Por exemplo: require('buffer'); Módulos declarados dessa forma dão preferência aos módulos do sistema, mesmo tendo um arquivo buffer.js na raiz de nossa aplicação. Módulos da aplicação. É comum uma grande aplicação dividir suas tarefas em módulos para atender aos requisitos do sistema, tomando como exemplo uma aplicação com o arquivo circle.js (expõe funções matemáticas aplicadas a círculos) em sua raiz. Esse arquivo corresponde a um módulo da aplicação e pode ser chamado da seguinte forma: circle = require('./circle'); circle = require('circle'); circle = require('/path/to/app/circle'); todas são formas válidas de carregar módulos na aplicação. Prefixos ./ / Os prefixos determinam a localização do módulo. O prefíxo / utiliza o caminho absoluto do módulo, já o prefíxo ./ utiliza o caminho relativo a partir do caminho atual. Dica: Quando o módulo não vem prefixado e há um conflito de nomes com módulos do sistema, os módulos do sistema ganham preferência no carregamento. Extensão dos módulos Quando os módulos não tem a extensão explícita o Node procura pelas seguintes extensões em sua devida ordem :
Primeiro .js: o arquivo é iterpretado como um arquivo de código fonte javascript; Depois .json: é utilizado um analisador JSON no arquivo; Por último .node: interpretado como um addon através do dlopen.
Grupo SoftwareOriginSP
Página 86
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Módulos do node_module Quando o arquivo não é prefixado e não é nativo do Node core modules, o Node busca o módulo na pasta node_module. A busca percorre toda o caminho de diretórios do código executado até a raíz. Fica mais fácil de entender com o exemplo abaixo. (vamos supor que código é executado em /path/to/app/code.js) require('mymodule'); Como mymodule não é um módulo do sistema ele então pesquisa na pastas node_module. Essa pesquisa é recursiva por todo o caminho iniciando no diretório atual até atingir a raiz.
Diretórios como módulos Quando um diretório da aplicação contém um arquivo package.json ele é também é considerado um módulo e pode ser carregado como outros módulos. draw = require('./draw'); Ele busca pelo arquivo: ./draw/package.json que contém informações do módulo e qual arquivo carregar. Caso não exista esse arquivo mas exista um index.js ele é utilizado como arquivo source do módulo. Outras informações que você deve conhecer sobre os módulos
Os módulos são carregados apenas uma vez. Node trata as chamadas cíclicas de módulos, então mesmo que não seja muito bom tê-las você não precisa se preocupar. Somente variáveis exportadas estão disponíveis no módulo, as demais variáveis e funções são privadas do módulo. Você pode carregar somente atributos específicos do módulo. Ex: var EventEmitter = require('events').EventEmitter;
Cuidado com a armadilha do escopo de variáveis em JavaScript JavaScript pode parecer sintaticamente parecido com linguagens c-like, mas seu comportamento com relação ao escopo das variáveis funciona bem diferente do que estamos acostumados. O javascript trabalha com o conceito de contexto de execução, chamando normalmente de contexto. O contexto define o escopo das variáveis ou funções. O contexto mais externo é conhecido como contexto global (global context) e é representado por um objeto. Ele é diferente para cada ambiente de execução do javascript. O global context de um browser é o objeto window, para o node.js ele é acessível através do GLOBAL ou GLOBAL.window ( forma mais parecida com a dos browser). Toda variável ou função global é então um atributo desse objeto. Quando um contexto é finalizado ele remove todos os objetos que pertencem a ele. Cada função tem seu próprio contexto de execução. Quando uma função é chamada seu contexto é incluído na pilha de execução, e removido quando a função termina sua execução retornando ao contexto anterior ao da função. Grupo SoftwareOriginSP
Página 87
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Pesquisa do indentificador A cada execução de um contexto é criado uma “encadeamento de contexto” dos objetos na memória para permitir o acesso a todas as variáveis e funções que o contexto tem acesso. As declarações de funções e variáveis do contexto atual são os primeiros objetos que fazem parte dessa cadeia, os contextos encadeados começam dos mais próximos para os mais distantes na pilha de contextos. A pesquisa pelo identificado percorre essa cadeia de contexto. A procura sempre ocorre do início da cadeia para o final até ele ser encontrado ou chegar ao ultimo contexto(global context). Trabalhando com with e try catch. O uso dessas expressões criam uma camada a mais no encadeamento de contexto. Com o with é criado uma camada de objetos com todos os atributos do objeto que está se trabalhando, e quando o catch é executado um objeto é incluído dando acesso a exceção jogada. function() { with(location) { var url = href + “?run=1”; } return url; } Blocos sem escopo Essa é com certeza a maior diferença entre javascript e as outras linguagens. No javascript blocos de código não criam um contexto próprio de execução permanecendo no contexto em que o o bloco foi executado. if(true) { var nome = “gabriel”; } alert(nome); // gabriel ou ainda mais confuso for(i= 0;i<10;i++) { /*codigo */ } alert(i); // 10 Os programadores também tem que tomar cuidado quanto a declaração de variáveis. Se esquecer de utilizar o var a variável é incluída no contexto global e não no contexto de execução atual. function f() { var a = 10; b = 20; return a + b; } alert(f()); //30 alert(b); //20 alert(typeof a); //undefined Referências: - javascript - node.js global variables? - Stack Overflow - Professional Javascript for web developer Passando função como parâmetro no JavaScript com Node.js Antes de tentar entender o funcionamento do Node.js temos que compreender uma característica do JavaScript herdada da programação funcional. No JavaScript você pode, por exemplo, passar uma função como parâmetro. Vamos ver alguns exemplos práticos para ver como isso funciona na prática. Podemos fazer alguma coisa como isso no JavaScript: function falar(palavra) { console.log(palavra); Grupo SoftwareOriginSP
Página 88
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
} function executar(funcao, valor) { funcao(valor); } executar(falar, "Oi JavaScript!"); Se executarmos nosso código vamos receber a saída impressa no console Oi JavaScript!. Você pode testar este exemplo utilizando seu prompt de comando. O Node.js é um ambiente de execução para usar o JavaScript fora do navegador também! Você pode acessar digitando o comando node no seu console e inicia o console interativo REPL, como no exemplo: > function falar(palavra) { ... console.log(palavra); ...} undefined > function executar(funcao, valor){ ... funcao(valor); ... } undefined > executar(falar, "Oi JavaScript!"); Oi JavaScript! undefined E o nosso console imprimiu a saída esperada: Oi JavaScript! Vamos entender o que está acontecendo neste contexto. O que fizemos aqui foi passar a função falar como o primeiro parâmetro da função executar. Isso é bem diferente de passar o valor retornado return pela função falar. A função falar, então, se tornou uma variável de nome funcao dentro da função executar, e executar pode chamar a função contida na variável adicionando os parênteses na variável: funcao(). E o que são estes undefined no console? Para cada comando dado no console do Node na linha seguinte é impresso o valor retornado return deste comando. Vamos analisar com calma: o primeiro comando foi definir a função falar, após a definição retornou undefined, o segundo comando foi definir a função executar, que também retornou undefined, o terceiro comando que executou a função executar, que executou a função falar contida na variável funcao, que acabou por executar a função console.log que imprime no console uma string, mas a função executar em si não tem nenhum valor de retorno, então retornou undefined. Nós podemos passar uma função como parâmetro para outra função usando seu nome, como foi visto no exemplo. Após entender este conceito veremos no exemplo a seguir que esta é a maneira indireta de se fazer isso, nós não precisamos definir a função para depois passar ela como parâmetro, nós podemos definir e passar a função como parâmetro para outra função no mesmo lugar. function executar(funcao, valor) { funcao(valor); } executar( function(palavra) { console.log(palavra) }, "Oi JavaScript!");
Grupo SoftwareOriginSP
Página 89
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Neste exemplo nós definimos a função que nós queremos passar para executar no lugar onde a função espera receber seu primeiro parâmetro. Aqui nós não precisamos nem dar um nome à função passada, por isso esta função é chamada de função anônima. Com estes exemplos podemos observar que no JavaScript é possível passar uma função como parâmetro quando chamamos outra função, e podemos fazer isso atribuindo nossa função à uma variável que pode ser passada, ou simplesmente definindo nossa função bem onde estamos chamando a função que vai recebê-la como parâmetro, usando uma função anônima. Exemplo Hello World em Node.js Este é um programa rápido para garantir que tudo está instalado e funcionando corretamente: var http = require('http'); http.createServer(function (req, res) { res.writeHead(200, {'Content-Type': 'text/plain'}); res.end('Hello Node.JS!'); }).listen(8080); console.log('Server running at http://localhost:8080/'); Rode o código com o comando de linha do Node em seu prompt de comando: > node hello_node.js Server running at http://localhost:8080/ Agora quando você acessar o endereço http://localhost:8080/ com seu navegador você ler um caloroso Hello Node.JS ! Parabéns você instalou o Node.js com sucesso e está pronto para mergulhar nos nós assíncronos do Node. JavaScript no servidor com Node.js A primeira encarnação do JavaScript viveu no navegador. Mas este é o contexto. Isso define o que você pode fazer com a linguagem, mas isso não fala muito sobre o que a linguagem em si pode fazer. JavaScript é uma linguagem completa: você pode usar isso em muitos contextos e fazer de tudo, com isso você pode alcançar o que as outras linguagens consegue. Node.js é realmente só mais um contexto: isso permite você rodar código JavaScript em backend, fora do navegador. A fim de executar o JavaScript que você pretende rodar em backend, ele precisa ser interpretado e executado. Isso é o que Node.js faz, fazendo uso da Máquina Virtual V8 da Google, o mesmo ambiente de execução do JavaScript usado pelo Google Chrome. Além disso, Node.js vem com muitos módulos úteis, então você não precisa escrever tudo do começo, por exemplo alguma função que escreva uma string no console. Portanto, Node.js é de fato duas coisas: um ambiente de execução e uma biblioteca. Você pode aprofundar-se cada vez mais neste mundo e descobrir como é fácil e divertido desenvolver aplicações altamente escaláveis em JavaScript com Node. O primeiro passo é instalar o Node e a maneira mais fácil de conseguir isso é instalando o Node.js através do gerenciador de pacotes, porém você também vai poder instalar o Node.js através do GitHub. Feito isso, desenvolva sua primeira aplicação, com apenas 6 linhas de código, através do Exemplo Hello World em Node.js. Você perceberá como é fácil e divertido o desenvolvimento
Grupo SoftwareOriginSP
Página 90
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
com JavaScript no servidor e não só isso, ele também é altamente eficiênte e escalável por ser um servidor orientado a eventos não bloqueante. Caso você queira conhecer mais sobre eventos e como desenvolver aplicações orientadas a eventos, você pode ler o artigo explicando eventos em Node que poderá te ensinar muito sobre eventos. É claro que tem muito mais a ser descoberto no Node e o objetivo deste artigo é te dar apenas o gostinho bom do prazer de desenvolver em Node, não deixe de acompanhar as comunidades de Node.js e de assinar nosso Feed de notícias para se aprofundar cada vez mais no assunto e ser você também um entusiásta desta tecnologia. O que é Node.js? Node.js é uma plataforma construída sobre o motor JavaScript do Google Chrome para facilmente construir aplicações de rede rápidas e escaláveis. Node.js usa um modelo de I/O direcionada a evento não bloqueante que o torna leve e eficiente, ideal para aplicações em tempo real com troca intensa de dados através de dispositivos distribuídos. Na JSConf 2009 Européia, um programador jovem chamado Ryan Dahl, apresentou um projeto em que estava trabalhando. Este projeto era uma plataforma que combinava a máquina virtual JavaScript V8 da Google e um laço de eventos. O projeto apontava para uma direção diferente das outras plataformas em JavaScript que rodam no servidor: todos I/O primitivos são orientado a evento. Aproveitando o poder e a simplicidade do Javascript, isso tornou tarefas difíceis de escrever aplicações assíncronas em tarefas fáceis. Desde quando foi aplaudido de pé no final do seu discurso, o projeto de Dahl tem recebido uma popularidade e uma aprovação sem precedentes. Que problema o Node pode resolver? Node estabeleceu o objetivo número um que é “fornecer uma maneira fácil para construir programas de rede escaláveis”. Qual é o problema com os programas servidores atuais? Vamos fazer os cálculos. Em linguagens como Java™ e PHP, cada conexão cria uma nova thread que potencialmente tem anexado 2 MB de memória com ela. Em um sistema que tenha 8 GB de RAM, isso põe o número máximo teórico de conexões concorrentes a cerca de 4.000 usuários. E quando o número de usuários aumenta, se você quer que sua aplicação web suporte mais usuários, você tem que adicionar mais e mais servidores. Somado a estes custos também podem haver possíveis problemas técnicos: um usuário pode usar diferentes servidores para cada requisição, então cada recurso compartilhado deve ser compartilhado para todos os servidores. Por todas estas rações, o gargalho em toda a arquitetura de aplicações web (incluindo velocidade de tráfego, velocidade do processador e velocidade da memória) é o número de conexões concorrentes que o servidor pode manipular. Node resolve esta questão trocando a maneira como a conexão é tratada no servidor. Ao invés de criar uma nova OS thread a cada conexão (e alocar a memória anexa a ela), cada conexão dispara um evento executado dentro da engine de processos do Node. Node afirma que nunca vai dar deadlock, já que não há bloqueios permitidos, e ele não bloqueia diretamente para chamadas de I/O. Node também alega que um servidor rodando ele pode suportar dezenas de milhares de conexões simultâneas. Então, agora que você tem um programa que pode manipular dezenas de milhares de conexões simultâneas, o que você pode realmente fazer com o Node? Seria incrível se você tivesse uma aplicação web que necessitasse desta quantidade de conexões. Este é um daqueles tipos de problema: “se você tem um problema, não é mais um problema”. Grupo SoftwareOriginSP
Página 91
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
O que Node definitivamente não é? Sim, Node é um servidor de programas. Entretanto o produto base do Node definitivamente não é como o Apache ou o Tomcat. Estes servidores são basicamente servidores ready-toinstall e estão prontos para instalar aplicativos instantâneamente. Você pode subir e rodar um servidor em um minuto com estes produtos. Node definitivamente não é isso. Parecido com como o Apache pode adicionar um módulo PHP para permitir desenvolvedores criarem páginas da web dinâmicas, e um módulo SSL para conexões seguras, Node tem o conceito de módulos que podem ser adicionados no núcleo do Node. Há literalmente centenas de módulos para rodarem com o Node, e a comunidade é bastante ativa em produzir, publicar e atualizar dezenas de módulos por dia.
Como o Node funciona O Node roda em uma JavaScript V8 VM. Mas espere, JavaScript no servidor? Isso, você leu certo. JavaScript no lado do servidor pode ser um conceito novo para todos que trabalharam exclusivamente com o JavaScript no lado do cliente, mas a idéia em sí não é tão absurda porque não usar a mesma linguagem de programação no cliente que você usa no servidor? O que é V8? O motor JavaScript V8 é o motor que a Google usa com seu navegador Chrome. Poucas pessoas pensam sobre o que realmente acontece com o JavaScript no lado do cliente. Bem, a engine JavaScript realmente interpreta o código e o executa. Com o V8 a Google criou um ultra-rápido interpretador escrito em C++, com um outro aspecto único: você pode baixar a engine e incorporá-la em qualquer aplicação desejada. Isso não está restrito em rodar em um navegador. Então Node atualmente usa o motor JavaScript V8 escrito pela Google e propõe que seja usado no servidor. Perfeito! Para que criar uma nova linguagem quando há uma boa solução já disponível?
Programação orientada a Evento Muitos programadores foram ensinados a acreditar que a programação orientada a objetos é um modelo de programação perfeito e a não usarem nada mais. Node utiliza o que é chamado modelo de programação orientada a evento. Programação orientada a evento no lado do cliente com jQuery: // jQuery code on the client-side showing how Event-Driven programming works // When a button is pressed, an Event occurs - deal with it // directly right here in an anonymous function, where all the // necessary variables are present and can be referenced directly $("#myButton").click(function(){ if ($("#myTextField").val() != $(this).val()) alert("Field must match button text"); });
Grupo SoftwareOriginSP
Página 92
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
O lado do servidor na verdade não é diferente do lado do cliente. Claro que não há botões sendo pressionados e não há campos de texto sendo escritos, mas em um nível mais alto, os eventos estão ocorrendo. Uma conexão é feita - evento! Dado é recebido através da conexão evento! Data parou de chegar através da conexão - evento! Por que é que este tipo de configuração é ideal para o Node? JavaScript é uma excelente linguagem para programação orientada a evento, porque ela permite funções anônimas e encerramentos, e o mais importante, a sintaxe é familiar para quase todos que já programaram na vida. As funções de callback que são chamadas quando um evento ocorre podem ser escritas no mesmo lugar onde você captura o evento. Fácil para desenvolver, fácil para manter. Sem frameworks complicados de Orientação a Objeto, sem interfaces, nenhum potencial para o excesso de arquitetura de qualquer coisa. Basta escutar um evento, escrever uma função de callback, e o Node toma conta de tudo.
8° Capítulo Um pouco dos conceitos da linguagem JavaScript Grupo SoftwareOriginSP
Página 93
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
O que é Node.js? Node.js é uma plataforma construída sobre o motor JavaScript do Google Chrome para facilmente construir aplicações de rede rápidas e escaláveis. Node.js usa um modelo de I/O direcionada a evento não bloqueante que o torna leve e eficiente, ideal para aplicações em tempo real com troca intensa de dados através de dispositivos distribuídos. Na JSConf 2009 Européia, um programador jovem chamado Ryan Dahl, apresentou um projeto em que estava trabalhando. Este projeto era uma plataforma que combinava a máquina virtual JavaScript V8 da Google e um laço de eventos. O projeto apontava para uma direção diferente das outras plataformas em JavaScript que rodam no servidor: todos I/O primitivos são orientado a evento. Aproveitando o poder e a simplicidade do Javascript, isso tornou tarefas difíceis de escrever aplicações assíncronas em tarefas fáceis. Desde quando foi aplaudido de pé no final do seu discurso, o projeto de Dahl tem recebido uma popularidade e uma aprovação sem precedentes.
Que problema o Node pode resolver? Node estabeleceu o objetivo número um que é “fornecer uma maneira fácil para construir programas de rede escaláveis”. Qual é o problema com os programas servidores atuais? Vamos fazer os cálculos. Em linguagens como Java™ e PHP, cada conexão cria uma nova thread que potencialmente tem anexado 2 MB de memória com ela. Em um sistema que tenha 8 GB de RAM, isso põe o número máximo teórico de conexões concorrentes a cerca de 4.000 usuários. E quando o número de usuários aumenta, se você quer que sua aplicação web suporte mais usuários, você tem que adicionar mais e mais servidores. Somado a estes custos também podem haver possíveis problemas técnicos: um usuário pode usar diferentes servidores para cada requisição, então cada recurso compartilhado deve ser compartilhado para todos os servidores. Por todas estas rações, o gargalho em toda a arquitetura de aplicações web (incluindo velocidade de tráfego, velocidade do processador e velocidade da memória) é o número de conexões concorrentes que o servidor pode manipular. Node resolve esta questão trocando a maneira como a conexão é tratada no servidor. Ao invés de criar uma nova OS thread a cada conexão (e alocar a memória anexa a ela), cada conexão dispara um evento executado dentro da engine de processos do Node. Node afirma que nunca vai dar deadlock, já que não há bloqueios permitidos, e ele não bloqueia diretamente para chamadas de I/O. Node também alega que um servidor rodando ele pode suportar dezenas de milhares de conexões simultâneas. Então, agora que você tem um programa que pode manipular dezenas de milhares de conexões simultâneas, o que você pode realmente fazer com o Node? Seria incrível se você tivesse uma aplicação web que necessitasse desta quantidade de conexões. Este é um daqueles tipos de problema: “se você tem um problema, não é mais um problema”. O que Node definitivamente não é? Sim, Node é um servidor de programas. Entretanto o produto base do Node definitivamente não é como o Apache ou o Tomcat. Estes servidores são basicamente servidores ready-toinstall e estão prontos para instalar aplicativos instantâneamente. Você pode subir e rodar um servidor em um minuto com estes produtos. Node definitivamente não é isso. Parecido com como o Apache pode adicionar um módulo PHP para permitir desenvolvedores criarem Grupo SoftwareOriginSP
Página 94
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
páginas da web dinâmicas, e um módulo SSL para conexões seguras, Node tem o conceito de módulos que podem ser adicionados no núcleo do Node. Há literalmente centenas de módulos para rodarem com o Node, e a comunidade é bastante ativa em produzir, publicar e atualizar dezenas de módulos por dia.
Como o Node funciona O Node roda em uma JavaScript V8 VM. Mas espere, JavaScript no servidor? Isso, você leu certo. JavaScript no lado do servidor pode ser um conceito novo para todos que trabalharam exclusivamente com o JavaScript no lado do cliente, mas a idéia em sí não é tão absurda porque não usar a mesma linguagem de programação no cliente que você usa no servidor? O que é V8? O motor JavaScript V8 é o motor que a Google usa com seu navegador Chrome. Poucas pessoas pensam sobre o que realmente acontece com o JavaScript no lado do cliente. Bem, a engine JavaScript realmente interpreta o código e o executa. Com o V8 a Google criou um ultra-rápido interpretador escrito em C++, com um outro aspecto único: você pode baixar a engine e incorporá-la em qualquer aplicação desejada. Isso não está restrito em rodar em um navegador. Então Node atualmente usa o motor JavaScript V8 escrito pela Google e propõe que seja usado no servidor. Perfeito! Para que criar uma nova linguagem quando há uma boa solução já disponível?
Programação orientada a Evento Muitos programadores foram ensinados a acreditar que a programação orientada a objetos é um modelo de programação perfeito e a não usarem nada mais. Node utiliza o que é chamado modelo de programação orientada a evento. Programação orientada a evento no lado do cliente com jQuery: // jQuery code on the client-side showing how Event-Driven programming works // When a button is pressed, an Event occurs - deal with it // directly right here in an anonymous function, where all the // necessary variables are present and can be referenced directly $("#myButton").click(function(){ if ($("#myTextField").val() != $(this).val()) alert("Field must match button text"); }); O lado do servidor na verdade não é diferente do lado do cliente. Claro que não há botões sendo pressionados e não há campos de texto sendo escritos, mas em um nível mais alto, os eventos estão ocorrendo. Uma conexão é feita - evento! Dado é recebido através da conexão evento! Data parou de chegar através da conexão - evento! Por que é que este tipo de configuração é ideal para o Node? JavaScript é uma excelente linguagem para programação orientada a evento, porque ela permite funções anônimas e encerramentos, e o mais importante, a sintaxe é familiar para quase todos que já programaram na vida. As funções de callback que são chamadas quando um evento ocorre podem ser escritas no mesmo lugar onde você captura o evento. Fácil para desenvolver, fácil Grupo SoftwareOriginSP
Página 95
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
para manter. Sem frameworks complicados de Orientação a Objeto, sem interfaces, nenhum potencial para o excesso de arquitetura de qualquer coisa. Basta escutar um evento, escrever uma função de callback, e o Node toma conta de tudo.
Nodejs e MongoDB - Introdução ao Mongoose Mongoose é uma biblioteca do Nodejs que proporciona uma solução baseada em esquemas para modelar os dados da sua aplicação. Ele possui sistema de conversão de tipos, validação, criação de consultas e hooks para lógica de negócios. Mongoose fornece um mapeamento de objetos do MongoDB similar ao ORM (Object Relational Mapping), ou ODM (Object Data Mapping) no caso do Mongoose. Isso significa que o Mongoose traduz os dados do banco de dados para objetos JavaScript para que possam ser utilizados por sua aplicação. Obs: Este artigo vai assumir que você tem o Nodejs e o MongoDB instalados. Você precisa estar com o MongoDB rodando em seu computador para poder fazer os seguintes exemplos. Instalando o pacote Mongoose Utilizando o [NPM][] via linha de comando é muito simples fazer a instalação dos pacotes do Nodejs. Para fazer a instalação do Mongoose, execute a seguinte linha de comando que o NPM cuidará de instalar a versão estável mais recente do pacote requerido: npm install Mongoose Conectando com o MongoDB Neste artigo vamos conectar o Mongoose com o banco de dados de testes que o MongoDB define quando você o inicializa pelo console e vamos garantir que qualquer erro de conexão seja impresso no console. var Mongoose = require('Mongoose');
var db = Mongoose.connection;
db.on('error', console.error); db.once('open', function() { console.log('Conectado ao MongoDB.') // Vamos adicionar nossos Esquemas, Modelos e consultas aqui }); Grupo SoftwareOriginSP
Página 96
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Mongoose.connect('mongodb://localhost/test');
Ao executar nossa aplicação de exemplo podemos observar no console do MongoDB que foram abertas 5 conexões simultâneas. Isto ocorre porque o Mongoose usa um conjunto de 5 conexões simultâneas por padrão que são compartilhadas por toda sua aplicação. Para melhorar o desempenho das nossas aplicações vamos deixá-las abertas, porém você pode alterar o comportamento padrão adicionando parâmetros opcionais ao Mongoose.connect() o parâmetro poolSize define a quantidade de conexões simultâneas. Veja abaixo a saída do meu MongoDB exibindo que foram abertas 5 conexões simultâneas:
Sat Aug 31 11:12:21.827 [initandlisten] connection accepted from 127.0.0.1:64413 #2 (1 connection now open) Sat Aug 31 11:12:21.830 [initandlisten] connection accepted from 127.0.0.1:64414 #3 (2 connections now open) Sat Aug 31 11:12:21.831 [initandlisten] connection accepted from 127.0.0.1:64415 #4 (3 connections now open) Sat Aug 31 11:12:21.831 [initandlisten] connection accepted from 127.0.0.1:64416 #5 (4 connections now open) Sat Aug 31 11:12:21.832 [initandlisten] connection accepted from 127.0.0.1:64417 #6 (5 connections now open) Esquemas e Modelos Para começar vamos precisar de um esquema e um modelo para que possamos trabalhar com os dados que serão persistidos em nosso banco de dados MongoDB. Esquemas definem a estrutura dos documentos dentro de uma coleção e modelos são usados para criar instâncias de dados que serão armazenados em documentos. Nós vamos fazer um banco de dados de filmes para acompanhar os filmes que tem cenas extras após os créditos finais (também chamado de ‘credit cookies’). O código abaixo mostra nosso esquema movieSchema e nosso modelo Movie criado.
var movieSchema = new Mongoose.Schema({ title: { type: String }, rating: String, Grupo SoftwareOriginSP
Página 97
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
releaseYear: Number, hasCreditCookie: Boolean });
var Movie = Mongoose.model('Movie', movieSchema);
A última linha deste código compila o modelo Movie utilizando o esquema movieSchema como estrutura. O Mongoose também cria uma coleção no MongoDB chamada Movies para estes documentos. Você pode notar que o modelo Movie está em letra maiúscula, isto porque quando um modelo é compilado é retornado uma função construtora que será usada para criar as instâncias do modelo. As instâncias criadas pelo construtor do modelo são documentos que serão persistidos pelo MongoDB ao utilizar a função save.
Criar, Recuperar, Atualizar e Deletar (CRUD)
Criar um novo documento Movie é fácil agora que já definimos o modelo, basta instancializar o modelo Movie e salvar esta instância na base. Atualizar é igualmente fácil, faça suas modificações e então chame a função save do seu documento.
var thor = new Movie({ title: 'Thor', rating: 'PG-13', releaseYear: '2011', // Note o uso de String ao inves de Number hasCreditCookie: true });
thor.save(function(err, thor) { if (err) return console.error(err); Grupo SoftwareOriginSP
Página 98
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
console.dir(thor); });
Observe que utilizamos uma String ao invés de um Número no campo releaseYear e o Mongoose vai se encarregar de converter o dado para tipo especificado no esquema. Quando adicionamos estes dois códigos dados à nossa aplicação e executamos vemos que a função save vai fornecer um documento recém criado, observado pelo que foi impresso no console de nossa aplicação.
Coleções no MongoDB possuem esquemas flexíveis, isto quer dizer que coleções não impõem a estrutura dos documentos. Na prática isto significa que documentos da mesma coleção não precisam ter o mesmo conjunto de campos ou estrutura, e campos comuns em documentos de uma coleção podem carregar diferentes tipos de dados. Como foi visto em nosso exemplo, utilizando o Mongoose para mapear nossa base, ele padroniza os documentos de um mesmo esquema a fim garantir que instâncias do modelo que compilou aquele esquema sempre tenham o mesmo tipo de dados nos atributos especificados pelo esquema. MongoDB também possui uma estrutura de dados chamada índice que permite você localizar rápidamente documentos baseado nos valores armazenados em certos campos específicos. Fundamentalmente, índices no MongoDB é similar à índices em outros sistemas de banco de dados. Em nosso exemplo o índice do modelo criado é _id.
Recuperando um documento da base
Diferentes maneiras podem ser utilizadas para recuperar um documento existente na base de dados. Você pode buscar documentos baseado em qualquer propriedade do esquema e Grupo SoftwareOriginSP
Página 99
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
você pode buscar qualquer quantidade de documentos. Utilize findOne para limitar os resultados a um único documento.
// Buscando um unico filme pelo nome Movie.findOne({ title: 'Thor' }, function(err, thor) { if (err) return console.error(err); console.dir(thor); });
// Buscando todos os filmes Movie.find(function(err, movies) { if (err) return console.error(err); console.dir(movies); });
// Buscando todos os filmes que possuem 'credit cookie'. Movie.find({ hasCreditCookie: true }, function(err, movies) { if (err) return console.error(err); console.dir(movies); });
Mongoose também permite você criar funções auxiliares estáticas para buscar seus dados. Para isso você deve atribuir sua função estática ao esquema antes de ser feita compilação para o modelo. movieSchema.statics.findAllWithCreditCookies = function(callback) { return this.find({ hasCreditCookie: true }, callback); };
Grupo SoftwareOriginSP
Página 100
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
var Movie = Mongoose.model('Movie', movieSchema);
// Utilizadno a funcao auxiliar estatica do modelo 'Movie' compilado Movie.findAllWithCreditCookies(function(err, movies) { if (err) return console.error(err); console.dir(movies); });
Isso é tudo que precisa fazer para manipular os dados em MongoDB. Mongoose faz esta tarefa muito simples então você pode desenvolver seus serviços rapidamente. Usando isso junto com suas habilidades com Express, você pode desenvolver um aplicativo web muito bom e funcional. Abaixo está um código utilizando os conceitos aqui apresentados que implementa a função auxiliar estática, para buscar todos filmes com ‘credit cookies’, salva 3 filmes do Thor com ‘credit cookies’ e em seguida cria um Timeout para buscar os filmes utilizando a função auxiliar 1 segundo depois - Isto é feito pois como estamos trabalhando com IO não bloqueante o Node não aguarda o tempo de salvar os filmes antes de prosseguir, por isso damos um tempo para se certificar que eles foram salvos antes de buscá-los. var Mongoose = require('Mongoose');
var db = Mongoose.connection;
db.on('error', console.error); db.once('open', function() { // Create your schemas and models here. console.log('Conectado.')
var movieSchema = new Mongoose.Schema({ Grupo SoftwareOriginSP
Página 101
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
for (var i =1; i<=3; i++){ var thor = new Movie({ title: 'Thor ' + i, rating: 'PG-13', releaseYear: '2011', // Note o uso de String ao inves de Number hasCreditCookie: true });
thor.save(function(err, thor) { if (err) return console.error(err); console.log('Salvo: ') console.dir(thor); }); Grupo SoftwareOriginSP
Página 102
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
}
setTimeout(function(){ // Utilizadno a funcao auxiliar estatica do modelo 'Movie' compilado Movie.findAllWithCreditCookies(function(err, movies) { if (err) return console.error(err); console.log('Buscado: ') console.dir(movies); }); }, 1000); });
Mongoose.connect('mongodb://localhost/test');
Mineração de dados e as funções map, reduce e filter Este artigo vai mostrar como é simples fazer uma mineração de dados na Internet com o Nodejs e demonstrando como utilizar funções muito úteis do JavaScript: map, reduce e filter. Bancos de dados não-relacionais (NoSQL), como o CouchDB e o mongoDB, utilizam o MapReduce, que é uma combinação das funções map e reduce apresentadas aqui, para efetuarem suas consultas na base de dados. Eles implementam funções de maneira muito similares às do JavaScript e este é o melhor motivo para compreender o funcionamento desta operação. O texto também serve de base para quem deseja fazer um web crawler, também chamado de web spider, que são programas que acessam páginas da web e extraem informações relevantes de seu conteúdo. Os motores de busca, como a Google e o Bing, utilizam esta técnica para manterem suas bases de dados atualizadas. Os crawlers também são utilizados para varrer a Internet a fim de minerar endereços de email e dados pessoais.
Buscando o conteúdo na Web
Grupo SoftwareOriginSP
Página 103
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
A fonte de dados utilizada será o Feed RSS do HackerNews, como o RSS está em XML podemos tratá-lo da mesma forma que um documento HTML. Para dar início ao nosso projeto temos que criar nosso aplicativo e fazer com que ele busque todo o Feed para que seja possível fazer a extração dos dados. Para isso será utilizado a função get do módulo https. Observe como o aplicativo exemplo app.js ficou. // app.js
var https = require('https');
function getCallback(response){ var body = ''; console.log("Temos uma resposta: " + response.statusCode); response.on('data', function (chunk) { body += chunk; }); response.on('end', function(){ console.log("Corpo da mensagem: " + body.slice(0,200) + '...'); }); }
Vamos acompanhar o que é feito neste código passo a passo. Primeiro importamos o módulo https que buscará o endereço da web que queremos. Depois definimos a função getCallback que, como o nome sugere, será a função callback do nosso https.get(), ela recebe o objeto response como argumento e é adicionado um event listner para o evento data, que Grupo SoftwareOriginSP
Página 104
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
concatena a resposta na variável body, e um event listner para o evento end, que chama a função console.log() quando a resposta de nossa requisição está completa. Por último executamos o método https.get() passando como parâmetro o endereço do Feed que vamos buscar e a função callback que tratará a resposta, adicionamos também um event listner para o evento error que nos indicará se ocorreu algum erro durante nossa requisição. É interessante ressaltar que o Node trabalha com eventos, então cada pacote recebido como resposta é um evento que deve ser tratado no nosso objeto response, por isso temos que concatenar toda a resposta a fim de trabalhar com o corpo completo da página requisitada.
Minerando os dados
Agora que temos o corpo da página temos que fazer a extração dos dados que queremos e do jeito que queremos. Neste exemplo vamos demonstrar as habilidades das funções map, reduce e filter desenvolvendo um contador de palavras dos títulos dos posts. Ele contará o número de ocorrências de cada palavrá e retornará uma lista de túplas [palavra, nro_ocorrências] das palavras que ocorreram mais de 10 vezes nos títulos. Como foi dito, a base de dados que vamos minerar está em RSS então ela pode ser tratada da mesma forma que uma página HTML. Para isso podemos utilizar o jQuery para extrair a informação formatada. Para utilizar as funções do núcleo do jQuery no servidor podemos tanto utilizar a própria biblioteca jQuery, desenvolvida para o navegador, como utilizar a biblioteca Cheerio, que é uma implementação rápida, flexível e limpa do core do jQuery designada específicamente para o servidor. Matthew Mueller diz que seus benchmarks sugerem que Cheerio seria 8 vezes mais rápida que a implementação do jQuery para o navegador. Antes de utilizá-la, vamos fazer a instalação do pacote do Cheerio para em nosso aplicativo. Para isso basta instalá-lo utilizando a NPM via linha de comando: npm install cheerio
Vamos, então, importar o módulo para nosso aplicativo e criar uma função para minerar os dados da página requisitada. Vamos chamar nossa função de minerarDados() e passar para ela o corpo da resposta recebida pela função getCallback(). O código do nosso exemplo ficaria assim:
var https = require('https'), Grupo SoftwareOriginSP
Página 105
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
cheerio = require('cheerio');
function minerarDados(body){ var $ = cheerio.load(body); var titles = $('title'); console.log(titles.slice(0,2)); }
function getCallback(response){ var body = ''; console.log("Got response: " + response.statusCode); response.on('data', function (chunk) { body += chunk; }); response.on('end', function(){ minerarDados(body); }); }
https.get('https://news.ycombinator.com/bigrss', getCallback) .on('error', function(e){ console.log("Ocorreu um erro: " + e.message); }); Agora nossa função minerarDados() conseguiu extrair os objetos do tipo tag de nome title e imprime dois deles no console. Ok, ela extrai todos os títulos do Feed RSS, porém temos um problema, $('title') retorna uma lista de nós (NodeList). Vamos então aplicar o MapReduce para extrair a informação que desejamos e por último filtrar os resultados indesejados.
Grupo SoftwareOriginSP
Página 106
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Para converter-mos nossa lista de nós em um Array contendo apenas o conteúdo de texto dos nomes vamos primeiro transformar nossa NodeList em um Array de objetos utilizando a função Array.prototype.slice.call() e neste array de objetos vamos aplicar a função map() do JavaScript, o código ficaria assim.
A função map() recebe dois parâmetros mas normalmente apenas o primeiro é especificado. O primeiro parâmetro é uma função de callback que será chamada sobre todos os elementos no Array. O segundo parâmetro é utilizado para especificar o valor para o objeto this durante a execução da função. O mais importante são os parâmetros passados para a função de callback, eles são: o elemento do Array em sí, o índice do Array, e todo o Array (contexto). A assinatura da função de callback se parece com essa: var callback = function(elemento, indice, contexto) { /* omitido */ } Porém também podemos utilizar a função map() do jQuery implementada pelo Cheerio, mas para isso é preciso ter atenção pois a assinatura da função callback do jQuery é ligeiramente diferente, em seus parâmetros primeiro vem o índice do elemento seguido do elemento em sí. Sua assinatura é assim: var callback = function(indice, elemento) { /* omitido */ } Para implementar utilizando a função map() do jQuery é preciso fazer ligeiras modificações, o código deverá se parecer com isto: var titles = $('title').map( function(index, node) { return $(node).text(); }); Eliminando pontuações desnecessárias Agora que já temos um Array de strings contento o texto de todos os títulos da nossa base de dados que estamos explorando. Você já deve ter notado que os títulos não contém apenas Grupo SoftwareOriginSP
Página 107
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
letras, eles também contém números e pontuações. Estes são desnecessários em nosso contador de palavras, então vamos tratar de removê-los. Isto pode ser feito nesta mesma chamada map() utilizando expressão regular. var words = $('title') .map(function(index, node) { return $(node).text().toLowerCase().match(/([a-z]+)/g); }); Se você já utilizou RegEx (expressões regulares) no JavaScript você sabe que isso gera um efeito colateral: Esta função retorna Arrays com todas as palavras que passaram na RegEx individualmente. Contudo esse é um efeito colateral útil para nós pois só precisamos das palavras, no entanto temos mais um problema: o Array words retornado é de duas dimensões, isso que significa que temos que achatá-lo para apenas uma dimensão. Este é um bom momento para fazer uso da função reduce() do JavaScript. var words = $('title') .map(function(index, node) { return $(node).text().toLowerCase().match(/([a-z]+)/g); }) .reduce(function(last, now){ return last.concat(now) }, []); array.reduce(callback[, initialValue]) Assim como map, reduce recebe dois argumentos. O primeiro é novamente a função de callback, que será chamada para cada elemento no Array. O segundo parâmetro é o initialValue que será de fácil entendimento quando você ver a assinatura da função de callback: var callback = function(valorAnterior, valorAtual, indice, contexto) { /* omitted */ } Como você pôde ver, o primeiro parâmetro passado para a função de callback é o valor anterior. Se você precisar somar um Array de números, isso não será problema. Mas em nosso caso nós precisamos retornar um Array, então nós especificamos um valor inicial para o valorAnterior, neste caso um Array vazio [] onde serão adicionados os elementos de nossa antiga Array bi-dimensional.
Grupo SoftwareOriginSP
Página 108
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Contando as palavras
Para esta tarefa podemos utilziar reduce novamente, mas antes de fazê-lo, vou mostrar como o resultado vai se parecer: [['the', 'on', 'news', 'hacker', ...], [50, 66, 20, 19, ...]] Isso será uma Array de duas dimensões novamente. São dois Arrays dentro de um Array, e o índice da palavra no primeiro Array corresponderá com o índice do número de vezes que ela ocorre nos títulos no segundo Array. Para fazer isso vamos utilizar reduce e vamos especificar uma Array 2d vazia como valor inicial: [[], []]. var scores = $('title') .map(function(index, node) { return $(node).text().toLowerCase().match(/([a-z]+)/g); }) .reduce(function(last, now){ return last.concat(now) }, []) .reduce(function(last, now){ var index = last[0].indexOf(now); if (index === -1) { last[0].push(now); last[1].push(1); } else { last[1][index] += 1; } return last; }, [[], []]);
Comprimindo os Arrays Grupo SoftwareOriginSP
Página 109
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Estamos próximos de concluirmos nossa coleção de dados. Tudo que devemos fazer agora é combinar as duas Arrays em uma. O formato final do Array será este: [['the', 50], ['on', 66], ['news', 20], ['hacker', 19], ...] O JavaScript não implementa a função zip() nativamente, nós poderíamos utilziar esta função do pacote Underscore mas como esta função é muito simples de ser implementada vamos implementar nós mesmos a mesclagem dos valores destas listas. var zip = []; scores[0].forEach(function(word, i) { zip.push([word, scores[1][i]]) });
Filtrando os dados
Lembrando que nossa aplicação só se interessa por palavras que ocorreram mais de 10 vezes, a fim de filtrar as palavras menos importantes da lista, então vamos implementar esta última funiconalidade. Para isto podemos utilizar a função nativa do JavaScript filter da seguinte maneira: var filtered = zip.filter(function (element){ return element[1] >= 10 }); array.filter(callback[, thisObject])
Os parâmetros da função filter são exatamente os mesmos da função map já apresentada, ela possui dois parâmetros porém normalmente só o primeiro é especificado. O primeiro parâmetro é uma função de callback que será chamada sobre todos os elementos no Array. O segundo parâmetro é utilizado para especificar o valor para o objeto this durante a execução da função. O importante é a função de callback, seus parâmetros são: o elemento do Array em sí, o índice do Array e todo o Array (contexto). A assinatura da função de callback se parece com essa: var callback = function(elemento, indice, contexto) { /* omitido */ } Grupo SoftwareOriginSP
Página 110
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Esta função retorna verdadeiro ou falso para filtrar os elementos que continuam no Array e os elementos que são excluídos do Array. Quando fizemos element[1] >= 10 utilizamos o operador lógico de comparação que retorna verdadeiro para todos os elementos cujo valor do índice 1 seja maior ou igual a 10 e falso caso contrário - o índice 1 guarada a quantidade de vezes que a palavra ocorreu nos títulos.
Conclusão
Agora nosso exemplo está concluido e conseguímos fazer a mineração dos dados que queríamos dos Feeds do HackerNews. O código final ficou assim: var https = require('https'), cheerio = require('cheerio');
function minerarDados(body){ var $ = cheerio.load(body); var scores = $('title') .map(function(index, node) { return $(node).text().toLowerCase().match(/([a-z]+)/g); }) .reduce(function(last, now){ return last.concat(now) }, []) .reduce(function(last, now){ var index = last[0].indexOf(now); if (index === -1) { last[0].push(now); last[1].push(1); } else { last[1][index] += 1; Grupo SoftwareOriginSP
Página 111
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
} return last; }, [[], []]); var zip = []; scores[0].forEach(function(word, i) { zip.push([word, scores[1][i]]) }); var filtered = zip.filter(function (element){ return element[1] >= 10 }); console.log(filtered.slice(0,20)); }
function getCallback(response){ var body = ''; console.log("Got response: " + response.statusCode); response.on('data', function (chunk) { body += chunk; }); response.on('end', function(){ minerarDados(body); }); }
https.get('https://news.ycombinator.com/bigrss', getCallback) .on('error', function(e){ console.log("Ocorreu um erro: " + e.message); Grupo SoftwareOriginSP
Página 112
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
}); O exemplo apresentado funcionou corretamente e conseguiu extrair a informação da base da Internet escolhida e imprimir os 20 primeiros elementos do nosso Array no console console.log(filtered.slice(0,20));. O artigo teve como finalidade ensinar os leitores como é fácil fazer tal operação utilizando o Nodejs e as funções nativas do JavaScript, podendo ser utilizado como base para desenvolvimento de aplicações mais complexas como robôs de web crawling entre outros.
Como evitar o inferno de callbacks
Este artigo é um guia para se escrever programas assíncronos em JavaScript que expõe dicas para se evitar o ‘inferno de callbacks’. O bom entendimento de callbacks é essencial para a programação orientada a eventos do Node, este é o nome dado a funções que serão executadas de modo assíncrono, ou posteriormente. Caso tenha dúvidas do que é uma função callback você pode ler o artigo explicando callbacks em Node.
O que é o ‘inferno de callbacks’
JavaScript assíncrono, ou JavaScript que usa callbacks é difícil de se entender intuitivamente. Muitos códigos acabam se parecendo como este: fs.readdir(source, function(err, files) { if (err) { console.log('Error finding files: ' + err) } else { files.forEach(function(filename, fileIndex) { console.log(filename) gm(source + filename).size(function(err, values) { if (err) { console.log('Error identifying file size: ' + err) Grupo SoftwareOriginSP
Página 113
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
} else { console.log(filename + ' : ' + values) aspect = (values.width / values.height) widths.forEach(function(width, widthIndex) { height = Math.round(width / aspect) console.log('resizing ' + filename + 'to ' + height + 'x' + height) this.resize(width, height).write(destination + 'w' + width + '_' + filename, function(err) { if (err) console.log('Error writing file: ' + err) }) }.bind(this)) } }) }) } }) Você consegue ver quantas functions e }) têm neste código? Isto é carinhosamente conhecido como o inferno de callbacks. Escrever códigos melhores não é difícil, você só precisa saber algumas coisas. Nomeie suas funções Aqui esta um confuso código em JavaScript que roda no navegador que utiliza browserrequest para fazer uma requisição AJAX para o servidor. var form = document.querySelector('form') form.onsubmit = function(submitEvent) { var name = document.querySelector('input').value request({ uri: "http://example.com/upload", body: name, Grupo SoftwareOriginSP
Página 114
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
method: "POST" }, function(err, response, body) { var statusMessage = document.querySelector('.status') if (err) return statusMessage.value = err statusMessage.value = body }) } O código possui duas funções anônimas, observe como fica o código quando damos nomes a estas funções.
var form = document.querySelector('form') form.onsubmit = function formSubmit(submitEvent) { var name = document.querySelector('input').value request({ uri: "http://example.com/upload", body: name, method: "POST" }, function postResponse(err, response, body) { var statusMessage = document.querySelector('.status') if (err) return statusMessage.value = err statusMessage.value = body }) } Como você pode ver nomear as funções é muito fácil e traz algumas coisas boas para seu código:
Facilita a leitura do código Grupo SoftwareOriginSP
Página 115
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Quando acontecem exceções você pega rastreamento da pilha (stacktraces) que referencia o nome atual da função ao invés de “anonymous” Permite você manter seu código mais raso, ou não aninhado profundamente Mantenha seu código raso Aproveitando o exemplo apresentado, vamos um pouco mais longe e se livrar do nível triplo de aninhamento que há no código.
function formSubmit(submitEvent) { var name = document.querySelector('input').value request({ uri: "http://example.com/upload", body: name, method: "POST" }, postResponse) }
function postResponse(err, response, body) { var statusMessage = document.querySelector('.status') if (err) return statusMessage.value = err statusMessage.value = body }
Códigos como este facilitam a leitura e a manutenção posterior.
Grupo SoftwareOriginSP
Página 116
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Modularize seu código
Esta é a parte mais importante: Qualquer pessoa é capaz de criar módulos (biblioteca AKA). Citando Isaac Schlueter (do projeto do Node): “Escreva módulos pequenos onde cada um faça uma coisa, e monte-os em outros módulos que fazem coisas maiores. Você não entra no inferno de callback se você não ir para lá.” Vamos pegar o exemplo apresentado e transformá-lo em um módulo dividindo-o em um par de arquivos. Aqui será apresentado um método simples que funciona tanto no navegador como no servidor. Vamos criar um arquivo chamado formuploader.js que contém nossas duas funções apresentadas anteriormente.
function formSubmit(submitEvent) { var name = document.querySelector('input').value request({ uri: "http://example.com/upload", body: name, method: "POST" }, postResponse) }
function postResponse(err, response, body) { var statusMessage = document.querySelector('.status') if (err) return statusMessage.value = err statusMessage.value = body }
exports.submit = formSubmit
Grupo SoftwareOriginSP
Página 117
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
A variável exports da última linha é um exemplo do sistema de módulos CommonJS. O bom deste sistema de módulos é que ele é muito simples - você só precisa definir o que deve ser compartilhado quando o módulo for importado, para isto é utilizado a variável local exports do módulo. Para compreender melhor como funciona a importação de módulos você pode ler o artigo que explica como funciona a função require do Node.js; O Node tem um sistema simples de carregamento de módulos que utiliza o padrão CommonJS e de fato a maior parte das funções do núcleo do Node é implementada utilizando módulos escritos em JavaScript. Compreender profundamente o sistema de módulos do Node é crucial para implementação de códigos legíveis e de fácil manutenção, fundamental para a maior parte das aplicações. Para entender melhor este assunto acesse o artigo que aborda detalhadamente os módulos em Node.js. Para usar módulos no padrão CommonJS no navegador, você pode utilizar a biblioteca browserify. Esta biblioteca basicamente permite você utilizar a função require para carregar módulos no seu programa. Agora que temos o formuploader.js e ele foi carregado na página utilizando a tag HTML script, nós só precisamos importá-lo e usá-lo. Veja como ficou o código da nossa aplicação agora:
var formUploader = require('formuploader') document.querySelector('form').onsubmit = formUploader.submit
Agora nossa aplicação exemplo apresentada só possui duas linhas e tem os seguintes benefícios: Facilita o entendimento de novos desenvolvedores - Eles não precisam empacar tentando ler todas as funções formuploader para entender o que está acontecendo aqui As funções do formuploader agora podem ser usadas em outros lugares sem a duplicação de código e pode facilmente ser compartilhaada no GitHub O código em sí é agradável, simples e fácil de ler Há muitos padrões de módulos para o navegador e para o servidor. Alguns deles são muito complicados. Os mostrados aqui são os considerados mais simples de se entender.
E o Promises?
Grupo SoftwareOriginSP
Página 118
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Promises é o padrão mais abstrato para trabalhar com código assíncrono em JavaScript. O escopo deste artigo é mostrar como escrever um código agradável em JavaScript. Se você utiliza uma biblioteca de terceiros que adiciona abstração para seu JavaScript, então certifiquese de estar disposto a forçar todos que contribuem para a sua biblioteca a também terem os mesmos pontos de vistas sobre o JavaScript que você. Max Ogden expõe que utiliza callbacks em 90% do código assíncrono que escreve e quando as coisas se complicam ele utiliza algo como a biblioteca async, que é um módulo que fornece funções poderosas para trabalhar com código JavaScript assíncrono, ela foi projetada para utilizar como o Node.js mas também pode ser utilizada diretamente no navegador.
Leitura adicional
Um padrão para desacoplar eventos DOM (em Inglês)
Módulos em Node.JS
Node tem um sistema simples de carregamento de módulos, a utilização de módulos permite incluir outros arquivos JavaScript em sua aplicação, este sistema utiliza o formáto de módulos CommonJS e de fato grande maioria das funcionalidades do núcleo do Node é implementada utilizando módulos escritos em Javascript, o que significa que você pode ler o código fonte das bibliotecas do núcleo (core) no Github. Módulos são cruciais para construção de aplicações em Node pois eles permitem incluir bibliotecas externas, como bibliotecas de acesso ao banco de dados, e ajudam a organizar seu código em partes separadas com responsabilidades limitadas. Você deve tentar indentificar partes reusáveis do seu código e transformá-las em módulos separados para reduzir a quantidade de código por arquivo e para ficar mais fácil de ler e manter seu código. Utilizar módulos em Node é simples, você usa a função require(), que recebe um argumento: o nome da biblioteca do core ou o caminho do arquivo do módulo que você quer carregar. É aconselhável compreender como funciona a função require do Node.js. Outro artigo aqui também já tratou como utilizar módulos em sua aplicação Node.js. A mais expressiva e simples solução para módulos em JavaScript foi adotada como padrão pelo Node, o padrão CommonJS, que é usado de forma levemente diferente no Node. CommonJS te dá uma boa sintaxe, e mais importante, módulos de primeira classe. E se você estiver se perguntando porque módulos de primeira classe são importantes, a resposta é “pela mesma razão que funções de primeira classe são importantes”. Grupo SoftwareOriginSP
Página 119
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Lembrando que na teoria de linguagens de programação, diz-se que algo é um objeto de primeira classe em uma linguagem quando ele pode ser construído em tempo de execução, passado como parâmetro, atribuído a uma variável, devolvido como resultado de uma função, incluído em uma estrutura de dados maior, etc. Na maioria das linguagens de programação os números e as strings são objetos de primeira classe, mas em muitas linguagens funções não são objetos de primeira classe. Em Javascript, Python, Ruby, C# e muitas outras linguagens, sabemos que funções são objetos de primeira classe, e essa facilidade acaba sendo chamado de “funções de primeira classe”. Em Node, arquivos e módulos tem correspondência de um para um. Observe o exemplo a seguir onde nosso módulo calculo.js carrega o módulo circulo.js salvo no mesmo diretório.
// calculo.js var circulo = require('./circulo.js'); console.log( 'Um circulo de raio 4 tem area de ' + circulo.area(4)); // circulo.js var PI = Math.PI;
exports.area = function (r) { return PI * r * r; };
exports.circunferencia = function (r) { return 2 * PI * r; };
Neste exemplo o módulo circulo.js exportou as funções area() e circunferencia() . Aqui é apresentado o principal conceito do sistema de carregamento módulos do Node, para um módulo exportar um objeto, ou um construtor, basta adicioná-lo no objeto especial exports . Todas as demais variáveis declaradas dentro do módulo serão variáveis privadas do módulo e não serão exportadas, neste exemplo a variável PI é privada do módulo circulo.js e não é acessível no escopo de calculo.js . Grupo SoftwareOriginSP
Página 120
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
É importante frisar que exports é uma referência para module.exports utilizado apenas para o acréscimo de objetos, caso você queira exportar um único item, como um construtor, você vai precisar usar o objeto module.exports diretamente. Acompanhe o exemplo do nosso módulo construtor.js apresentado a seguir.
// construtor.js function MeuConstrutor (opts) { //... }
// CUIDADO A LINHA ABAIXO NAO EXPORTA NADA exports = MeuConstrutor;
// A linha abaixo exporta o construtor module.exports = MeuConstrutor;
Note que a linha exports = MeuConstrutor; não irá ter o comportamento esperado, caso o exemplo parasse nesta linha apenas um objeto vazio seria exportado. Para exportar um único objeto, ou um construtor como neste exemplo, deve-se utilizar a variável module.exports . O sistema de módulos é implementado no módulo require("module") . Importações cíclicas Quando temos chamadas cíclicas de módulos utilizando a função require() um módulo não é retornado enquanto não tiver sido executado por completo, e o módulo será salvo em cache a primeira vez que for carregado. Acompanhe a situação apresentada a seguir para compreender o dinâmica das importações cíclicas.
// a.js console.log('a iniciando'); exports.pronto = false; Grupo SoftwareOriginSP
Página 121
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
var b = require('./b.js'); console.log('dentro de a, b.pronto = %j', b.pronto); exports.pronto = true; console.log('a pronto'); // b.js console.log('b iniciando'); exports.pronto = false; var a = require('./a.js'); console.log('dentro de b, a.pronto = %j', a.pronto); exports.pronto = true; console.log('b pronto'); // principal.js console.log('principal iniciando'); var a = require('./a.js'); var b = require('./b.js'); console.log('dentro de principal, a.pronto=%j, b.pronto=%j', a.pronto, b.pronto); console.log('principal pronto');
Quando o módulo principal.js carrega a.js, por sua vez a.js carrega b.js, neste ponto b.js tenta carregar a.js . Para evitar um cíclo infinito, de a.js carregando b.js e vice-versa, uma cópia incompleta do objeto exports de a.js é retornada para o módulo b.js . Então b.js completa seu carregamento, e seu objeto exports é fornecido para o módulo a.js . Quando principal.js terminar de carregar, ambos os módulos estarão prontos. A saída deste programa seria assim: $ node principal.js principal iniciando a iniciando b iniciando Grupo SoftwareOriginSP
Página 122
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
dentro de b, a.pronto = false b pronto dentro de a, b.pronto = true a pronto dentro de principal, a.pronto=true, b.pronto=true principal pronto Esta foi a forma que os desenvolvedores do Node utilizaram para resolver o problema de importações cíclicas, e caso você tenha dependências cíclicas de módulos no seu programa, se certifique de planejar corretamente para que elas funcionem como o esperado.
Módulos do núcleo do Node
O Node tem muitos módulos compilados em arquivos binários. Estes módulos estão descritos em detalhes na documentação da API do Node - em Inglês. Os módulos do núcleo (em Inglês core) estão dentro da pasta lib/ na raiz do Node. O Node sempre dá preferência em carregar os módulos do núcleo se seu identificador é passado para a função require() . Por exemplo require('http') irá sempre retornar o módulo HTTP embutido no núcelo do Node, mesmo que tenha um arquivo com o mesmo nome nesta pasta.
Arquivo dos Módulos Caso o nome do arquivo passado para a função require() não for encontrado, então o Node irá tentar carregar um arquivo com o nome passado adicionando a extensão ` .js, .json, e por fim .node` . Notar que arquivos ` .js são interpretados como arquivos de texto **JavaScript** e arquivos .json são analisados como arquivos de texto **JSON**. Já arquivos .node são interpretados como um módulo compilado carregado com dlopen` . Um módulo com prefixo ` / ` será buscado no caminho absoluto sistema de arquivos. Por exemplo, require('/home/node/exemplo.js') irá carregar o arquivo presente em ` /home/node/exemplo.js` .
Grupo SoftwareOriginSP
Página 123
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Um módulo com prefixo ` ./ ` será buscado no caminho relativo ao módulo que chamou a função require() . Como no exemplo apresentado, circulo.js deve estar no mesmo diretório de calculo.js para que require('./circulo.js') encontre ele. Já um módulo com prefixo ` ../ ` será buscado na pasta superior relativa ao módulo que chamou a função require() . Caso o módulo requisitado não contenha o prefixo ` / ` ou ` ./ ` para indicar a localização do arquivo o módulo será considerado ou um módulo do núcleo ou um módulo instalado na pasta node_modules, gerenciada pela NPM . Agora se o caminho do arquivo não existir, require() vai emitir um Erro com sua propriedade code igua a 'MODULE_NOT_FOUND' .
Carregando da pasta node_modules
Se o identificador passado para require() não é um módulo nativo, e não começa com ` / , ../ , ou ./ , então Node começa a buscar no diretório pai do módulo atual, e adiciona /node_modules`, e espera carregar o módulo requisitado neste local. Se ele não for encontrado lá, então ele move para o diretório pai do diretório atual, e assim sucessivamente, até atingir o diretório raíz do sistema. Por exemplo, se o arquivo ` /home/node/projetos/exemplo.js chamou require(‘meu_modulo.js’)`, então o Node deve procurar nos seguintes locais, nesta ordem:
/home/node/projetos/node_modules/meu_modulo.js /home/node/node_modules/meu_modulo.js /home/node_modules/meu_modulo.js /node_modules/meu_modulo.js Pastas como Módulos
É conveniente organizar programas e bibliotecas dentro de seus diretórios independentes, e então fornecer um único acesso para esta biblioteca. Há três maneiras que uma pasta pode ser passada para a função require() como um argumento. A primeira é criando um arquivo package.json na raíz da pasta, que especifica o módulo principal - main - e um nome - name . Veja um exemplo do arquivo package.json a seguir: Grupo SoftwareOriginSP
Página 124
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Se este exemplo estivesse na pasta ` ./uma-biblioteca, então uma chamada require(‘./umabiblioteca’) tentaria carregar o arquivo ./uma-biblioteca/lib/uma-biblioteca.js` . Caso não haja um arquivo package.json presente no diretório, então node tentaria carregar um arquivo index.js ou index.node presentes neste diretório. Se no exemplo dado não existisse o arquivo package.json, então uma chamada a require('./uma-biblioteca') tentaria carregar o arquivo ` ./uma-biblioteca/index.js ou ./uma-biblioteca/index.node` .
Módulos salvos em Cache
Os módulos são salvos em Cache após a primeira vez que eles são carregados. Isto significa, entre outras coisas, que toda chamada require('modulo') vai retornar exatamente o mesmo objeto retornado na primeira chamada, se ela fosse importar o mesmo arquivo. Múltiplas chamadas para require('modulo') não pode fazer com que o código do módulo seja executado múltiplas vezes. Esta é uma característica importante. Com isso, objetos podem ser retornados, permitindo assim que dependências transitivas possam ser carregadas mesmo se elas causarem ciclos - isto é, tiverem importações cíclicas. Caso você precise que um módulo execute um código várias vezes, então exporte uma função contendo este código e faça chamadas para esta função.
Advertências para Módulos salvos em Cache
Um módulo é salvo no Cache baseado no seu endereço em disco, por exemplo ` /home/node/projetos/node_modules/meu_modulo.js . Uma vez que uma chamada para a função require(‘meu_modulo’) faz uma busca pelo módulo em vários diretórios, seguindo a sequência passada, caso ela encontre o módulo em uma pasta diferente da já salva em Cache, por exemplo no diretório atual ./meu_modulo.js`, então o módulo será considerado um módulo novo e não será usado a referência já salva em Cache.
Grupo SoftwareOriginSP
Página 125
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
O objeto module
Em cada módulo module é uma variável re referencia para o objeto que representa o módulo atual. Em particular module.exports é acessível através do módulo global exports . O objeto modulo na realidade não é global, mas um objeto local de cada módulo.
module.exports
O objeto module.exports é criado pelo sistema de módulos do Node e a variável exports aponta para este objeto. Seu módulo pode retornar vários objetos e funções simplesmente adicionando-os a variável export, por exemplo exports.falar = function(){ console.log('Bom dia!') }; . Porém algumas vezes gostaríamos que nosso módulo retorne a instância de uma classe. Atribua o objeto desejado para ser exportado em module.exports . Atenção, neste caso não utilize a variável exports . Por exemplo suponha que estivéssemos fazendo um módulo chamado a.js .
// a.js var EventEmitter = require('events').EventEmitter;
module.exports = new EventEmitter();
// Executar algum trabalho, e apos algum tempo emitir // o evento 'pronto' para o proprio modulo. setTimeout(function() { module.exports.emit('pronto'); }, 1000); Então em outro arquivo nós poderíamos fazer: // main.js var a = require('./a'); a.on('pronto', function() { Grupo SoftwareOriginSP
Página 126
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
console.log('O modulo a esta pronto!'); }); É importante saber que atribuições a module.exports devem ser feitas imediatamente, elas não podem ocorrer em nenhum callback. O exemplo a seguir não irá funcionar. // x.js
var x = require('./x'); console.log(x.a); Um módulo Node tem variáveis disponíveis por padrão no escopo de cada módulo, acompanhe a lista abaixo contendo as mais interessantes: __filename: O nome do arquivo do código que está sendo executado __dirname: O nome do diretório que está salvo o script que está sendo executado process: Um objeto que é associado ao presente processo em execução. Além de variáveis, este objeto tem métodos como process.exit, process.cwd e process.uptime process.argv: Um array contendo os argumentos de linha de comando. O primeiro elemento será node, o segundo elemento será o nome do arquivo JavaScript, e os próximos serão todos os argumentos de linha de comandos adicionais, caso sejam atribuídos process.stdin, process.stout, process.stderr: Streams que correspondem à entrada padrão, a saída padrão, e a saída de erro padrão do processo atual process.env: Um objeto contendo as variáveis de ambiente do usuário do processo atual require.main: Quando um arquivo é executado diretemente pelo Node, require.main é atribuído à este module . Execute o exemplo a seguir para que você acompanhar a utilização destas variáveis dentro de um módulo, você pode executar o código abaixo salvando-o em um arquivo exemplo.js e executando através da linha de comando node exemplo.js . Grupo SoftwareOriginSP
Página 127
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
// exemplo.js
console.log('__filename: ', __filename); console.log('__dirname: ', __dirname); console.log('process.argv: ', process.argv); console.log('process.env: ', process.env); if(module === require.main) { console.log('Este e o modulo principal sendo executado.'); }
Carregando seu módulo de pastas globais
Se a variável de ambiente NODE_PATH é definida com uma lista de caminhos absolutos delimitados por dois pontos, então o node vai buscar nestes caminhos por módulos se eles não forem encontrados em nenhum dos locais anteriormente buscados. Lembrando que no Windows para definir uma variável de ambiente você deve acessar as propriedades do Meu Computador, acessar Configurações avançadas do sistema e entrar nas Variáveis de Ambiente, e para adicionar a variável NODE_PATH clique Novo…, adicione o nome da variável como NODE_PATH e no valor da variável você pode inserir uma lista de caminhos absolutos delimitados por ponto e virgula - no caso do Windows. Adicionalmente, node vai buscar nas seguintes localizações: $HOME/.node_modules $HOME/.node_libraries $PREFIX/lib/node Onde $HOME é o diretório Home do usuário e $PREFIX é a configuração node_prefix do Node. Estes são, em maior parte, por razões históricas. Sendo que você sempre será altamente encorajado à colocar suas dependências localmente na pasta node_modules . Elas serão carregadas mais rápido e com mais segurança.
Identificando o módulo principal Grupo SoftwareOriginSP
Página 128
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Quando um arquivo é executado diretamente no Node, require.main é definido como seu module . Isso significa que você pode determinar quando um arquivo está sendo executado diretamente fazendo o teste: require.main === module Para o exemplo apresentado principal.js, esta operação retornará true se executado diretamente via node principal.js, porém retornará false se executado através de uma importação require('./principal.js') Pelo fato do objeto module fornecer a propriedade filename (normalmente equivalente à variável global - diferente para cada módulo - __filemane), você também pode obter o endereço do ponto de entrada da aplicação, o módulo principal, através da variável require.main.filename . Note que se você importar um módulo através do REPL do Node a variável require.main naturalmente retornará undefined, porque não há um módulo principal sendo executado, e sim a linha de comando do Node.
NPM
Não se pode falar em módulos em Node sem falar do NPM, ele é o gerenciador de pacotes incluso dentro do pacote do Node. Não vou entrar neste artigo, mas vocês perceberão que ele é fantástico e você deve usá-lo. Você pode ler mais neste artigo introdutório sobre a NPM e neste outro artigo que fala mais sobre a NPM e como instalar pacotes utilizando ela. Se você não está usando a NPM e os módulos do Node para implementar sua aplicação, você pode estar fazendo isso errado! – Modularizar: Escrever pequenos módulos que fazem apenas uma coisa, e integrá-los para fazer coisas mais complexas – Usar o gerenciador de pacotes: Usar a NPM para administrar suas dependências e parar de se preocupar com elas – Utilizar módulos em Node: É um sistema de módulos simples e expressivo que facilita a modularização de sua aplicação em Node e te proporciona módulos paramétricos de primeira classe
O que é a NPM do Node.JS
NPM é o nome reduzido de Node Package Manager (Gerenciador de Pacotes do Node). A NPM é duas coisas: Primeiro, e mais importante, é um repositório online para publicação de Grupo SoftwareOriginSP
Página 129
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
projetos de código aberto para o Node.js; segundo, ele é um utilitário de linha de comando que interage com este repositório online, que ajuda na instalação de pacotes, gerenciamento de versão e gerenciamento de dependências.. A NPM já conta com mais de 35 mil pacotes (Jul-2013), são bibliotecas e aplicações de código aberto, e muitas são adicionadas todos os dias. Estas aplicações podem ser encontradas através do portal de busca da NPM. Uma vez encontrado o pacote que você deseja instalar, ele pode ser instalado com uma única linha de comando. Digamos que você está trabalhando duro um dia para desenvolver sua Próxima Grande Aplicação. Então você se depara com um problema e decide que está na hora de usar aquela biblioteca legal que você tem ouvido a respeito - vamos usar a biblioteca assincrona de Coalan McMahon como exemplo. Felizmente a NPM é muito simples de usar, você só precisa usar o comando npm install async, e o módulo específico será instalado no diretório atual dentro da pasta ./node_modules/. Uma vez instalada sua pasta node_modules, você será capaz de usar require() nela como se fossem módulos internos do seu projeto. Para compreender melhor a função require() eu aconselho que você leia o artigo que explica como funciona a função require do Node.js. Vamos ver um exemplo de pacote instalado globalmente - vamos falar do coffee-script. O comando para o NPM é simples: npm install coffee-script -g. Para usuários do Linux este comando irá instalar o progama e colocar um link simbólico (symlink) na pasta /usr/local/bin. Isto permitirá você rodar o programa diretamente do console como qualquer outra ferramenta CLI (Command-Line Interface). Neste caso, rodar o comando coffee te permitirá usar o REPL do coffee script. Outro importante uso da NPM é o gerenciamento de dependências. Quando você tem um projeto Node com um arquivo package.json, você pode rodar o comando npm install na pasta raiz do seu projeto e a NPM vai isntalar todas as dependências listadas no package.json. Isto faz a instalação de projetos Node de repositórios Git muito mais fáceis! Vamos usar o exemplo do vows, um dos frameworks de testes do Node, ele pode ser instalado através do Git, e suas dependências: eyes, diff e glob podem ser automáticamente instaladas utilizando uma única linha de comando da NPM. Observe os comandos para clonar o projeto vows do github utilizando o git, e instalar suas dependências listadas no arquivo package.json: git clone https://github.com/cloudhead/vows.git cd vows npm install Após rodar estes comandos, você vai ver a pasta node_modules contendo todas as dependências especificadas no package.json.
Grupo SoftwareOriginSP
Página 130
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Como usar o util.inspect em NodeJS
O NodeJS fornece uma útil função para propósitos de debugação que retorna uma representação em texto de um objeto. util.inspect pode ser um salva-vidas ao se trabalhar com propriedades de objetos grandes e complexos. O módulo util do NodeJS contém uma série de métodos utilizados para formatar texto, debugar, imprimir mensagens no console, checar objetos (util.isArray(objeto), util.isRegExp(objeto), util.isDate(objeto) e util.isError(objeto)) e um método de herança (util.inherits(construtor, superConstrutor)). Neste artigo veremos o método util.inspect. A seguir há um exemplo simples, util.inspect pode ser usado com qualquer objeto e uma boa demonstração pode ser usá-lo nos objetos embutidos do Node. Tente o código abaixo no REPL do Node. Caso você não conheça o REPL, ou não saiba como acessá-lo, eu te aconselho a ler nosso artigo explicando como usar o REPL do Node. > var util = require('util') undefined > util.inspect(console) '{ log: [Function],\n info: [Function],\n warn: [Function],\n error: [Function],\n dir: [Function],\n time: [Function],\n timeEnd: [Function],\n trace: [Function],\n assert: [Function] }' Como você pode se recordar, a cada comando passada para o REPL ele imprime o retorno do comando. O que nos interessa aqui é o retorno do comando util.inspect(console) que pode ser visto na linha abaixo deste comando. Podemos ver que o comando nos retornou uma string contendo todas as funções enumeráveis deste módulo. Então nós sabemos que a função util.inspect retorna uma string contendo as propriedades enumeráveis de qualquer objeto e também é válido acrescentar que a função console.dir é um envólucro em torno de util.inspect, utilizando seus argumentos padrões e imprimindo seu retorno na tela. Ao se imprimir o retorno do util.inspect utilizando a função console.log o console utilizará a formatação contida na string, como a quebra de linhas e a exibição de cores.
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
error: [Function], dir: [Function], time: [Function], timeEnd: [Function], trace: [Function], assert: [Function] } undefined util.inspect pode também ser passada com vários argumentos opcionais, apresentados aqui com seus valores padrões: util.inspect(object, showHidden=false, depth=2, colors=false, customInspect=true); Por exemplo, util.inspect(meuObjeto, true, 7, true, false) poderia inspecionar meuObjeto, mostrando todos as propriedades ocultas e não ocultas até a profundidade 7, colorir a saída e a função inspect() definida no objeto não será chamada. O argumento opcional depth (profundidade em inglês) é o número de níveis de profundidade de um objeto que será feita a recursão, seu valor padrão é 2. Isso é útil para objetos grandes e complicados. Definindo este valor como null vai forçar a recursão até atingir o último nível de profundidade. Para observar seu comportamento você pode inserir os comandos a seguir no REPL do Node.
var http = require('http'); console.log(util.inspect(http, false, 1)); console.log(util.inspect(http, false, 2));
O argumento opicional showHidden é um booleano que determina se vai ou não exibir as propriedades não-enumeradas de um objeto, seu valor padrão é false o que tende a resultar em saídas muito mais legíveis. Isso não é algo que um iniciante deve se preocupar na maior parte do tempo, mas é válido demonstrar rapidamente. Você poderá perceber a diferença executando os comandos a seguir no REPL do Node.
console.log(util.inspect(console, false, 1)) console.log(util.inspect(console, true, 1)) Grupo SoftwareOriginSP
Página 132
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
O argumento opicional color também é um valor booleano que diz se a função deve, ou não, decorar a string de saída com códigos de cores ANSI, as cores são customizáveis. Basicamente ela faz com que, na janela do terminal, a string de retorno possa ser impressa em cores. Faça o teste utilizando o comando a seguir.
var util = require('util'); console.log(util.inspect(console, false, 2, true));
Módulos do Núcleo do Node
Primeiro eu recomendo que uma versão do node esteja instalada no seu computador. Um modo fácil para que isso aconteça e visitando nodejs.org e clique em Install. Node tem um pequeno grupo de módulos que vem com ele por padrão (comumente chamados como ‘núcleo do node’) eles são representados por suas API’s publicas e você utiliza elas para escrever os seu programas. Para trabalhar com sistema de arquivos existe o módulo fs e para redes os módulos são net (TCP), http, dgram (UDP). Em adição ao fs e os módulos de rede existem outros módulos na base do núcleo do node. Lá também temos um modulo assincrono para resolver consultas de DNS chamado dns, um módulo para pegar informações especificas do sistema, por exemplo o tmpdir e ele chama os, um outro para alocação de pedaços de binários na memória chamado buffer, varios módulos para análise de urls e caminhos (url, querystring, path) entre outros. A maioria dos módulos presentes no núcleo do node suportam os principais casos de uso dele: escrever rapidamente programas que conversem com os sistema de arquivos ou rede. Node lida com I/O com: callbacks, eventos, streams e módulos. Se você aprende como essas quatro coisas trabalham vocês esta habil para ir em quanlquer módulo do core do node a ter um entendimento básico sobre como a interface funciona com ele.
Evoluindo de Forma Granular em Node
Como todo boa ferramenta, o Node é bem adequado para certos casos de uso. Por exemplo: Rails, o popular web framework, é ótimo para modelar complexas lógicas de negócio, ex. usando código para representar a vida em um plano objetivado que vivemos físicamente como contas, empréstimos, itinerários e inventários. Embora técnicamente seja possível fazer o mesmo utilizando o Node, haveria desvantagens claras sabendo que o Node é Grupo SoftwareOriginSP
Página 133
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
projetado para resolver problemas de I/O e não saber sobre a parte da ‘lógica de negócio’. Cada ferramenta tem seu foco voltado a resolver diferentes problemas. Esperamos que este guia ajude-o a ganhar uma compreensão intuitiva dos pontos fortes do Node e você saberá quando ele será útil.
O que está fora do escopo do Node?
Fundamentalmente o Node é somente usado como ferramenta para gerenciar I/O ao redor do sitema de arquivos e redes, ele deixa outras funcionalidades mais bonitas com módulos de terceiros. Estas são algumas das coisas fora do escopo do Node:
Web frameworks
Existem uma boa quantidade de web frameworks construidos em cima do Node (framework é um pacote que tenta resolver um problema de alto nível e problemas similares ao modelar a lógica de negócio), mas o Node não é um framework para web. Frameworks web são escritos para serem utilizado no Node e nem sempre tomam o mesmo tipo de decisões sobre a adição de complexidade, abstração e compreensão que o Node faz e podem ter outras prioridades.
Sintaxe da linguagem
O Node usa JavaScript e não muda nada sobre isso. Felix Geisendörfer tem um belo conteúdo escrito sobre o ‘estilo de escrita do Node’ aqui.
Abstração da linguagem
Quando possível o Node vai usar a maneira mais simples de escrever algo. Código mais ‘bonito’ faz do seu JavaScript mais complexo e compromissado com vantagens e desvantagens. Programar é difícil, especialmente em JS onde você tem 1000 soluções para o mesmo problema! Essa é a principal razão para o Node optar pela simplicidade sempre que possível e que pode ser uma opção universal. Se você está resolvendo um problema complexo e você está insatisfeito com o modo que o Node implementa as coisas com soluções originais do JS, Grupo SoftwareOriginSP
Página 134
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
sinta-se livre para resolver isso dentro do seu app ou módulo usando quaisquer abstrações que você preferir. Um grande exemplo de como o Node usa os callbacks. Logo no início foi experimentado a caracteristica chamada ‘promessas’ que adiciona algumas funcionalidades para fazer o código assíncrono parecer mais linear. Ele foi levado para o fora do núcleo do Node por algumas razões: eles são mais complexos que callbacks ele podem ser implementados na userland (distriuído no npm como módulo de terceiros) Considerando uma das mais universais e básicas ideias que o Node faz: ler um arquivo. Quando você lê um arquivo e precisa saber onde os erros acontecem, como exemplo quando o disco rígido morre no meio da sua leitura. Se você tem promessas tudo que você terá é um código como esse: fs.readFile('movie.mp4') .then(function(data) { // faça algo com os dados }) .error(function(error) { // lide com o erro }) Isso adiciona uma complexidade, desnecessária. No lugar de duas funções separadas o Node somente usa uma única função de callback. Aqui temos as regras: Quando não existir erros passe null como primeiro argumento Quando o existir erro, passar ele como primeiro argumento O restante dos argumentos são usados para qualquer coisa (usualmente dados ou respostas já que na maior parte do tempo o Node está lendo ou escrevendo coisas) Por isso, o Node usa o estilo de callback: fs.readFile('movie.mp4', function(err, data) { // lide com o erro, e faça algo com os dados })
Grupo SoftwareOriginSP
Página 135
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Eventos em Node
No node com o módulo de eventos você pode disparar eventos, também chamando de ‘emissor de evento’, até mesmo o node usa em todas as suas APIs para emitir coisas. Eventos são padrões comuns de programação, para conhecer melhor procure por ‘observer pattern’ ou ‘pub/sub’ (publicar/assinar). Ao passo que callbacks são uma relação um-para-um entre algo que espera pelo callback e outra parte que chama o callback, eventos seguem o mesmo padrão com exceção de que eles são uma API de muitos-para-muitos. Aqui temos casos comuns de uso dos eventos ao invés de simples callbacks: Uma sala de chat onde você tem um canal de mensagens com muitos ouvintes. Servidor de um jogo que necessita saber quando os players se ligam, desligam, movem-se, atiram ou pulam Conectores de bancos de dados podem precisar saber onde suas conexões estão abertar, fechadas ou enviando um erro. Se você tentar escrever um servidor de chat que se conecte usando apenas callbacks ele irá parecer com isso: var chatClient = require('my-chat-client')
function onConnect() { // exibe a UI quando conectar-se }
function onConnectionError(error) { // exibe erros para o usuario }
function onDisconnect() { // avisa ao usuario que ele foi desconectado } Grupo SoftwareOriginSP
Página 136
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
function onMessage(message) { // exibe a mensagem na UI da sala }
chatClient.connect( 'http://mychatserver.com', onConnect, onConnectionError, onDisconnect, onMessage ) Como você pode ver é realmente pesado escrever porque você tem que passar todas as funções em uma ordem especifica para a função .connect. Escrevendo isso com eventos irá se parecer com isso: var chatClient = require('my-chat-client').connect()
chatClient.on('connect', function() { // exibe a UI quando conectar-se })
chatClient.on('connectionError', function() { // exibe erros para o usuario })
chatClient.on('disconnect', function() { // avisa ao usuario que ele foi desconectado Grupo SoftwareOriginSP
Página 137
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
})
chatClient.on('message', function() { // exibe a mensagem na UI da sala }) Esta abordagem é bastante similar a utilização com callbacks-puros mas essa abordagem introduz o método .on, onde é atrelado um callback ao evento. Isso significa que você escolhe o que tem que estar assinado para chatClient. Você é capaz de assinar o mesmo evento várias vezes com diferentes callbacks: var chatClient = require('my-chat-client').connect() chatClient.on('message', logMessage) chatClient.on('message', storeMessage)
function logMessage(message) { console.log(message) }
function storeMessage(message) { myDatabase.save(message) } Mais conteúdo sobre eventos em Node está por ser escrito ainda no livro…
Entendendo o Node
Node.js é um projeto de código aberto projetado para auxiliar na escrita de programas em JavaScript que conversem com redes, sistema de arquivos ou outro I/O (entrada/saida, leitura/escrita). É isso o que ele é. Ele é uma simples e estável plataforma de I/O que encoraja você a escrever módulos para funcionar nele.
Grupo SoftwareOriginSP
Página 138
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Quais são alguns exemplos de I/O? Bom… aqui tem um diagrama de uma aplicação que eu fiz com node e ela mostra várias fontes de I/O:
Se você não entende todas as diferentes coisas que estão nesse diagrama, fique tranquilo. O ponto é, mostrar um unico nó de processo (o hexagono no meio) pode agir como o corredor entre todos os diferentes pontos finais de I/O (laranja e roxo representam I/O). Normalmente na construção desses sistemas temas as seguintes situações: Dificuldade de programar, mas tem um super rendimento com resultados rápidos (igual escrever servidores web do zero em C). Facil de programar mas não é o ideal em relação a velocidade/robustez (semelhando ao caso de tentar o upload de um arquivo de 5GB e o seu servidor quebrar) A meta do Node é acertar o balanço entre estes dois: relativamente simples de entender e usar rápido o suficiente para mais casos de uso. Node não é o tanto dos seguintes itens: Um web framework (semelhante ao Rails ou Django, que são utilizadas para certos tipos de coisas) Uma linguagem de programação (ele usa JavaScript mas o node não é uma linguagem) Em vez disso, node é em algum lugar no meio. Assim: Projetado para ser simples e portanto relativamente simples de entender o uso
Grupo SoftwareOriginSP
Página 139
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Normalmente para programas baseados em I/O que precisam ser rápidos e/ou lidar com muitas conexões No baixo nivel, node é descrito como uma ferramente para escrever os seguintes dois maiores tipos de programas: Programas de reder ustilzando protocolos como: HTTP, TCP, UDP, DNS e SSL. Programas de leitura e escrita de dados para o arqivos do sistema ou processes/memôria local. O que é um programa baseado em “I/O”? Aqui estão os pontos comuns do I/O: Bancos de dados (ex. MySQL, PostgreSQL, MongoDB, Redis, CouchDB). APIs (ex. Twitter, Facebook, Apple Push Notifications). Conexões HTTP/WebSocket (de usuários de um aplicativo web). Arquivos (redimensionamento de imagem, editor de videos, rádio online). Node faz o I/O de forma assíncrona asynchronous para lidar com diferentes situações simultaneas. Por exemplo, se você vai até um fast food e faz o pedido de um cheesebuger você tem de imadiato o pedido feito mas não o lanche, então você espara ele ficar pronto para comer. Neste meio tempo outros pedidos estão sendo feitos na lanchonete para outras pessoas. Imagine que você tenha que esperar o registro do seu cheeseburger, bloqueando outras pessoas porque o seu pedido tem que ser feito para o de outra começar enquanto preparam o seu. Isso é chamado de I/O bloqueante porque todo o I/O (cozinhar cheeseburgers) acontece um a um enfileirando tudo. O Node, por sua vez é não-bloqueante, significando que os pedidos serão feitos e entregues quando estiverem prontos. Aqui tem uma lista de coisas divertidas que podem serem feitas com graças ao node por sua natureza não bloqueante: Controle quadicopteros voadores Escrever bots par ao chat IRC Criar robôs bipedes que andam Callbacks em Node É um dos tópicos mais importantes para se entender se você tem que entender como utilizar o node. Quase tudo no node usa callbacks. Eles não foram inventados para o node, somente tem um uso particular com funções do JavaScript. Callbacks são funções que serão executadas de modo assincrono, ou posteriormente. Ao passo que o código for lido de cima para baixo de modo processual, programas assincronos possivelmente executam funções em tempos diferentes baseado na ordem e velocidade em que requisições http ou o trabalho de arquivamento do sistema acontecem. Grupo SoftwareOriginSP
Página 140
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
A diferença pode ser confusa sendo determinada quando uma função é assincrona ou não depende da execução de um grande contexto. Seque um simples exemplo de código assincrono: var myNumber = 1 function addOne() { myNumber++ } // define a função addOne() // executa a função console.log(myNumber) // mostra na saida padrão o numero 2 O código acima define uma função e na linha seguinte chama essa função, sem esperar por nada. Onde ela é chamada imediatamente adicionando 1 a váriavel myNumber, então depois que a função foi chamada você espera que o numero seja 2 na váriavel myNumber. Supondo que precisamos armazenar nosso numero em um arquivo chamado number.txt:
var fs = require('fs') // require é uma função especial do node para requisitar os módulos var myNumber = undefined // não sabemos ainda qual valor esta armazenado no arquivo
function addOne() { fs.readFile('./number.txt', function doneReading(err, fileContents) { myNumber = parseInt(fileContents) myNumber++ }) }
addOne()
console.log(myNumber) // mostra na saida padrão `undefined` Porque é que temos undefined quando pedimos para mostrar o numero dessa vez? Nesse código usamos o método fs.readFile, que acontece de módo assincrono. Usualmente você tem que se comunicar com os discos rigidos ou redes bem de modo assincrono. Se for somente para acesso a memória ou fazer algo na CPU isso é feito bem de modo sincrono. A razão pela Grupo SoftwareOriginSP
Página 141
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
qual fazer o I/O assincrono é porque o acesso ao disco é 100,000 vezes mais devagar do que comunicar-se com a memória (RAM). Onde você executa um programa onde as funções são imediatamente definidas, mas não executa elas imediatamente. Este é um conceito fundamental para entender sobre a programação assincrona. Onde addOne é chamada fora de fs.readFile e move-se para a proxima tarefa pronta para ser executada e não esperar o disco responder igual a fs.readFile. Se não tem nada para executar o node espera por pendências de operações fs/network terminarem ou pararem de executar saindo da linha de comando. Onde fs.readFile esta completa para ler o arquivo (talves isso demore millisegundos ou segundos e até mesmo minutos dependendo de quanto o disco é rápipdo) executando a função doneReading e dando um erro (claro que se algum tipo de erro acontecer) e o conteúdo do arquivo. A razão pela qual undefined foi mostrado no código acima é que ele é chamado dentro de console.log e fora de fs.readFile mostrando o valor anterior de myNumber e não o conteúdo do arquivo. Se você tem um pedaço de código que precisa ser executado várias e várias vezes ou um tempo depois, o primeiro passo é colocar esse pedaço de código dentro de uma função. Aonde você podera chamar sem precisar escrever ele em todas as partes que for necessário e nomeando da meneira que fique claro aquilo que esta sendo feito. Isso ajuda a dar nomes descritivos para as funções. Callback são somente funções que são executadas de forma tardia. A chave para a compreensão de callbacks é perceber que eles são utilizados quando você não sabe quando alguma operação assincrona estará completa, mas você sabe quando se completará - a ultima linha da função assincrona! A ordem de cima-para-baixo que você declara para callbacks isso não é necessariamnete importante, somente a ordem lógica/hierárquica de assentamento do código. Primeiro divida o código em funções e use callbacks para declarar se uma depende da outra função para encerrar. O método fs.readFile é fornecido pelo node no módulo fs, é assincrono e acontece quando se tem um tempo curto para finalizar. Considerando o que ele faz: ele vai até o sistema operacional, que por sua vez esta rodando em um disco rígido isso pode ou não estar girando a milhares de rotações por minuto. Então ele tem que usar o lazer para ler os dados e enviar atráves das camadas de comunição do sistema de volta para o seu programa em javascript. Você da ao fs.readFile uma função (conhecida como callback) que sera chamada depois de recuperar os dados do sistema de arquivos. Ela coloca os dados recuperados em uma variavel do javascript e passa para sua função (callback) com essa variavél, neste caso a variavel se chama fileContents porque ela contém o conteúdo do arquivo que foi lido. Pense no restaurante do exemplo do tutorial lá do inicio. Em muitos restaurantes você pega um número e coloca em sua mesa e espera por sua comida. Eles são como os callbacks. Isso deixa claro para o servidor a quem chamar quando o cheeseburger estiver pronto. Grupo SoftwareOriginSP
Página 142
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Colocando console.log em uma função e passando ele para um callback. var fs = require('fs') var myNumber = undefined
function addOne(callback) { fs.readFile('./number.txt', function doneReading(err, fileContents) { myNumber = parseInt(fileContents) myNumber++ callback() } }
function logMyNumber() { console.log(myNumber) }
addOne(logMyNumber) Agora a função logMyNumber que é passada como argumento se torna a variavél callback dentro da função addOne. Depois que fs.readFile terminar a variavél callback é chamada (callback()). Somente funções podem ser chamadas, então se você passar qualquer outra coisa diferente de uma uma função, causará um erro. Onde uma função é chamada no javascript o código dentro dessa função é imediatamente executado. Neste caso nosso log é executado onde callback é atualmente a função logMyNumber. Lembre-se, somente se você definiu uma função não significa que ela será executada. Você tem que chamar uma função para ela acontecer. Para quebrar esse exemplo em mais pedaços, aqui tem uma linha do tempo de eventos que acontecem quando o seu programa é executado: 1: o código é analisado, isso significa que se existir algum erro de sintaxe o pragrama quebrará e será apontado aonde isso aconteceu.
Grupo SoftwareOriginSP
Página 143
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
2: addOne será chamado, onde logMyNumber será passado com uma função chamada callback, que é o que precisa ser chamado quando addOne estiver completa. Imediatamante disparando o método assincrono fs.readFile. Essa parte do programa leva um tempo para terminar. 3: com nada para fazer, o node espera por um tempo até o fs.readFile encerrar a sua execução. 4: fs.readFile termina e chama o callback, doneReading, que incrementa o numero e imediatamente chama a função logMyNumber contida na variável callback. Talves a parte mais confusa de se programar com callbacks é que as funções são somente objetos armazenados em variáveis e passadas em todo o programa com diferentes nomes. Dando nomes simples e descritivos para suas variáveis faz seu código ser mais legível para outros. Geralmente falando em programas no node onde você enxerga uma variável como callback ou cb você assume ela como uma função. Você talves tenha escutado alguns termos como programação evencionada ou ciclo de eventos. Onde é referenciado da mesma maneira que fs.readFile foi implementada. Node primeiramente despacha a operação fs.readFile e espera por fs.readFile enviar um evento para concluir. Equanto a resposta é esperada o node vai buscando checar outras coisas. Dentro do node há uma lista de coisas a serem feitas mas não informaram ainda, então o ciclo do node acaba e retorna para a lista várias vezes checando se o que estava sendo processado terminou. Depois do termino ele pega o que foi ‘processado’, ex. callbacks que dependem desse termino são chamados. Aquie temos uma versão de um pseudocódigo do exemplo acima: function addOne(thenRunThisFunction) { waitAMinute(function waitedAMinute() { thenRunThisFunction() }) }
addOne(function thisGetsRunAfterAddOneFinishes() {}) Imagine que tenha 3 funções assincronas a, b e c. Para cada uma leva-se 1 minuto de execução e depois de terminado elas chamam um callback (que é passado como primeiro argumento). Se você tem que falar para o node ‘comece executando a, depois b depois que a terminar, e executar c então b termina’ isso passa a ser: a(function() { Grupo SoftwareOriginSP
Página 144
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
b(function() { c() }) }) Onde esse código será executado, a é imediatamente executado, um minuto depois que ele teminar e chama b, outro minuto depois que ele teminar e chamar c e finalmente depois que 3 minutos se passaram o node para de executar desde que não tenha mais nada para fazer. Isso é definitivamente a maneira mais elegante para escrever o código acima, mas o ponto é esse, se você tem esse código que espera por uma porção de código assincrono terminar, onde é expressado essas dependencias colocando seu código em funções, isso é passado às outras funções como callbacks. A projeção do node requer que você pense de modo não-linear. Considerando essa lista de operações: ler um arquivo processar um arquivo Se você ingênuamente transforma-se isso em um pseudocódigo você teria este resultado: var file = readFile() processFile(file) Esse tipo de linearidade no código (passo-a-passo, em ordem) não é o modo como o node trabalha. Se esse código fosse executado onde readFile e processFile estão sendo chamados ao mesmo tempo. Não faria o menor sentido porque readFile leva um tempo para completar sua execução. Ao passo que você precisa expressar processFile que depende do readFile completo. Esta a exata finalidade dos callbacks! E por causa da forma que o JavaScript trabalha você pode escrever dependencias de diferentes maneiras: var fs = require('fs') fs.readFile('movie.mp4', finishedReading)
function finishedReading(error, movieData) { if (error) return console.error(error) // faça algo com os dados em movieData } Grupo SoftwareOriginSP
Página 145
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Mas você támbem pode estruturar o código dessa maneira que irá funcionar: var fs = require('fs')
function finishedReading(error, movieData) { if (error) return console.error(error) // faça algo com os dados em movieData }
fs.readFile('movie.mp4', finishedReading) Ou até mesmo assim: var fs = require('fs')
fs.readFile('movie.mp4', function finishedReading(error, movieData) { if (error) return console.error(error) // faça algo com os dados em movieData }) Depurando (Debugando) Node.js com o Depurador embutido. Se a versão web do seu aplicativo está funcionando, porém a versão de linha de comando está dando erro. O que está acontecendo de errado? Pode ser difícil de descobrir. Se a aplicação simplesmente terminou sem imprimir nada. Seus try/catch e manipuladores de evento não identificaram nenhum erro e a típica abordagem por console.log() não imprime nada. Então o melhor a se fazer é usar o depurador do Node.js para fazer a depuração do seu código. Uns até falam debugar o código, debugar é uma palavra derivada da palavra em Inglês debugging (ou simplesmente debug). O motor V8 da Google, onde o Node roda, vem com um extenso depurador acessível via protocolo TCP e o Node.js tem um cliente embutido para este depurador. Este é considerado o depurador (cliente de depuração) do Node. Este artigo ensina como usar o depurador do Node para depurar seu script expondo como colocar pontos de interrupção (breakpoints), rodar o REPL do Node no meio da depuração, percorrer pelo código e analizar as saídas do depurador. O básico
Grupo SoftwareOriginSP
Página 146
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
O motor V8 da Google tem um depurador embutido e o Node tem um cliente para acessar este depurador O depurador pode ser iniciado usando executando o node com o argumento debug Na sua aplicação você pode definir pontos de interrupção usando a palavra reservada debugger Você pode adicionar vigilantes para acompanhar o valor de expressões durante a depuração Agora vamos a cada etapa da debugação. Inserindo alguns pontos de interrupção Um ponto de interrupção (breakpoint) é um lugar em seu codigo onde você deseja que sua aplicação pare momentaneamente para que você possa analisar o estado do seu programa naquele ponto do código. Se você não usar um ponto de interrupção você vai ter percorrer o código passo-a-passo até chegar onde você deseja depurar. Então, basicamente, seu script você vai precisar colocar alguns pontos de interrupção em lugares próximos onde você acha que o seu sistema esteja se comportando de maneira indevida. Acompanhe o exemplo depurar.js apresentado abaixo. x = 5; setTimeout(function () { debugger; // Ponto de interrupcao console.log("mundo"); }, 1000); console.log("Ola"); O ponto de interrupção é adicionado no código usando a palavra reservada debugger. Este é um acessório do motor V8, não necessariamente do Node.js. Você pode setar muitos pontos de interrupção no seu código se você precisar. Iniciando o depurador Para iniciar o depurador, rode seu script usando o argumento debug do node, no nosso exemplo: node debug depurar.js. Você vai ser levado ao REPL do depurador debug > e então você vai poder usar o comando help para ver a lista de comandos disponíveis e começar e debugar seu código. Quando você abre o depurador seu inicia pausado na primeira linha e você precisa usar o comando cont para rodar seu código até ser interrompido quando o depurador encontrar a palavra reservada debugger ou você usar o comando pause. $ node debug depurar.js Grupo SoftwareOriginSP
Página 147
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
< debugger listening on port 5858 connecting... ok break in /home/rafadev/git/depurador/depurar.js:1 1 x = 5; 2 setTimeout(function () { 3 debugger; debug> cont < Ola break in /home/rafadev/git/depurador/depurar.js:3 1 x = 5; 2 setTimeout(function () { 3 debugger; 4 console.log("mundo"); 5 }, 1000); debug> next break in /home/rafadev/git/depurador/depurar.js:4 2 setTimeout(function () { 3 debugger; 4 console.log("mundo"); 5 }, 1000); 6 console.log("Ola"); debug> repl Press Ctrl + C to leave debug repl >x 5 > 2+2 4 Grupo SoftwareOriginSP
Página 148
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
debug> next < mundo break in /home/rafadev/git/depurador/depurar.js:5 3 debugger; 4 console.log("mundo"); 5 }, 1000); 6 console.log("Ola"); 7 debug> quit O comando next avalia a próxima linha do seu código e o comando repl permite você avaliar o estado atual do seu programa remotamente através do REPL do Node. Há também outros comandos disponíveis, lembrando que você pode digitar help para lembrar dos comandos disponíveis. Listagem de código e gerando um rastreamento A primeira coisa que você possívelmente vai fazer é se orientar. Onde, exatamente, nós estamos no código? Para fazer isso use a função list. Esta função imprime um segmento formatado do código de onde você está. Esta função recebe como parâmetro um número dizendo quantas linhas de contexto serão impessas. Exemplo de uso: list(2) vai imprimir onde você está mais as 2 linhas anteriores e as 2 posteriores. debug> list(2) 3 4 setTimeout(function () { 5 debugger; 6 console.log("mundo"); 7 }, 1000); Aqui nós podemos ver o contexto da linha corrente que o programa se encontra, então podemos saber aonde o programa encontra-se pausado. O próximo comando a ser avaliado estará marcado com a cor verde, neste exemplo a palavra debugger está em verde. Vigilantes (Watchers)
Grupo SoftwareOriginSP
Página 149
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Você pode vigiar o valor de uma expressão ou de uma variável enquanto depura seu código. Em cada ponto de interrupção os vigilantes serão avaliados no contexto atual e seus valores serão impressos logo depois da origem da interrupção. Para começar a vigiar uma expressão, insira watch("sua_expressao"). O comando watchers imprime os vigilantes ativos. Para deixar de vigiar uma expressão, insira unwatch("sua_expressao"). Movimentação com next, step e out O comando next, como já foi dito, prossegue a execução do programa avaliando a linha a atual e pausando na primeira expressão da próxima linha. Caso a linha a ser avaliada faça referência a algum método de outro módulo, ou até mesmo do core, você também pode depurar o funcionamento interno deste método, basta utilizar a função step que é o abreviamento para “step into this one” que pode ser traduzido para “entrar neste” que basicamente entra para depurar o método internamente. Quando você está depurando internamente o funcionamento deste método, você pode ir utilizando os comandos next e step para rastrear com detalhes o que está ocorrendo “por trás dos panos” depurando até o próprio core do Node, caso você deseje. Já o comando out serve para você voltar para o código que chamou aquele método e pausar para a próxima expressão. Lista de Comandos Movimentação cont, c - Continuar a execução next, n - Próximo passo step, s - Entrar out, o - Sair pause - Pausar a execução de um código (como o botão de pausar nas ferramentas de desenvolvimento) Pontos de interrupção setBreakpoint(), sb() - Setar um ponto de interrupção na linha corrente setBreakpoint(linha), sb(linha) - Setar um ponto de interrupção em uma linha específica setBreakpoint('fn()'), sb(...) - Setar um ponto de interrupção na primeira instrução no corpo da função setBreakpoint('script.js', 1), sb(...) - Setar um ponto de interrupção na primeira linha do script.js
Grupo SoftwareOriginSP
Página 150
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
clearBreakpoint, cb(...) - Remover um ponto de interrupção Também é possível inserir um ponto de interrupção em um arquivo (módulo) que ainda não foi carregado, por exemplo usando o comando setBreakpoint('mod.js', 23) adiciona um ponto de interrupção no módulo mod.js na linha 23. $ node debug main.js < debugger listening on port 5858 connecting to port 5858... ok break in /home/rafadev/git/depurador/main.js:1 1 var mod = require('./mod.js'); 2 mod.hello(); 3 mod.hello(); debug> setBreakpoint('mod.js', 23) Warning: script 'mod.js' was not loaded yet. 1 var mod = require('./mod.js'); 2 mod.hello(); 3 mod.hello(); debug> c break in /home/rafadev/git/depurador/mod.js:23 21 22 exports.hello = function() { 23 return 'hello from module'; 24 }; 25 debug> Informação backtrace, bt - Imprime um registro de chamadas do quadro atual de execução list(5) - Lista o código fonte do script com 5 linhas de contexto (5 linhas antes e 5 depois) Grupo SoftwareOriginSP
Página 151
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
watch(expr) - Adiciona uma expressão para a lista vigiada unwatch(expr) - Remove uma expressão da lista vigiada watchers - Lista todos os vigiados e seus valores (atumaticamente listado em cada ponto de interrupção) repl - Abre o REPL de debugação para avaliação no contexto de depuração do script Controle de execução run - Executa o script (é automaticamente executado quando o depurador é iniciado) restart - Reinicia um script kill - Mata um script Diversos scripts - Lista todos os scripts carregados version - Imprime a versão da engine V8 Vantagens de uso O depurador da V8 pode ser abilitado e acessado tanto iniciando o Node com o sinalizador (flag) de linha de comando --debug como sinalizando um processo existente em Node com SIGUSR1. Uma vez que o processo foi colocado em modo de depuração dessa forma ele pode ser conectado pelo depurador do node. Tanto conectado pelo pid quanto pelo URI para o depurador. A sintaxe é: node debug -p - Conecta com o processo via pid node debug - conecta com o processo via URI tal como localhost:5858 Primeiros passos com Passport e Express em Node.js Passport é um middleware para Node.js que faz a implementação de autenticação em um aplicativo de maneira rápida e fácil. Em aplicações web modernas podem-se ter várias formas de autenticação. Tradicionalmente usuários se logam fornecendo um usuário e uma senha. Com o crescimento das redes socieais, logar-se com um provedor OAuth como o Facebook ou o Twitter tem se tornado métodos populares de autenticação. Passport reconhece que cada aplicação tem requisítos únicos de autenticação. Mecanismos de autenticação, conhecidos como estratégias, são empacotados como módulos individuais. As aplicações podem escolher qual estratégia empregar sem criar dependências desnecessárias.
Grupo SoftwareOriginSP
Página 152
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Sabendo que muitos usuários preferem se logar utilizando uma já conta existente em uma rede social, como o Facebook ou Twitter. Neste exemplo vai ser implementado o suporte para o usuário se logar com a sua conta do Twitter. Instalando as dependências $ npm install express $ npm install passport $ npm install passport-twitter $ npm install connect-ensure-login O exemplo consiste em uma aplicação Express simples, baseada na aplicação desenvolvida passo a passo do artigo anterior, então para melhor acompanhamento se certifique de ter compreendido os conceitos básicos já apresentados. Passport tem uma arquitetura modular que quebra o mecanismo de autenticação em estratégias (neste caso do Twitter) que são distribuídas separadamente, mantendo o núcleo leve. Também será usado o connect-ensure-login para proteger rotas exclusivas para usuários logados. Checklist da autenticação São necessários verificar três itens para implementar a autenticação. Configurar o middleware de sessão Configurar as estratégias de autenticação Adicionar as rotas de autenticação Estes tópicos serão detalhados a seguir, começando por: rotas para o middleware. Rotas para autenticação Vamos adicionar rotas para /account, que mostra para a pessoa detalhes da conta. app.get('/account', ensureLoggedIn('/login'), function(req, res) { res.send('Ola '+ href="/logout">Logout ');
Para acessar esta página o usuário terá que estar logado, ensureLoggedIn vai verificar isso, e caso ele não esteja logado, redireciona o usuário para a página de login. Que é implementada a seguir. app.get('/login', function(req, res) { res.send('Login com Twitter'); }); Simples assim. O usuário pode clicar no link e se logar com sua conta do Twitter. O que nos leva à próxima etapa da nossa implementação: Configurar a autenticação do Twitter A autenticação do Twitter usa o padrão de autorização OAuth, que significa que você vai precisar obter uma chave (key) e uma chave secreta (secret) do Twitter. Se você não tem uma ainda, você vai precisar registrar sua aplicação no Twitter. Para nosso que nosso exemplo funcione em seu servidor local, você pode completar o campo Website com http://127.0.0.1:3000/ e o campo Callback URL com http://127.0.0.1:3000/auth/twitter/callback. Uma vez que você tenha as chaves, configure a estratégia de autenticação do Twitter assim: var TWITTER_CONSUMER_KEY = "INSIRA_SUA_KEY_AQUI"; var TWITTER_CONSUMER_SECRET = "INSIRA_SUA_SECRET_AQUI";
passport.use(new TwitterStrategy({ consumerKey: TWITTER_CONSUMER_KEY, consumerSecret: TWITTER_CONSUMER_SECRET, callbackURL: "http://127.0.0.1:3000/auth/twitter/callback" }, function(token, tokenSecret, profile, done) { // NOTA: Voce tera, provavelmente, que associar o usuario do Twitter //
com um registro do usuario no banco de dados da aplicacao.
var user = profile; return done(null, user); Grupo SoftwareOriginSP
Página 154
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
} )); Esta função fornecida para a estratégia é conhecida como “callback de verificação”. Callbacks de verificação recebem credenciais como argumentos (neste caso o token, tokenSecret e profile), que são usados para localizar e retornar registros do usuário. A instància user retornada vai ser definida no request em req.user para identificar o usuário logado. Na maioria das aplicações, você vai precisar associar a conta do Twitter com um registro do usuário no banco de dados da aplicação. Isso permite, também, você associar outras contas (como as do Facebook) no mesmo usuário, permitindo eles se logarem usando ambos os serviços. Para manter este exemplo simples vai ser utilizado os dados do profile diretamente, dispensando o uso de associações com o banco de dados. O protocolo OAuth usado pelo Twitter envolve um processo de dois passos usando redirecionamentos para a troca e verificação dos tokens. Isto é bastante complicado, mas o middleware Passport faz esta tarefa fácil. Apenas adicione as seguintes rotas: app.get('/auth/twitter', passport.authenticate('twitter')); app.get('/auth/twitter/callback', passport.authenticate('twitter', { successReturnToOrRedirect: '/account', failureRedirect: '/login' })); A primeira rota vai começar a transação OAuth e redirecionar o usuário para o Twitter. Uma vez logado, o Twitter vai redirecionar o usuário de volta para nossa aplicação e Passaport vai trazê-lo de volta para a página original requisitada (ou /). Fácil de mais, mas ainda há mais uma coisa a se fazer. Configurando as sessões A fim de manter usuário logado em nosso sistema, uma aplicação precisa implementar suporte para sessões. Faça isso usando o cookie parser e o middleware de sessões embutidos no Express, e inicializando o Passport. app.use(express.static(__dirname + '/public')); app.use(express.cookieParser()); app.use(express.session({ secret: 'usado para calcular o hash' })); app.use(passport.initialize()); app.use(passport.session()); Quando o usuário se logar o registro do usuário é armazenado na sessão a fim de manter o estado de logado enquanto ele nevega em seu site. Funções de serialização e deserialização são fornecidas para controlar este processo. Grupo SoftwareOriginSP
Página 155
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
passport.deserializeUser(function(obj, done) { done(null, obj); }); Como foi notado acima, se você está criando registros do usuário em seu próprio banco de dados, você pode serializar somente o ID do usuário para minimizar a quantidade de dados armazenados na sessão. Para manter este exemplo simples, todo o registro do usuário foi serializado. Rodando a aplicação Para rodar nossa aplicação basta savá-la em um arquivo, neste exemplo chamado app.js e executá-la passando o nome do arquivo como parâmetro para o node: $ node app.js Seridor Express iniciado na porta 3000 E para confirmar que tudo está funcionando você pode acessar a sua aplicação através de seu navegador pelo endereço http://localhost:3000. Concluindo Se você seguiu o passo-a-passo agora os usuários podem se logar usando suas contas do Twitter. Se você quiser se aprofundar mais nesta ferramenta consulte o guia e acesse o repositório do github para mais detalhes de como ela funciona. Agora se você precisa implementar uma API de autenticação, dê uma olhada nos projetos irmãos do Passport: OAuthorize e OAuth2orize O código completo da aplicação Abaixo segue o código completo da aplicação, basta copiar e colar em um arquivo JavaScript, alterar as strings INSIRA_SUA_KEY_AQUI e INSIRA_SUA_SECRET_AQUI com os dados do seu registro no Twitter e tudo funcionará corretamente. var express = require('express'), passport = require('passport'), TwitterStrategy = require('passport-twitter').Strategy, Grupo SoftwareOriginSP
Página 156
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
var server = app.listen(3000); console.log('Seridor express iniciado na porta %s', server.address().port);
JavaScript princípios básicos
A linguagem JavaScript é uma linguagem de script interpretada com base em objetos. A linguagem JavaScript usa uma sintaxe semelhante à de C e oferece suporte a construções estruturadas, como if...else, for e do...while. As chaves ({}) são usadas para delimitar blocos de instrução. A linguagem oferece suporte a vários tipos de dados, entre eles String, Number, Boolean, Object e Array. Inclui suporte para recursos avançados de data, funções trigonométricas e expressões regulares. JavaScript usa protótipos em vez de classes. Você pode definir um objeto criando uma função construtora. JavaScript é uma linguagem fracamente tipada, o que significa que você não declara os tipos de dados das variáveis explicitamente. Em muitos casos o JavaScript executa conversões automaticamente quando são necessárias. Por exemplo, se você adicionar um número em um item que consiste em um texto (uma cadeia de caracteres), o número é convertido para texto. Escrevendo código JavaScript
Como muitas outras linguagens de programação, JavaScript é organizado em instruções, blocos que consistem em vários conjuntos de instruções relacionadas, e comentários. Em uma instrução você pode usar variáveis, cadeias de caracteres, números, e expressões. Instruções
Um programa em JavaScript é um conjunto de instruções. As instruções do JavaScript combinam expressões de forma a realizar uma tarefa por completo. Grupo SoftwareOriginSP
Página 159
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Uma declaração consiste em uma ou mais expressões, palavras-chave, ou operadores (símbolos). Normalmente uma declaração é escrita em uma única linha, mas também pode ser escrita em duas ou mais linhas. Além disso, duas ou mais declarações podem ser gravadas na mesma linha separando-as com ponto-e-vírgula. Geralmente, cada nova linha inicia uma nova declaração. É uma boa idéia finalizar explicitamente suas declarações.Você faz isso com pontoe-vírgula (;), que é o caractere de fim da declaração de JavaScript . Aqui estão dois exemplos de declarações de JavaScript . Frases após os caracteres //são comentários, partes explicativas dentro do programa. JavaScript
var aBird = "Robin"; // Assign the text "Robin" to the variable aBird. var today = new Date(); // Assign today's date to the variable today. Um grupo de instruções em JavaScript circundado por chaves ({}) é chamado um bloco. As instruções agrupadas em um bloco geralmente podem ser tratadas como uma única instrução. Isso significa que você pode usar blocos na maioria dos locais em que o JavaScript espera uma única instrução. Exceções notáveis incluem os cabeçalhos de repetições for e while. Observe que as únicas declarações em um bloco terminam em ponto-e-vírgula, mas o próprio bloco não. Geralmente, os blocos são usados nas funções e em condições. Observe que ao contrário de C++ e de quaisquer outras linguagens, JavaScript não considera um bloco ser um novo escopo; somente as funções criam um novo escopo. No exemplo a seguir, a cláusula else contém um bloco de instruções cercado por duas chaves. O bloco é tratado como uma única instrução. Além de isso, a função própria consiste em um bloco de instruções circundado por chaves. As instruções abaixo da função estão fora do bloco e portanto não são parte da definição de função. JavaScript
function inchestometers(inches) { if (inches < 0) return -1; else { var meters = inches / 39.37; return meters; } } var inches = 12; var meters = inchestometers(inches); document.write("the value in meters is " + meters);
Grupo SoftwareOriginSP
Página 160
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Comentários
Um comentário de linha única de JavaScript começa com um par de barras (//). Aqui está um exemplo de comentário em uma única linha. JavaScript
var aGoodIdea = "Comment your code thoroughly."; // This is a single-line comment. Um comentário de várias linhas em JavaScript começa com uma barra e um asterisco (/*), e termina com o inverso (*/). JavaScript
/* This is a multiline comment that explains the preceding code statement. The statement assigns a value to the aGoodIdea variable. The value, which is contained between the quote marks, is called a literal. A literal explicitly and directly contains information; it does not refer to the information indirectly. The quote marks are not part of the literal. */ Observação Se você tentar inserir um comentário de várias linhas dentro de outro, JavaScript interpreta o comentário de várias linhas resultante de uma maneira inesperada.* Que marca o fim do comentário de várias linhas inserido é interpretado como o fim do comentário de várias linhas inteira.Isso significa que o texto que segue o comentário de várias linhas inserido não será comentado fora; em vez de isso, será interpretado como código de JavaScript , e irá gerar erros de sintaxe. É recomendável que você escreva todos os seus comentários como blocos de comentários de linha única. Isso permite que você comente para fora de segmentos de código com um comentário de várias linhas posterior. JavaScript
// This is another multiline comment, written as a series of single-line comments. // After the statement is executed, you can refer to the content of the // aGoodIdea variable by using its name. var extendedIdea = aGoodIdea + " You never know when you'll have to figure out what it does.";
Grupo SoftwareOriginSP
Página 161
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Atribuições e igualdade
O sinal de igual (=) é usado em instruções de JavaScript para atribuir valores a variáveis: é o operador de atribuição. O operando esquerdo do operador = é sempre um Lvalue.Exemplos de Lvalues são: variáveis, elementos da matriz, propriedades do objeto. O operador direito do operador = é sempre um Rvalue.Rvalues pode ser um valor arbitrário de qualquer tipo, incluindo o valor de uma expressão.Aqui está um exemplo de uma instrução de atribuição de JavaScript . JavaScript
var anInteger = 3; O compilador interpreta esta instrução JavaScript com o significado: “Atribua valor 3 a anInteger variável,” ou “anInteger usa o valor 3 ". Esteja certo que entende a diferença entre operador de atribuição (=) e == o operador de igualdade ().Quando você deseja comparar dois valores para descobrir se forem iguais, use dois sinais de igual (==).Isso é abordado em detalhes em Fluxo de programa de controle. Expressões
Um valor da expressão de JavaScript pode ser de qualquer tipo válido de JavaScript um número, uma cadeia de caracteres, um objeto, e assim por diante. As expressões mais simples são as literais. Aqui estão alguns exemplos de expressões literais JavaScript. JavaScript 3.9 // numeric literal "Hello!" // string literal false // boolean literal null // literal null value {x:1, y:2} // Object literal [1,2,3] // Array literal function(x){return x*x;} // function literal Expressões mais complicadas podem conter variáveis, chamadas de função, e outras expressões. Você pode combinar expressões para criar expressões complexas usando operadores.Exemplos de operadores são: + () - (adição, subtração, multiplicação) * (), e / (divisão). Aqui estão alguns exemplos de expressões complexas JavaScript . Grupo SoftwareOriginSP
Página 162
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
JavaScript
var anExpression = 3 * (4 / 5) + 6; var aSecondExpression = Math.PI * radius * radius; var aThirdExpression = aSecondExpression + "%" + anExpression; var aFourthExpression = "(" + aSecondExpression + ") % (" + anExpression + ")";
Variáveis (JavaScript)
Em JavaScript, uma variável contém um valor, como "hello" ou 5.Quando você usa a variável, faz referência aos dados que ela representa, por exemplo NumberOfDaysLeft = EndDate – TodaysDate. Você usa variáveis para armazenar, recuperar e manipular valores que aparecem no seu código.Tente dar nomes significativos às variáveis, para que as outras pessoas possam compreender melhor a finalidade do seu código. A primeira vez que uma variável aparece no script é a sua declaração.A primeira menção da variável a configura na memória, para que mais tarde você possa fazer referência a ela no seu script.Você deve declarar variáveis antes de usá-las.Para fazer isso, use a palavra-chave var. JavaScript
// A single declaration. var count; // Multiple declarations with a single var keyword. var count, amount, level; // Variable declaration and initialization in one statement. var count = 0, amount = 100; Se você não inicializar sua variável na instrução var, ela usará automaticamente o valor undefined. Nomeando variáveis
A linguagem JavaScript faz distinção entre maiúsculas e minúsculas.Isso significa que um nome de variável, como myCounter, é diferente do nome de variável MYCounter.Nomes de variáveis podem ser de qualquer comprimento.As regras para a criação de nomes de variável legais são as seguintes:
O primeiro caractere deve ser uma letra ASCII (em maiúscula ou minúscula) ou um caractere de sublinhado (_).Observe que um número não pode ser usado como o primeiro caractere. Os caracteres subsequentes devem ser letras, números ou sublinhados (_). O nome da variável não deve ser uma palavra reservada.
Veja a seguir alguns exemplos de nomes de variável válidos: Grupo SoftwareOriginSP
Página 163
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
_pagecount Part9 Number_Items Veja a seguir alguns exemplos de nomes de variável inválidos: JavaScript
// Cannot begin with a number. 99Balloons // The ampersand (&) character is not a valid character for variable names. Alpha&Beta Quando quiser declarar uma variável e inicializá-la, mas não quiser dar a ela um valor específico, atribua o valor null.Veja um exemplo. JavaScript
var bestAge = null; var muchTooOld = 3 * bestAge; // muchTooOld has the value 0. Se você declarar uma variável sem lhe atribuir um valor, ela terá o valor undefined.Veja um exemplo. JavaScript
var currentCount; // finalCount has the value NaN because currentCount is undefined. var finalCount = 1 * currentCount; O valor null se comporta como o número 0, enquanto undefined se comporta como o valor especial NaN (não é um número).Se você comparar um valor null e um valor undefined, verá que eles são iguais. É possível declarar uma variável sem usar a palavra-chave var na declaração e atribuir um valor para ela.Trata-se de uma declaração implícita. JavaScript
// The variable noStringAtAll is declared implicitly. noStringAtAll = ""; Não é possível usar uma variável que nunca tenha sido declarada. JavaScript
Grupo SoftwareOriginSP
Página 164
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
// Error. Length and width do not yet exist. var area = length * width; Coerção
A linguagem JavaScript é uma linguagem fracamente tipada, ao contrário de linguagens fortemente tipadas como C++. Isso significa que variáveis JavaScript não têm nenhum tipo predeterminado. Em vez de isso, o tipo de uma variável é o tipo do seu valor.Esse comportamento permite que você trate um valor como se ele fosse de um tipo diferente. Em JavaScript, você pode realizar operações em valores de diferentes tipos sem causar uma exceção. O interpretador JavaScript converte implicitamente, ou força, um dos tipos de dados no outro e, em seguida, realiza a operação. As regras para coerção de valores Booleanos, de cadeia de caracteres e de número são as seguintes:
Se você adicionar um número e uma cadeia de caracteres, o número será forçado para uma cadeia de caracteres. Se você adicionar um Booleano e uma cadeia de caracteres, o Booleano será forçado para uma cadeia de caracteres. Se você adicionar um número e um Booleano, o Booleano será forçado para um número.
No exemplo a seguir, um número adicionado a uma cadeia de caracteres resulta em uma cadeia de caracteres. JavaScript
var x = 2000; var y = "Hello"; // The number is coerced to a string. x = x + y; document.write(x); // Output: // 2000Hello Cadeias de caracteres são convertidas automaticamente em números equivalentes para fins de comparação.Para converter explicitamente uma cadeia de caracteres em um número inteiro, use a função parseInt.Para converter explicitamente uma cadeia de caracteres em um número, use a função parseFloat.
Tipos de dados (JavaScript)
No JavaScript, existem três tipos de dados primários, dois tipos de dados compostos e dois tipos de dados especiais.
Grupo SoftwareOriginSP
Página 165
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Tipos de Dados Primários
Os tipos de dados primários (primitivos) são:
Cadeia de caracteres Número Booleano
Tipos de dados compostos
Os tipos de dados compostos (de referência) são:
Object Matriz
Tipos de Dados Especiais
Os tipos de dados especiais são:
Nulo Indefinido
Tipo de dados de cadeia de caracteres
Um valor de cadeia de caracteres é uma cadeia de zero ou mais caracteres Unicode (letras, dígitos e sinais de pontuação).Tipos de dados de cadeia de caracteres são usados para representar texto em JavaScript.Você inclui literais de cadeia de caracteres em seus scripts ao delimitá-los por aspas simples ou duplas.Aspas duplas podem estar contidas em cadeias de caracteres delimitadas por aspas simples, enquanto aspas simples podem estar contidas em cadeias de caracteres delimitadas por aspas duplas.Veja a seguir alguns exemplos de cadeias de caracteres: JavaScript
"Happy am I; from care I'm free!" '"Avast, ye lubbers!" roared the technician.' "45" 'c' Observe que JavaScript não tem um tipo para representar um único caractere.Para representar um único caractere em JavaScript, você cria uma cadeia de caracteres que consiste em apenas um caractere.Uma cadeia de caracteres que contém zero caracteres ("") é uma cadeia de caracteres vazia (de comprimento zero). O JavaScript fornece sequências de escape que você pode incluir em cadeias de caracteres para criar os caracteres que não podem ser digitados diretamente.Por exemplo, \t especifica Grupo SoftwareOriginSP
Página 166
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
um caractere de tabulação.Para obter mais informações, consulte Caracteres especiais (JavaScript). Tipo de Dados de Número
Em JavaScript, não há diferença entre valores de número inteiro e de ponto flutuante; um número JavaScript pode ser qualquer um dos dois (internamente, JavaScript representa todos os números como valores de ponto flutuante). Valores Inteiros Valores inteiros podem ser números inteiros positivos, números inteiros negativos e 0.Eles podem ser representados na base 10 (decimal), na base 16 (hexadecimal) e na base 8 (octal).A maioria dos números em JavaScript é escrita em decimal. Você indica números inteiros hexadecimais ("hex") prefixando-os com um "0x" à esquerda (zero e x|X).Eles podem conter somente dígitos de 0 a 9 e letras de A a F (maiúsculas ou minúsculas).As letras de A a F são usadas para representar, como dígitos únicos, o intervalo de 10 a 15 na base 10.Ou seja, 0xF é equivalente a 15 e 0x10, a 16. Você indica inteiros octais prefixando-os com um "0" (zero) à esquerda.Eles podem conter somente dígitos de 0 a 7.Um número com "0" à esquerda e os dígitos "8" e/ou "9" é interpretado como um número decimal. Números hexadecimais e octais podem ser negativos, mas não podem ter uma parte decimal e não podem ser escritos em notação científica (exponencial). Observação A partir do Modo padrão do Internet Explorer 9, modo padrão do Internet Explorer 10, modo padrão do Explorer 11 e aplicativos da Windows Store, a função parseInt não trata uma cadeia de caracteres que possui um prefixo "0" como um octal.No entanto, quando você não estiver usando a função parseInt, cadeias de caracteres com um prefixo "0" ainda poderão ser interpretadas como octais. Valores de Ponto Flutuante Valores de ponto flutuante podem ser números inteiros com uma parte decimal.Além de isso, eles podem ser expressos em notação científica.Ou seja, um "e" em maiúscula ou minúscula é usado para representar "dez à potência de".JavaScript representa números usando o padrão de ponto flutuante IEEE 754 de oito bytes para representação numérica.Isso significa que você pode escrever números de no máximo 1,79769x10308 e de no mínimo 5x10-324.Um número que contém um ponto decimal e um único "0" antes desse ponto decimal é interpretado como um número de ponto flutuante decimal. Observe que um número que começa com "0x" ou "00" e contém um ponto decimal irá gerar um erro.Veja a seguir alguns exemplos de números JavaScript. Equivalente Número Descrição decimal .0001, 0.0001, Quatro números de ponto flutuante equivalentes. 0.0001 1e-4, 1.0e-4 3.45e2 Um número de ponto flutuante. 345 45 Um inteiro. 45 0378 Um inteiro.Embora se pareça com um número octal (começa 378 Grupo SoftwareOriginSP
Página 167
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
com zero), 8 não é um dígito octal válido e, portanto, o número é tratado como um decimal. Um inteiro octal.Observe que, embora ele pareça ser apenas 0377 uma unidade menor que o número acima, seu valor real é 255 bem diferente. Um número de ponto flutuante.Mesmo começando com zero, 0.0001 0.0001 este não é um número octal, pois possui um ponto decimal. É um erro.Os dois zeros à esquerda marcam o número como N/D (erro do 00.0001 um octal, mas octals não permitem um componente decimal. compilador) 0Xff Um inteiro hexadecimal. 255 0x37CF Um inteiro hexadecimal. 14287 Um inteiro hexadecimal.Observe que o 'e' não é tratado como 0x3e7 999 exponenciação. É um erro.Números hexadecimais não podem ter partes N/D (erro do 0x3.45e2 decimais. compilador) Além disso, o JavaScript contém números com valores especiais.Eles são:
NaN (não é um número).Usado quando uma operação matemática é realizada em dados inadequados, como cadeias de caracteres ou o valor undefined Infinito positivo.Usado quando um número positivo é muito grande para ser representado em JavaScript Infinito negativo.Usado quando um número negativo é muito grande para ser representado em JavaScript 0 positivo e negativo.JavaScript faz distinção entre zeros positivos e negativos.
Tipo de dados Booliano
Enquanto os tipos de dados cadeia de caracteres e número podem ter um número praticamente ilimitado de valores diferentes, o tipo de dados booliano só pode ter dois.Eles são os literais true e false.Um valor booliano é um valor verdadeiro: especifica se a condição é verdadeira ou não. As comparações que você faz nos seus scripts sempre têm um resultado Booliano.Considere a seguinte linha de código JavaScript. JavaScript
y = (x == 2000); Aqui, o valor da variável x é comparado ao número 2000.Se for, o resultado da comparação será o valor booliano true, que é atribuído à variável y.Se x não for igual a 2000, o resultado da comparação será o valor booliano false. Valores boolianos são especialmente úteis em estruturas de controle.O código a seguir combina uma comparação que cria um valor booliano diretamente com uma instrução que usa esse valor.Considere a seguinte amostra de código JavaScript. JavaScript
Grupo SoftwareOriginSP
Página 168
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
if (x == 2000) { z = z + 1; } else { x = x + 1; } A instrução if/else no JavaScript executará uma ação se um valor booliano for true (z = z + 1), e uma ação alternativa se o valor booliano for false (x = x + 1). Você pode usar qualquer expressão como uma expressão comparativa.Qualquer expressão avaliada como 0, nula, indefinida ou uma cadeia de caracteres vazia é interpretada como false.Uma expressão que é avaliada como qualquer outro valor é interpretada como true.Por exemplo, você pode usar uma expressão como: JavaScript
// This may not do what you expect. See below! if (x = y + z) Observe que a linha anterior não verifica se x é igual a y + z, pois apenas um sinal de igual (o operador de atribuição) é usado.Em vez disso, o código anterior atribui o valor y + z à variável x e depois verifica se o resultado da expressão inteira (o valor de x) é zero.Para verificar se x é igual a y + z, você precisará usar o código a seguir. JavaScript
// This is different from the code above! if (x == y + z) Para obter mais informações sobre comparações, consulte Controlando o fluxo de programas. O Tipo de Dados nulo
O tipo de dados null tem apenas um valor em JavaScript: null.A palavra-chave null não pode ser usada como o nome de uma função ou variável. Uma variável que contém null não contém nenhum Número, Cadeia de Caracteres, Booliano, Matriz ou Objeto.Você pode apagar o conteúdo de uma variável (sem a excluir) atribuindo o valor null. Observe que, em JavaScript, null não é o mesmo que 0 (como no caso de C e C++).Observe também que o operador typeof em relatórios do JavaScript relatará valores null como sendo do tipo Object, e não do tipo null.Esse comportamento potencialmente confuso serve para compatibilidade com versões anteriores. O Tipo de Dados indefinido
O valor undefined é retornado quando você usa uma propriedade do objeto que não existe, ou uma variável que tenha sido declarada, mas que nunca tenha tido um valor atribuído a ela. você pode verificar se uma variável existe comparando-a a undefined, embora seja possível verificar se o tipo é undefined comparando o tipo de variável à cadeia de caracteres "indefinida".O exemplo a seguir mostra como descobrir se a variável x foi declarada: Grupo SoftwareOriginSP
Página 169
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
JavaScript
var x; // This method works. if (x == undefined) { document.write("comparing x to undefined "); } . // This method doesn't work - you must check for the string "undefined". if (typeof(x) == undefined) { document.write("comparing the type of x to undefined "); } // This method does work. if (typeof(x) == "undefined") { document.write("comparing the type of x to the string 'undefined'"); } // Output: // comparing x to undefined // comparing the type of x to the string 'undefined' Você também pode comparar o valor indefinido a null.Esta comparação será true se a propriedade for someObject.propnull ou se a propriedade someObject.prop não existir. JavaScript
someObject.prop == null; Para descobrir se uma propriedade de objeto existe, você pode usar o operador in: JavaScript
if ("prop" in someObject) // someObject has the property 'prop' Operadores (JavaScript)
O JavaScript tem uma gama completa dos operadores, incluindo operadores aritméticos, lógicos, bit a bit, de atribuição e também alguns operadores diversificados.Para explicações e exemplos, consulte os tópicos sobre operadores específicos.
Grupo SoftwareOriginSP
Página 170
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Descrição Símbolo NÃO lógico ! Menor que < Maior que > Menor ou igual a <= Maior ou igual a >= Igualdade == Desigualdade != E lógico && OU lógico || Condicional (ternário) ?: Vírgula , Igualdade estrita === Desigualdade estrita !== Operadores bit a bit
Descrição Símbolo NOT bit a bit ~ Deslocamento para a esquerda bit a bit << Deslocamento para a direita bit a bit >> Deslocamento para a direita sem sinal >>> AND bit a bit & XOR bit a bit ^ OR bit a bit | Grupo SoftwareOriginSP
Página 171
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Operadores de Atribuição
Descrição Símbolo Atribuição = Atribuição composta OP= (como += e &=) Operadores diversos
Descrição Símbolo excluir excluir typeof typeof void void instanceof instanceof novo novo em em Igualdade e igualdade estrita
A diferença entre o == (igualdade) e === (igualdade estrita) é que o operador de igualdade forçará valores de diferentes tipos antes de verificar a igualdade.Por exemplo, comparar a cadeia de caracteres "1" com o número 1 fará uma comparação como true.O operador de igualdade estrita, por outro lado, não forçará valores para diferentes tipos e, portanto, a cadeia de caracteres "1" não será comparada como igual ao número 1. Cadeias de caracteres primitivas, números e boolianos são comparados por valor.Se eles tiverem o mesmo valor, serão iguais.Objetos (incluindo Array, Function, String, Número, Boolean, Erro, Date e objetos RegExp) são comparados por referência.Mesmo que duas variáveis desses tipos tenham o mesmo valor, elas só serão iguais se fizerem referência exatamente ao mesmo objeto. Por exemplo: JavaScript
// Two strings with the same value. var string1 = "Hello"; var string2 = "Hello"; // Two String objects with the same value. var StringObject1 = new String(string1); var StringObject2 = new String(string2); if (string1 == string2) document.write("string1 is equal to string2 "); if (StringObject1 != StringObject2) Grupo SoftwareOriginSP
Página 172
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
document.write("StringObject1 is not equal to StringObject2 "); // To compare the values of String objects, use the toString() or valueOf() methods. if (StringObject1.valueOf() == StringObject2.valueOf()) document.write("The value of StringObject1 is equal to the value of StringObject2"); //Output: // string1 is equal to string2 // StringObject1 is not equal to StringObject2 // The value of StringObject1 is equal to the value of StringObject2
Precedência do operador (JavaScript)
A precedência dos operadores descreve a ordem em que as operações são realizadas quando uma expressão é avaliada.As operações com uma precedência maior são realizadas antes daquelas com precedência menor.Por exemplo, a multiplicação é realizada antes da adição. JavaScript Operadores
A tabela a seguir lista os operadores JavaScript ordenados da maior para a menor precedência.Operadores com a mesma precedência são avaliados da esquerda para a direita. Operador Descrição Acesso a campos, indexação de matrizes, chamadas de funções e .[]() agrupamento de expressões ++ -- - ~ ! delete new Operadores unários, tipo de dados de retorno, criação de objetos, typeof void valores indefinidos */% Multiplicação, divisão, divisão de módulo +-+ Adição, subtração, concatenação de cadeias de caracteres << >> >>> Deslocamento de bits Menor que, menor que ou igual a, maior que, maior que ou igual a, < <= > >= instanceof instância de == != === !== Igualdade, desigualdade, igualdade estrita e desigualdade estrita & AND bit a bit ^ XOR bit a bit | OR bit a bit && AND lógico || OR lógico ?: Condicional = OP= Atribuição, atribuição com operação (como += e &=) , Avaliação múltipla Os parênteses são usados para alterar a ordem de avaliação determinada pela precedência dos operadores.Isso significa que uma expressão entre parênteses é totalmente avaliada antes que o seu valor seja usado no restante da expressão. Grupo SoftwareOriginSP
Página 173
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Por exemplo: JavaScript
var result = 78 * 96 + 3; document.write(result); document.write(" "); result = 78 * (9 + 3); document.write(result); // Output: // 7491 // 936 Há três operadores na primeira expressão: =, * e +.De acordo com as regras de precedência de operadores, eles são avaliados na seguinte ordem: *, +, = (78 * 96 = 7488, 7488 + 3 = 7491). Na segunda expressão, o operador ( ) é avaliado primeiro, de modo que a expressão de adição é avaliada antes da multiplicação (9 + 3 = 12, 12 * 78 = 936). O exemplo a seguir mostra uma instrução que inclui uma variedade de operadores e é resolvida como true. JavaScript
var num = 10; if(5 == num / 2 && (2 + 2 * num).toString() === "22") { document.write(true); } // Output: // true Os operadores são avaliados nesta ordem: () para o agrupamento, *, + (dentro de agrupamento), "." para a função, () para a função, /, ==, ===, e &&.
Controlando o fluxo de programas (JavaScript)
Normalmente, instruções em um script JavaScript são executadas uma após a outra, na ordem em que são escritas.Isso é chamado de execução sequencial e é a direção padrão do fluxo do programa. Uma alternativa para a execução sequencial transfere o fluxo do programa para outra parte do script.Ou seja, em vez de executar a próxima instrução na sequência, outra instrução é executada. Para tornar um script útil, essa transferência de controle deve ser feita de maneira lógica.A transferência do controle do programa se baseia em uma decisão, que é o resultado de qual instrução é verdadeira (retornar um booliano true ou false).Você cria uma expressão e, depois, Grupo SoftwareOriginSP
Página 174
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
testa se o seu resultado é verdadeiro.Há dois tipos principais de estruturas de programa que fazem isso. O primeiro é a estrutura de seleção.Você usa para especificar rotas alternativas do fluxo do programa, criando uma junção no seu programa (como uma bifurcação em uma estrada).Há quatro estruturas de seleção disponíveis em JavaScript.
a estrutura de seleção única (if), a estrutura de seleção dupla (if/else), o operador ternário embutido ?: estrutura de seleção múltipla (switch).
O segundo tipo de estrutura de controle de programa é a estrutura de repetição.Você usa para especificar que uma ação deve ser repetida enquanto uma certa condição permanecer verdadeira.Quando as condições da instrução de controle tiverem sido atendidas (em geral depois de um número específico de iterações), o controle será transferido para a instrução seguinte além da estrutura de repetição.Há quatro estruturas de repetição disponíveis em JavaScript.
a expressão é testada no topo do loop (while), a expressão é testada no final do loop (do/while), operam em cada uma das propriedades de um objeto (for/in). repetição controlada por contador (para).
Você pode criar scripts bastante complexos aninhando e empilhando estruturas de controle de seleção e repetição. Um terceiro formato de fluxo de programa estruturado é fornecido pela manipulação de exceções, que não é discutida neste documento. Usando instruções condicionais
JavaScript oferece suporte para instruções condicionais if e if...else.Em instruções if, uma condição é testada e, se ela corresponder ao teste, o código JavaScript relevante será executado.Na instrução if...else, o código diferente será executado se a condição for reprovada no teste.A forma mais simples de uma instrução if pode ser escrita em uma linha, mas instruções if e if...else de várias linhas são muito mais comuns. Os exemplos a seguir demonstram sintaxes que você pode usar com instruções if e if...else.O primeiro exemplo mostra o tipo mais simples de teste booleano.Se (e somente se) o item entre parênteses for avaliado como (ou puder ser forçado como) verdadeiro, a instrução ou o bloco de instruções depois de se será executado. JavaScript
function GetReaction(newShip, color, texture, dayOfWeek) { // The test succeeds if the newShip Boolean value is true. if (newShip) { return "Champagne Bottle"; Grupo SoftwareOriginSP
Página 175
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
} // The test succeeds if both conditions are true. if (color == "deep yellow" && texture == "large and small wrinkles") { return "Is it a crenshaw melon?"; } // The test succeeds if either condition is true. if ((dayOfWeek == "Saturday") || (dayOfWeek == "Sunday")) { return "I'm off to the beach!"; } else { return "I'm going to work."; } } var reaction = GetReaction(false, "deep yellow", "smooth", "Sunday"); document.write(reaction); // Output: I'm off to the beach! Operador condicional
JavaScript também oferece suporte para um formato condicional implícito.Esse formato usa um ponto de interrogação após a condição a ser testada (em vez da palavra if antes da condição).Ele também especifica duas alternativas: uma a ser usada se a condição for atendida e outra se a condição não for atendida.Essas alternativas devem ser separadas por dois pontos. JavaScript
var AMorPM = (theHour >= 12) ? "PM" : "AM"; Se você tiver várias condições a serem testadas e souber que uma delas tem mais chances de ser aprovada ou reprovada do que as outras, será possível usar um recurso chamado de "avaliação em curto circuito" para acelerar a execução do seu script.Quando a linguagem JavaScript avalia uma expressão lógica, ela somente avalia as subexpressões necessárias para se chegar a um resultado. Por exemplo, se você tiver uma expressão E como ((x == 123) && (y == 6)), JavaScript verificará primeiro se x é 123.Se não for, a expressão inteira não poderá ser verdadeira, mesmo que y seja igual a 6.Portanto, o teste para y nunca é feito e JavaScript retorna o valor falso. De maneira semelhante, se apenas uma de várias condições tiver que ser verdadeira (usando o operador ||), o teste será interrompido assim que qualquer uma dessas condições for aprovada no teste.Isso é eficaz quando as condições a serem testadas envolvem a execução de chamadas de função ou outras expressões complexas.Com isso em mente, quando você escrever expressões OR, insira primeiro as condições com maiores chances de serem Grupo SoftwareOriginSP
Página 176
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
verdadeiras.Quando escrever expressões AND, insira primeiro as condições com maiores chances de serem falsas . Uma vantagem de projetar o seu script dessa maneira é que runsecond() não será executado no exemplo a seguir se runfirst() retornar 0. JavaScript
if ((runfirst() == 0) || (runsecond() == 0)) { // some code } Usando Loops
Há várias maneiras de executar repetidamente uma instrução ou um bloco de instruções.No geral, a execução repetitiva é chamada de loop ou iteração.Uma iteração é simplesmente uma única execução de um loop.Em geral, ele é controlado por um teste de uma variável cujo valor é alterado sempre que o loop é executado.JavaScript oferece suporte para quatro tipos de loops: loops for, loops for...in, loops while e loops do...while. Usando para Loops
A instrução for especifica uma variável de contador, uma condição de teste e uma ação que atualiza o contador.Antes de cada iteração do loop, a condição é testada.Se o teste for bemsucedido, o código dentro do loop será executado.Se o teste for malsucedido, o código dentro do loop não será executado, e o programa continuará na primeira linha de código imediatamente após o loop.Depois que o loop é executado, a variável de contador é atualizada antes do início da próxima iteração. Se a condição de loop nunca for atendida, o loop nunca será executado.Se a condição de teste sempre for atendida, um loop infinito será gerado.Embora o primeiro possa ser desejável em certos casos, o último raramente é. Portanto, tenha cautela ao escrever suas condições de loop. JavaScript
// The update expression ("icount++" in the following examples) // is executed at the end of the loop, after the block of // statements that forms the body of the loop is executed, and // before the condition is tested. // Set a limit of 10 on the loop. var howFar = 10; // Create an array called sum with 10 members, 0 through 9. var sum = new Array(howFar); sum[0] = 0; // Iterate from 0 through 9. Grupo SoftwareOriginSP
Página 177
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
var theSum = 0; for(var icount = 0; icount < howFar; icount++) { theSum += icount; sum[icount] = theSum; } // This code is not executed at all, because icount is not greater than howFar. var newSum = 0; for(var icount = 0; icount > howFar; icount++) { newSum += icount; } // This is an infinite loop. var sum = 0; for(var icount = 0; icount >= 0; icount++) { sum += icount; } Usando loops for...in
JavaScript fornece um tipo especial do loop para percorrer todas as propriedades definidas pelo usuário de um objeto ou todos os elementos de uma matriz.O contador de loop em um loop for...in é uma cadeia de caracteres, e não um número.Ele contém o nome da propriedade atual ou o índice do elemento de matriz atual. JavaScript
// Create an object with some properties var myObject = new Object(); myObject.name = "James"; myObject.age = "22"; myObject.phone = "555 1234"; // Enumerate (loop through)_all the properties in the object for (var prop in myObject) { // This displays "The property 'name' is James", etc. document.write("The property '" + prop + "' is " + myObject[prop]); // New line. document.write(" "); } Embora loops for...in pareçam semelhantes a loops For Each...Next VBScript, eles não funcionam da mesma maneira.O JavaScript for...in loop é iterado por propriedades de objetos JavaScript.O loop For Each...Next VBScript é iterado por itens de uma coleção.Para executar um loop nas coleções no JavaScript, você precisará usar o objeto de Objeto Enumerator Grupo SoftwareOriginSP
Página 178
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
(JavaScript), ou se presente, o método forEach do objeto de coleção.Embora alguns objetos, como aqueles no Internet Explorer, ofereçam suporte para ambos os loops For Each...Next VBScript e o JavaScript for...in , a maioria dos objetos não tem esse suporte. Usando loops while
Um loop while é semelhante a um loop for. A diferença é que um loop while não tem uma expressão de atualização ou uma variável de contador interno.Se você quiser controlar a execução repetitiva de uma instrução ou de um bloco de instruções, mas precisar de uma regra mais complexa do que simplesmente "executar este código n vezes", use um loop while.O exemplo a seguir usa o modelo de objeto do Internet Explorer e um loop while para fazer ao usuário uma pergunta simples. JavaScript
var x = 0; while ((x != 5) && (x != null)) { x = window.prompt("What is my favorite number?", x); } if (x == null) window.alert("You gave up!"); else window.alert("Correct answer!"); Observação Como os loops while não têm variáveis explícitas de contador interno, eles são mais vulneráveis a loops infinitos do que os outros tipos de loops.Além disso, como não é necessariamente fácil descobrir onde ou quando a condição do loop é atualizada, é fácil escrever um loop while no qual a condição nunca é atualizada.Por esse motivo, você deve ter cautela ao projetar loops while. Conforme mencionado acima, há também um loop do...while em JavaScript que é semelhante ao loop while, com a diferença de que ele sempre é executado pelo menos uma vez, pois a condição é testada no final do loop, e não no início.Por exemplo, o loop acima pode ser novamente escrito como: JavaScript
var x = 0; do { x = window.prompt("What is my favorite number?", x); } while ((x != 5) && (x != null)); if (x == null) window.alert("You gave up!"); Grupo SoftwareOriginSP
Página 179
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
else window.alert("Correct answer!"); Usando instruções break e continue
Em JavaScript, a instrução break é usada para interromper a execução de um loop se uma certa condição for atendida. (Observe que break também é usada para sair de um bloco switch).A instrução continue pode ser usada para saltar imediatamente para a próxima iteração, ignorando o restante do bloco de código, atualizando ao mesmo tempo a variável de contador se o loop for do tipo for ou for...in. O exemplo a seguir se baseia no exemplo anterior para usar instruções break e continue para controlar o loop. JavaScript
var x = 0; do { x = window.prompt("What is my favorite number?", x); // Did the user cancel? If so, break out of the loop if (x == null) break; // Did they enter a number? // If so, no need to ask them to enter a number. if (Number(x) == x) continue; // Ask user to only enter in numbers window.alert("Please only enter in numbers!"); } while (x != 5) if (x != 5) window.alert("You gave up!"); else window.alert("Correct answer!"); Funções (JavaScript)
As funções JavaScript executam ações. Elas também podem retornar valores.Às vezes, eles são resultados de cálculos ou comparações.Funções também são chamadas de "métodos globais". Elas combinam diversas operações em um nome.Isso permite simplificar seu código.Você pode escrever um conjunto de instruções, nomeá-lo e executar todo o conjunto chamando-o e transmitindo a ele todas as informações necessárias. Grupo SoftwareOriginSP
Página 180
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Você transmite informações a uma função colocando as informações entre parênteses após o nome da função.Trechos de informações que são transmitidos a uma função são chamados de argumentos ou parâmetros.Algumas funções não usam nenhum argumento, enquanto outras usam um ou mais argumentos.Em algumas funções, o número de argumentos depende de como você está usando a função. O JavaScript dá suporte a dois tipos de funções: aquelas que são internas à linguagem e as que são criadas por você. Funções internas
A linguagem JavaScript inclui várias funções internas.Algumas delas permitem que você processe expressões e caracteres especiais, enquanto outras convertem cadeias de caracteres em valores numéricos. Consulte Métodos (JavaScript) para obter informações sobre essas funções internas. Criando suas próprias funções
Você pode criar suas próprias funções e usá-las quando for necessário.Uma definição de função consiste em uma instrução de função e um bloco de instruções JavaScript. A função checkTriplet no exemplo a seguir usa os comprimentos dos lados de um triângulo como argumentos.Ela calcula por meio deles se o triângulo é um triângulo retângulo verificando se os três números constituem um trio de Pitágoras (o quadrado do comprimento da hipotenusa de um triângulo retângulo é igual à soma dos quadrados dos comprimentos dos outros dois lados).A função checkTriplet chama uma das duas outras funções para fazer o teste. Observe o uso de um número muito pequeno ("épsilon") como variável de teste na versão de ponto flutuante do teste.Devido a incertezas e erros de arredondamento em cálculos de ponto flutuante, não é prático testar diretamente se os três números constituem um trio de Pitágoras, a menos que se saiba que os três valores em questão são inteiros.Como um teste direto é mais preciso, o código neste exemplo determina se ele é apropriado e, se for, o utiliza. JavaScript
var epsilon = 0.00000000001; // Some very small number to test against. // The test function for integers. function integerCheck(a, b, c) { // The test itself. if ( (a*a) == ((b*b) + (c*c)) ) return true; return false; } // End of the integer checking function. // The test function for floating-point numbers. function floatCheck(a, b, c) Grupo SoftwareOriginSP
Página 181
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
{ // Make the test number. var delta = ((a*a) - ((b*b) + (c*c))) // The test requires the absolute value delta = Math.abs(delta); // If the difference is less than epsilon, then it's pretty close. if (delta < epsilon) return true; return false; } // End of the floating-poing check function.
// The triplet checker. function checkTriplet(a, b, c) { // Create a temporary variable for swapping values var d = 0; // First, move the longest side to position "a". // Swap a and b if necessary if (b > a) { d = a; a = b; b = d; } // Swap a and c if necessary if (c > a) { d = a; a = c; c = d; } // Test all 3 values. Are they integers? if (((a % 1) == 0) && ((b % 1) == 0) && ((c % 1) == 0)) { // If so, use the precise check. return integerCheck(a, b, c); } else { // If not, get as close as is reasonably possible. return floatCheck(a, b, c); } Grupo SoftwareOriginSP
Página 182
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
} // End of the triplet check function. // The next three statements assign sample values for testing purposes. var sideA = 5; var sideB = 5; var sideC = Math.sqrt(50.001); // Call the function. After the call, 'result' contains the result. var result = checkTriplet(sideA, sideB, sideC); Funções de seta
A sintaxe de função de seta, =>, fornece um método abreviado de especificar uma função anônima.Esta é a sintaxe de função de seta. JavaScript
([arg] [, arg]) => { statements } Os valores à esquerda da seta, que podem ser delimitados por parênteses, especificam os argumentos transmitidos para a função.Um único argumento para a função não requer parênteses.Se nenhum argumento for passado, são necessários parênteses.A definição da função à direita da seta pode ser uma expressão, como v + 1, ou um bloco de instruções entre chaves ({}). Importante A sintaxe de função de seta tem suporte apenas no Microsoft Edge. Não é possível usar o operador new com uma função de seta. Os exemplos de código a seguir mostram o uso da função de seta com expressões como as definições de função.No primeiro exemplo, v é transmitido como o argumento para a expressão.No segundo exemplo, v e i são transmitidos como argumentos para a expressão.
var evens = [2, 4, 6, 8]; // Using standard syntax. var odds = evens.map(function(v) { return v + 1; }); // Using arrow function syntax. // Add one to each value to produce output. var odds = evens.map(v => v + 1); // The following line of code adds the index value to the passed // in value to produce output. // Note: the second argument to the callback function in the map // method is the index value (i). var nums = evens.map((v, i) => v + i); Grupo SoftwareOriginSP
Página 183
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
console.log(odds); console.log(nums); // Output: // [object Array] [3, 5, 7, 9] // [object Array] [2, 5, 8, 11] O exemplo de código mostra o uso da função de seta com um bloco de instruções. JavaScript
var fives = new Array(); // Statement block, re-using nums array from previous example. // Note: The first argument to the callback function in forEach // is the value of the array element (v). nums.forEach(v => { if (v % 5 === 0) fives.push(v); }); console.log(fives); // Output: // [object Array] [5] Diferente das funções padrão, as funções de Seta compartilham o mesmo objeto lexical this que o código ao redor, o que pode ser usado para eliminar a necessidade de soluções alternativas, como var self = this;. O exemplo a seguir mostra que o valor do objeto this dentro da função de seta é o mesmo que o código ao redor (ainda se refere à variável bob variável). JavaScript
var bob = { _name: "Bob", _friends: ["Pete", "Joe", "Larry"], printFriends() { this._friends.forEach(f => console.log(this._name + " knows " + f)); } } // Output: // Bob knows Pete // Bob knows Joe // Bob knows Larry As funções de seta também compartilham o mesmo objeto lexical arguments que o código ao redor (como o objeto this). Grupo SoftwareOriginSP
Página 184
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Parâmetros padrão
Você pode especificar um valor padrão para um parâmetro em uma função atribuindo a ele um valor inicial.O valor padrão pode ser um valor constante ou uma expressão. Importante Parâmetros padrão têm suporte apenas no Microsoft Edge com recursos JavaScript experimentais habilitados (about:flags). No exemplo a seguir, o valor padrão de y é 10 e o valor padrão de z é 20.A função usará 10 como valor de y, a menos que o chamador transmita um valor distinto (ou indefinido) como segundo argumento.A função usará 20 como valor de z, a menos que o chamador transmita um valor distinto (ou indefinido) como terceiro argumento. JavaScript
var val = 20; function f(x, y=10, z=val) { return x + y + z; } console.log(f(3)); console.log(f(3, 3)); console.log(f(3, 3, 3)); // Output: // 33 // 26 // 9 Parâmetros Rest
Parâmetros Rest, especificados pelo operador de espalhamento), permitem que você transforme argumentos consecutivos em uma chamada de função para uma matriz. Parâmetros Rest eliminam a necessidade do objeto arguments.Parâmetros Rest diferem do objeto arguments de diversas maneiras, como:
Um parâmetro rest é uma instância de matriz e, portanto, dá suporte a operações que podem ser executadas em uma matriz. Um parâmetro rest inclui apenas os argumentos consecutivos que não são transmitidos como argumentos separados (nomeados) (por outro lado, o objeto arguments contém todos os argumentos transmitidos para a função).
Importante Parâmetros Rest e o operador de espalhamento têm suporte apenas no Microsoft Edge. Grupo SoftwareOriginSP
Página 185
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
No exemplo de código a seguir, true e "hello" são transmitidos como valores de matriz e armazenados no parâmetro y.O parâmetro rest deve ser o último parâmetro da função. JavaScript
function f(x, ...y) { // y is an array. return x * y.length; } console.log(f(3, "hello", true)); // Output: // 6 Para ver usos adicionais do operador de espalhamento, consulte Operador de Espalhamento.
Objetos e matrizes (JavaScript)
Objetos JavaScript são coleções de propriedades e métodos.Um método é uma função que é membro de um objeto.Uma propriedade é um valor ou um conjunto de valores (no formato de uma matriz ou um objeto) que é membro de um objeto.JavaScript suporta quatro tipos de objetos:
Objetos intrínsecos, como Array e String. Objetos criados por você. Objetos de host, como window e document. Objetos ActiveX.
Propriedades e Métodos Expando
Todos os objetos em JavaScript oferecem suporte a propriedades e métodos expando, que podem ser adicionadas e removidas em tempo de execução.Essas propriedades e métodos podem ter qualquer nome e podem ser identificadas por números.Se o nome da propriedade ou do método for um identificador simples, ele poderá ser escrito depois do nome do objeto com um ponto final, como myObj.name, myObj.age, e myObj.getAge no código a seguir. JavaScript
var myObj = new Object(); myObj.name = "Fred"; myObj.age = 42; myObj.getAge = Grupo SoftwareOriginSP
Página 186
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Se o nome da propriedade ou do método não for um identificador simples ou for desconhecido na ocasião da gravação do script, você poderá usar uma expressão arbitrária entre colchetes para indexar a propriedade.Os nomes de todas as propriedades expando em JavaScript são convertidos em cadeias de caracteres antes de serem adicionados ao objeto. JavaScript
var myObj = new Object(); // Add two expando properties that cannot be written in the // object.property syntax. // The first contains invalid characters (spaces), so must be // written inside square brackets. myObj["not a valid identifier"] = "This is the property value"; // The second expando name is a number, so it also must // be placed inside square brackets myObj[100] = "100"; Para obter informações sobre como criar um objeto a partir de uma definição, consulte Criando objetos (JavaScript). Matrizes como Objetos
No JavaScript, os objetos e as matrizes são manipulados de forma quase idêntica, uma vez que as matrizes são simplesmente um tipo especial de objeto.Os objetos e as matrizes podem ter propriedades e métodos. As matrizes têm uma propriedade length mas os objetos não.Quando você atribui um valor a um elemento de uma matriz cujo índice é maior do que o comprimento (por exemplo, myArray[100] = "hello"), a propriedade length é gerada automaticamente para o novo tamanho.Da mesma forma, se você tornar a propriedade length menor, qualquer elemento cujo índice esteja fora do comprimento da matriz será excluído. JavaScript Grupo SoftwareOriginSP
Página 187
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
// An array with three elements var myArray = new Array(3); // Add some data myArray[0] = "Hello"; myArray[1] = 42; myArray[2] = new Date(2000, 1, 1); document.write("original length is: " + myArray.length); document.write(" "); // Add some expando properties myArray.expando = "JavaScript!"; myArray["another Expando"] = "Windows"; // This will still display 3, since the two expando properties // don't affect the length. document.write("new length is : " + myArray.length); // Output: // original length is: 3 // new length is : 3 Matrizes fornecem métodos para iterar e manipular membros.O exemplo a seguir mostra como obter as propriedades dos objetos armazenados em uma matriz. JavaScript
var myArray = new Array(3); // Add some data for(var i = 1; i <= 3; i++) { myArray[i] = new Date(2000 + i, 1, 1); } myArray.forEach(function (item) { document.write(item.getFullYear()); }); // Output: // 2001 // 2002 // 2003
Grupo SoftwareOriginSP
Página 188
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Matrizes Multidimensionais
O JavaScript não dá suporte direto a matrizes multidimensionais, mas é possível obter o comportamento de matrizes multidimensionais armazenando matrizes dentro dos elementos de outra matriz. (Você pode armazenar qualquer tipo de dados dentro de elementos da matriz, incluindo outras matrizes.) Por exemplo, o código a seguir cria uma tabela de multiplicação para números até 5. JavaScript
// The size of the table. var iMaxNum = 5; // Loop counters. var i, j; // Set the length of the array to iMaxNum + 1. // The first array index is zero, not 1. var MultiplicationTable = new Array(iMaxNum + 1); // Loop for each major number (each row in the table) for (i = 1; i <= iMaxNum; i++) { // Create the columns in the table MultiplicationTable[i] = new Array(iMaxNum + 1); // Fill the row with the results of the multiplication for (j = 1; j <= iMaxNum; j++) { MultiplicationTable[i][j] = i * j; } } document.write(MultiplicationTable[3][4]); document.write(" "); document.write(MultiplicationTable[5][2]); document.write(" "); document.write(MultiplicationTable[1][4]); // Output: // 12 // 10 // 4
Criando objetos (JavaScript)
Grupo SoftwareOriginSP
Página 189
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Há várias maneiras para você pode criar seus próprios objetos em JavaScript.Você pode instanciar diretamente um Objeto Object (JavaScript) e, em seguida, adicionar os seus próprios métodos e propriedades.Ou pode usar a notação literal de objeto para definir o objeto.Você também pode usar uma função de construtor para definir um objeto.Para obter mais informações sobre como usar funções de construtor, consulte Usando construtores para definir tipos. Exemplo
O código a seguir mostra como instanciar um objeto e adicionar algumas propriedades.Nesse caso, apenas o objeto pasta tem as propriedades grain, width e shape. JavaScript
var pasta = new Object(); pasta.grain = "wheat"; pasta.width = 0.5; pasta.shape = "round"; pasta.getShape = function() { return this.shape; }; document.write(pasta.grain); document.write(" "); document.write(pasta.getShape()); // Output: // wheat // round Literais de objeto
Você também pode usar a notação literal de objeto para criar apenas uma instância de um objeto.O código a seguir mostra como instanciar um objeto usando a notação literal de objeto. JavaScript
var pasta = { grain: "wheat", width: 0.5, shape: "round" }; Você também pode usar um literal de objeto dentro de um construtor. Cuidado Os recursos descritos abaixo têm suporte apenas no Microsoft Edge. Grupo SoftwareOriginSP
Página 190
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Em Microsoft Edge, você pode usar a sintaxe abreviada para criar um literal de objeto. JavaScript
var key = 'a'; var value = 5; // Older version var obj1 = { key: key, value: value }; // Edge mode var obj2 = {key, value}; console.log(obj2); // Output: // [object Object] {key: "a", value: 5} O exemplo a seguir mostra o uso da sintaxe abreviada para definir métodos em literais de objeto. JavaScript
// Older versions var obj = { method1: function() {}, method2: function() {} }; // Edge mode var obj = { method1() {}, method2() {} }; Você também pode definir nomes de propriedade dinamicamente em literais de objeto em Microsoft Edge.O exemplo de código a seguir cria um nome de propriedade para um objeto dinamicamente usando a sintaxe set. JavaScript
var propName = "prop_42"; var obj = { value: 0, set [propName](v) { Grupo SoftwareOriginSP
Página 191
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
this.value = v; } } console.log(obj.value); // Runs the setter property. obj.prop_42 = 777; console.log(obj.value); // Output: // 0 // 777 O exemplo de código a seguir cria um nome de propriedade para um objeto dinamicamente usando a sintaxe get. JavaScript
var propName = "prop_42"; var obj = { get [propName]() { return 777; } } console.log(obj.prop_42); // Output: // 777 O exemplo de código a seguir cria uma propriedade computada usando a sintaxe de função de seta para acrescentar 42 ao nome da propriedade. JavaScript
var obj = { [ 'prop_' + (() => 42)() ]: 42 };
Exibindo texto em uma página da Web (JavaScript)
Há vários modos de exibir texto nas páginas da Web. Cada um desses modos apresenta vantagens e desvantagens, e é compatível com usos específicos.
Grupo SoftwareOriginSP
Página 192
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Exibindo texto
A maneira recomendada para exibir o texto é criar um elemento e gravar sua propriedade textContent. JavaScript
<script type="text/javascript"> var div = document.getElementById("textDiv"); div.textContent = "my text"; var text = div.textContent; Nesse exemplo, o valor de text é "my text". Porém, o valor resultante da inclusão ou configuração da propriedade textContent em um nó pai pode incluir o texto dos nós filhos. O exemplo a seguir mostra que a propriedade textContent configurada em um nó filho está incluída no valor da propriedade textContent do nó pai: JavaScript
<script type="text/javascript"> var div = document.getElementById("textDiv"); var nestedDiv = document.getElementById("nested"); nestedDiv.textContent = "nested"; var text = "[" + div.textContent + "]"; Nesse exemplo, o valor de text é "[nested]".
Você também pode criar um elemento e gravar suas propriedades innerHTML ou innerText. A configuração dessas propriedades afeta o texto do elemento em si, mas não dos elementos filhos. No entanto, essas propriedades também apresentam algumas desvantagens: o A propriedade innerText não funciona em todos os navegadores. Por isso, evite usá-la para não ter problemas de compatibilidade. o A propriedade innerText é afetada pelos estilos CSS e não será exibida se o elemento estiver oculto. o A propriedade innerHTML obtém e define nós aninhados e texto. As propriedades que não são seguras podem ser usadas em ataques de injeção
Grupo SoftwareOriginSP
Página 193
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
de script. Além disso, quando essa propriedade é definida como texto sem marcas HTML, todos os nós configurados anteriormente são removidos. Você pode usar o método document.write sem precisar criar um elemento. No entanto, esse método limpa toda a página da Web, o que talvez não seja o resultado desejado. O exemplo a seguir mostra uma das desvantagens do uso de document.write. O script deve mostrar a hora a cada cinco segundos, mas mostra apenas duas vezes. Quando document.write é chamado pela segunda vez, a página já está carregada, e document.write limpa toda a página ao chamar document.open. Nesse momento, a função ShowTime já não existe mais. HTML
<script type="text/javascript"> function ShowTime() { var dt = new Date(); document.write(dt.toTimeString()); // var elem = document.getElementById("divElem"); // elem.textContent = dt.toTimeString(); window.setTimeout("ShowTime();", 5000); } <script type="text/javascript"> ShowTime(); Para corrigir o código acima, remova a linha que contém document.write e remova os comentários das duas linhas do código abaixo dela.
Você também pode usar um alert prompt, ou confirm função, que exibe uma mensagem em uma janela pop-up. Na maioria dos casos, não recomendamos usar janelas pop-up em um navegador da Web. Grande parte dos navegadores modernos têm configurações que bloqueiam as janelas pop-up imediatamente. Com isso, é possível que sua mensagem não seja exibida. Além disso, o uso de janelas pop-up pode criar um loop infinito, o que impede o usuário de fechar a página da Web como de costume.
Grupo SoftwareOriginSP
Página 194
[NODE.JS
UMA NOVA ABORDAGEM DA LINGUAGEM JAVASCRIPT] 20 de março de 2016
Ferramentas de utilizadas:
Nodepad++ Geany Eclipse Visual Studio Code Visual Studio for web Visual Studio 2013