Javascript Subprogramação
Javascript Subprogramação
Aula 09
<Módulo 06/>
Subprogramação
Sumário
• Funções
• Introdução
• Parâmetro
• Múltiplos parâmetros
• Cuidado ! Shadowing
• Valor de retorno
• Métodos de objeto
• Introdução
• Invocando os métodos
• Módulos
• Módulos
• Criando Módulos
• Componentes
• Controle de tempo
Funções
Introdução
Dentro de uma página web, há várias situações onde você poderia querer notificar o usuário sobre o
resultado de ações dele.
Por exemplo, quando ele adiciona uma tarefa numa lista de tarefas, uma notificação dizendo “tarefa
adicionada com sucesso”, ou “falha na criação da tarefa”.
A função alert() pode ser usada para isso, mas a janela de alerta é feia ! E não pode ser customizada
com CSS.
Seria legal se existisse alguma outra função que mostrasse um alerta mais bonito, mas não existe.
Se não existe, podemos criar essa função.
Por exemplo:
function showNotification() {
// Estrutura da notificação:
// <div><div>mensagem</div><div>❌</div></div>
[Link](messageDiv);
[Link](closeButton);
[Link]("click", function () {
[Link]();
});
}
Funções
Invocação de função no Console (só para testar)
Coloque a função anterior dentro de um html vazio e abra no navegador para testar.
Ao abrir a página, evidentemente ela estará vazia.
Mas abra o Console e digite showNotification(), a notificação deverá aparecer:
O que aconteceu foi o seguinte (sequência de passos indicada pelos quadrados rosa):
Funções
Ao digitar no Console o nome da função com parênteses no final showNotification(), os parênteses
têm um significado especial: é o comando de Invocação da função.
Significa que o navegador web vai imediatamente executar o código da função (passos 1 e 2).
No final, o código da função acaba, e o navegador volta para o Console (linha pontilhada no passo 3).
O undefined que aparece no Console é porque essa função não teve nenhum resultado (ainda veremos
sobre resultado de funções).
E o seguinte javascript:
[Link]("button").addEventListener("click", function () {
const text = [Link]("input").value;
showNotification();
const li = [Link]("li");
[Link] = text;
[Link]("ul").appendChild(li);
});
function showNotification() {
// ... código da showNotification,
// trocando a mensagem "Mensagem Aqui"
// por "Sucesso" para fazer mais sentido
}
• No passo 1, o usuário clica no botão “Adicionar” e o código do ouvinte de evento começa a ser
executado.
• No passo 6, as 3 últimas linhas do ouvinte de evento são executadas, e elas inserem a <li>
da tarefa na página.
Funções
Por isso que a técnica de invocar funções no meio do código é chamada de Subprogramação:
Se considerarmos que cada função é uma tarefa, podemos interpretar que a showNotification (azul) é
uma subtarefa do ouvinte de evento (vermelho).
Quando a tarefa principal (ouvinte de evento vermelho) invoca a subtarefa (função showNotification
em azul), a tarefa principal fica congelada esperando a subtarefa retornar (terminar).
E pode haver uma “subsubtarefa” também. Não é o caso do exemplo anterior, mas seria algo assim:
• Uma função (tarefa principal, por exemplo um ouvinte de evento) invoca outra função
(subtarefa).
• Essa outra função invoca outra função (“subsubtarefa”).
Quando a tarefa principal invoca a subtarefa, fica congelada esperando a subtarefa ser executada.
Quando a subtarefa invoca a “subsubtarefa”, fica congelada esperando a “subsubtarefa” ser executada.
Aí a “subsubtarefa” termina e retoma a subtarefa.
Depois a subtarefa termina e retoma a tarefa principal.
[Link]("button").addEventListener("click", function () {
const text = [Link]("input").value;
[Link]("ul").appendChild(li);
Se você testar esse código, verá que ele não dá nenhum erro, mas também não mostra as mensagens
desejadas, e sim "Sucesso" sempre.
Para funcionar como esperado, a showNotification precisa de um tipo especial de variável chamado de
Parâmetro.
Começando pela solução, o código é o seguinte (comentários indicam as mudanças):
Funções
// Parâmetro `message` no meio dos parênteses.
// É uma variável que vai conter uma string.
function showNotification(message) {
const container = [Link]("div");
[Link] = "absolute";
[Link] = "24px";
[Link] = "24px";
[Link] = "300px";
[Link] = "32px";
[Link] = "8px 24px";
[Link] = "flex";
[Link] = "center";
[Link] = "space-between";
[Link] = "#202124";
[Link] = "white";
[Link] = "4px";
[Link] =
"0 1px 3px 0 rgba(60, 64, 67, 0.302), 0 4px 8px 3px rgba(60, 64, 67, 0.149)";
[Link](messageDiv);
[Link](closeButton);
[Link](container);
[Link]("click", function () {
[Link]();
});
}
• Passo 3: a string "Tarefa adicionada com sucesso" é atribuída à variável message dentro
da função.
Esse é um código “implícito”, é assim que um parâmetro funciona.
A variável message (parâmetro) é uma variável local normal da função showNotification.
A única característica especial é que seu valor é definido por quem invocou a função (ouvinte
de evento vermelho).
É praticamente a mesma coisa, mas agora a string é "Tarefa não pode ser vazia".
Em resumo: um parâmetro é uma variável declarada no meio dos parênteses da função: function
showNotification(message) { ... }
Apesar de ser tecnicamente a declaração da variável, você não pode colocar let nem const:
• ❌ function showNotification(let message) . Erro de sintaxe !
• ❌ function showNotification(const message) . Erro de sintaxe !
Implicitamente é um let (portanto você pode trocar o valor da variável dentro da função).
Ao invocar a função, você coloca no meio dos parênteses o valor que deseja atribuir à variável:
showNotification("Tarefa adicionada com sucesso").
O propósito de um parâmetro é oferecer, para quem invoca a função, uma oportunidade de passar
informações para dentro dela (nesse caso a mensagem).
Obs: o parâmetro é variável local da função showNotification, portanto somente a função pode
acessar o parâmetro.
Funções
Múltiplos parâmetros
A caixa de notificação está muito monocromática ! É preto sempre !
Vamos permitir alguma variabilidade, da seguinte forma:
• showNotification(《mensagem》, "success") vai mostrar uma notificação com fundo verde claro
(indicando sucesso)
• showNotification(《mensagem》, "error") vai mostrar uma notificação com fundo vermelho
(indicando erro)
[Link]("button").addEventListener("click", function () {
const text = [Link]("input").value;
[Link]("ul").appendChild(li);
[Link](messageDiv);
[Link](closeButton);
[Link](container);
[Link]("click", function () {
[Link]();
});
}
Funções
Agora temos 3 tipos de notificação:
Note que a invocação da função pode omitir o segundo parâmetro, o que é entendido como equivalente a
undefined.
Nesse caso, dentro da showNotification a variável type tem valor undefined, por isso não é
executado nem o if (type === "success") nem o else if (type === "error").
Na verdade é possível também omitir ambos os parâmetros, invocando a função como
showNotification().
Nesse caso tanto message quanto type terão valor undefined.
Apesar de não dar nenhum erro, já não faz mais sentido, porque a notificação vai aparecer na tela com
texto “undefined”.
Funções
Cuidado ! Passagem por valor e por referência
Esta seção não é essencial, se quiser pode pular e voltar se tiver problemas com o que será descrito aqui.
Nos exemplos anteriores, os parâmetros da função eram do tipo string.
Ao passar valores string para a função, o que ela recebe é uma cópia do valor original, vamos explicar:
Considere o seguinte código hipotético (não tem nenhuma utilidade prática):
function test(message) {
message += ", more text here";
[Link](message);
}
• Passo 2: A variável message recebe a string do passo anterior, portanto text e message cada
uma tem uma cópia diferente da string.
Ou seja, quando a função test inicia a executar, estamos assim:
Funções
• Passo 3: a variável message é alterada, e o [Link] mostra "some text here, more
text here".
Ficamos assim:
• Passo 4: depois de a função retornar e o código original ser retomado, o [Link] mostra
"some text here", comprovando que a variável text não foi impactada pelas manipulações
realizadas dentro da função, pois a variável message na função sempre trabalhou com uma
cópia do valor de text.
Ao fato de que o parâmetro message recebe uma cópia do valor fornecido, damos o nome de Passagem
por Valor.
Isso é o que acontece ao trabalhar com os chamados “tipos primitivos”, que são número, string,
booleano, undefined e null.
Porém, ao trabalhar com objetos (e arrays, ou qualquer tipo de objeto), que não são um tipo primitivo,
não é assim que funciona.
Por exemplo, considere o código seguinte (também meramente ilustrativo):
function test(array) {
[Link](100);
[Link](list);
}
Neste código, list é inicialmente o array [10, 20], e logo em seguida a função é executada.
Neste momento (função iniciou), estamos assim:
Funções
Note que a variável array não é um array independente, mas sim referencia o mesmo array da variável
list.
Por isso ao fazer o [Link](100), ficamos assim:
Funções
E então o [Link] dentro de test imprime [10, 20, 100].
Agora a test retorna e o código original é retomado:
Diferente do que acontecia com strings, a variável list foi sim impactada pelo push realizado dentro da
função test.
O [Link] vai imprimir de novo [10, 20, 100].
Ao fato de que o parâmetro array recebeu não uma cópia de list, mas sim os mesmos dados, damos o
nome de Passagem por Referência.
Isso acontece para o tipo de dados objeto, qualquer tipo de objeto:
• objetos “avulsos”, como {name: "joão", idade: "20"}
• objetos do DOM
• objetos Date
• arrays
• etc.
Funções
Cuidado ! Shadowing
Esta seção não é essencial, se quiser pode pular e voltar se tiver problemas com o que será descrito aqui.
Shadowing (sombreamento) acontece quando uma variável local (seja parâmetro ou não) de uma função
tem o mesmo nome que uma variável global.
Por exemplo no seguinte código hipotético (sem utilidade prática):
function increment(number) {
number++;
[Link](number);
}
Note que há uma variável global chamada number, e a função tem uma variável local (nesse caso, um
parâmetro) também chamado number.
Quando o código inicia (antes de começar a função), estamos assim:
Dentro da função, o nome number sempre vai se referir à variável local, por isso dizemos que ela
“sombreia” a variável global (torna a variável global inacessível dentro da função).
Daí o comando number++ incrementa a variável local, a global não é impactada:
function increment() {
number++;
[Link](number);
}
Suponha que você está programando uma plataforma de blog, onde cada artigo é guardado com a data
na forma ano-mês-dia (como "2024-01-29").
Ao exibir os artigos na página, você gostaria de mostrar a versão “amigável” da data.
Então seria bom ter uma função chamada por exemplo getFriendlyDate (“pegar/calcular data
amigável”), que fizesse a conversão para você.
Ou seja:
• getFriendlyDate("2024-01-29") ⇒ "hoje"
• getFriendlyDate("2024-01-28") ⇒ "ontem"
• getFriendlyDate("2024-01-27") ⇒ "há 2 dias"
• etc.
Em outras palavras, é uma função que faz algo análogo a Number: Number("100") resulta em 100, ou
seja, ela converte o valor pedido para outro valor (string em número).
Já a nossa getFriendlyDate deve converter uma string em outra string.
Para um protótipo, vamos supor que você tem uma <ul> no HTML onde vai exibir os artigos, e seu array
de artigos é:
const articles = [
{ title: "Artigo 1", date: "2024-01-29" }, // Data de hoje
{ title: "Artigo 2", date: "2024-01-28" }, // Data de ontem
{ title: "Artigo 3", date: "2024-01-22" }, // Uma data há mais de uma semana
{ title: "Artigo 4", date: "2024-01-15" }, // Duas semanas atrás
{ title: "Artigo 5", date: "2024-01-01" }, // Quase um mês atrás
{ title: "Artigo 6", date: "2023-12-29" }, // Mais de um mês atrás
{ title: "Artigo 7", date: "2023-12-01" }, // Dois meses atrás
{ title: "Artigo 8", date: "2023-10-29" }, // Três meses atrás
];
Funções
Se a função getFriendlyDate já tivesse sido definida (mas não foi, é justamente isso que vamos fazer
daqui a pouco), o código para exibir esses artigos seria o seguinte:
const ul = [Link]("ul");
// Diferença em milissegundos
const differenceInTime = [Link]() - [Link]();
if (differenceInDays < 1) {
result = "hoje";
} else if (differenceInDays < 2) {
result = "ontem";
} else if (differenceInDays < 7) {
const rounded = [Link](differenceInDays);
const plural = rounded !== 1 ? "dias" : "dia";
result = `há ${rounded} ${plural}`;
} else if (differenceInWeeks < 4) {
const rounded = [Link](differenceInWeeks);
const plural = rounded !== 1 ? "semanas" : "semana";
result = `há ${rounded} ${plural}`;
} else {
const rounded = [Link](differenceInMonths);
const plural = rounded !== 1 ? "meses" : "mês";
result = `há ${rounded} ${plural}`;
}
Com a função pronta, a página HTML funciona da seguinte forma (somente a primeira iteração do loop
por simplicidade, explicação em seguida):
Funções
• No passo 1, a iteração inicia criando o objeto <li>.
• No passo 3, o parâmetro date recebe implicitamente seu valor, e a função começa a executar.
• No passo 4, o código da função é executado e a variável local result adquire o valor "hoje".
• Nos passos 6 e 7, o código dentro do loop define o texto do <li> e o coloca dentro da lista
<ul>
Na próxima iteração do loop, esse diagrama vai se repetir, claramente com outros valores de data.
Mas o processo será o mesmo: a função getFriendlyDate será invocada, ela retornará o valor da
variável result, o qual será recebido pela variável friendlyDate, etc.
Em resumo, se você quer que sua função tenha um resultado, deve usar o comando return
《resultado》.
O resultado pode ser de qualquer tipo de dados: número, string, booleano, objeto, …
getFriendlyDate(date) {
const todayDate = new Date();
[Link](0);
[Link](0);
[Link](0);
[Link](0);
let result;
return result;
},
};
Métodos de objeto
Observe que não tem a palavra function quando uma função é definida como propriedade de um
objeto.
Outra forma de definir seria assim:
const utils = {
showNotification: function (message, type) {
// código
},
Onde usamos funções anônimas. Não há diferença prática entre as duas maneiras de definir as funções
dentro do objeto utils.
Você pode até misturar: definir uma função da primeira maneira e a outra da segunda maneira.
Quando funções são definidas como propriedades, elas são geralmente chamadas de Métodos.
Invocando os métodos
Tendo construído o objeto utils, você pode testar que ele funciona pelo Console:
Note que getFriendlyDate é uma propriedade do objeto utils, então fazemos o acesso usando a
notação de ponto que você já conhece.
Como você pode ver, os métodos são invocados da mesma forma que funções globais (funções definidas
no escopo global como antes): você usa a notação de ponto para acessar o método dentro do objeto,
digita os parênteses que significam a invocação, e coloca os valores dos parâmetros dentro dos
parênteses.
Métodos de objeto
Palavra-chave this dentro de um método
A palavra-chave this também funciona dentro de um método, e tem um significado especial.
Para dar um exemplo, vamos lembrar de uma situação típica nas aplicações web feitas até agora,
tomando como referência a aplicação de lista de tarefas que fizemos no final da aula 9.
Há um array global chamado tasks, contendo tarefas onde cada tarefa é um objeto da forma {id:
1706589563604, title: "lavar a louça", priority: "high"}.
Esse array é sincronizado com o Local Storage: quando a página inicia, ele é carregado do Local Storage,
e toda modificação feita durante o uso da página é salva no Local Storage.
A página permite ao usuário adicionar uma nova tarefa, deletar uma tarefa existente, ou trocar a
prioridade.
Nós podemos simplificar todas essas operações usando um objeto tasksController.
Para começar, não haverá mais um array global tasks, mas sim um objeto global tasksController
onde o array de tarefas será uma propriedade:
const tasksController = {
tasks: [], // colocaremos aqui as tarefas
};
const tasksController = {
tasks: [],
load: function () {
const tasksLocalStorage = [Link]("tasks");
const tasksController = {
tasks: [],
load: function () {
const tasksLocalStorage = [Link]("tasks");
const tasksController = {
tasks: [],
load: function () {
// ...
},
[Link]("tasks", [Link]([Link]));
},
[Link][index].priority = newPriority;
[Link]("tasks", [Link]([Link]));
},
[Link](index, 1);
[Link]("tasks", [Link]([Link]));
},
return [Link][index];
},
};
Métodos de objeto
O método create é usado assim: [Link]("lavar a louça", "normal").
Ele insere um novo objeto no array (com um ID calculado na hora), e já salva no Local Storage.
O método editPriority é usado assim: [Link](1706589563604,
"high").
Ele altera a prioridade do objeto tarefa no array, e já salva no Local Storage.
E o método delete é usado assim: [Link](1706589563604).
Ele deleta o objeto tarefa do array, e salva no Local Storage.
Finalmente, o método getTask é usado assim: [Link](1706589563604).
Ele encontra no array o objeto com o ID solicitado, e retorna esse objeto.
Módulos
Módulos no Javascript são uma forma padronizada de importar e exportar
funcionalidades entre arquivos.
O código Javascript pode ser dividido em várias funções, cada uma com um objetivo específico, tais como
os capítulos de um livro. O conjunto total de funções, trabalhando em conjunto, fazem a totalidade do
sistema.
- acoplamento: os usuários do módulo não precisam saber como o módulo foi contruído, mas
apenas os resultados retornados, de acordos com as entradas
- reutilizável: módulos são geralmente construídos para conter uma lógica única dentro do
sistema como, por exemplo, um módulo responsável com conter a lógica necessária a construção
de um botão sendo, portanto, reutilizáveis em vários locais do sistema
Há uma série de benefícios em usar módulos:
- manutenibilidade: por conterem lógica única e serem reutilizáveis, para realizar alterações nas
funcionalidades desempenhadas pelo módulo, basta apenas alterar o seu código interno, por
exemplo, para alterar o comportamento de todos os botões do sistema, basta alterar o módulo
responsável pela construção do botão
<!DOCTYPE html>
<html lang="pt-br">
<head>
...
</head>
<body>
...
<script type="module" src="[Link]"></script>
</body>
</html>
Após isso, basta configurar os arquivos de cada módulo da página e relacioná-los com as declarações
export e import.
Exportação de módulos
A declaração export é utilizada para exportar valores de um módulo Javascript. Os valores exportados
poderão ser importados em outros módulos do sistema.
Exportações nomeadas
A declaração export pode ser adicionada antes de cada valor a ser exportado:
{
pi: 3.14159265358979323846,
add: function(v1, v2) { ... },
sub: function(v1, v2) { ... },
mul: function(v1, v2) { ... },
div: function(v1, v2) { ... }
}
Da mesma forma, a declaração export pode ser utilizada à parte, como mostrado abaixo:
As declarações let, const, var, function ou class podem ser utilizadas antes do export.
A declaração default
A declaração default é utilizada para exportar um único valor para o módulo:
[Link] = label;
[Link] = onClick;
return e
}
Observe que não foi necessário nomear a função. Porém, também é possível exportar um valor nomeado
como default.
[Link] = label;
[Link] = onClick;
return e
}
Importação de módulos
Valores exportados em módulos Javascript podem ser importados onde forem necessários, com a
utilização da declaração import:
A declaração from indica o caminho do arquivo do módulo que está sendo importado. Essa importação
indica que a estrutura de arquivos do projeto é como mostrada abaixo:
├── project
│ ├── modules
│ │ └── [Link]
│ ├── [Link]
│ └── [Link]
A variável math será um objeto com todos os valores exportados pelo módulo math:
{
pi: 3.14159265358979323846,
add: function(v1, v2) { ... },
sub: function(v1, v2) { ... },
mul: function(v1, v2) { ... },
div: function(v1, v2) { ... }
}
Os valores exportados poderão então ser utilizados no arquivo onde foram importados:
[Link]([Link]); // 3.14159265358979323846
[Link]([Link](1, 2)); // 3
[Link]([Link](2, 1)); // 1
[Link]([Link](1, 2)); // 2
Outra forma de importar os valores exportados é atribuindo cada valor exportado a uma variável, função,
etc., da seguinte forma:
[Link](pi); // 3.14159265358979323846
[Link](add(1, 2)); // 3
[Link](sub(2, 1)); // 1
[Link](mul(1, 2)); // 2
[Link](div(3, 2)); // 1.5
Obseve que cada valor importado passa a ser referenciado com o nome utilizado na importação.
O alias as
A declaração as pode ser utilizado para criar um alias diferente para um valor exportado:
[Link](PI); // 3.14159265358979323846
[Link](add(1, 2)); // 3
[Link](sub(2, 1)); // 1
[Link](mul(1, 2)); // 2
O caractere * indica que todos os valores exportados são inseridos no escopo corrente e que podem ser
referenciados com o alias math.
Componentes
Componentes de Software é o termo utilizado para descrever o elemento de software que encapsula uma
série de funcionalidades. É uma unidade independente, que pode ser utilizada com outros componentes
para formar um sistema mais complexo.
No contexto de uma página Web, cada elemento distinto da página (menu, cards, header, rodapé, etc.)
pode ser construído como um componente.
[Link] = label;
[Link] = onClick;
return e
}
Toda a lógica necessária a construção de um botão é encapsulada dentro deste componente, que pode
ser utilizado onde for necessário na página Web.
Controle de tempo
A habilidade de gerenciar o tempo de forma precisa e dinâmica é essencial na
criação de aplicações web interativas.
Seja para criar animações, agendar tarefas assíncronas, ou controlar eventos em tempo real,
compreender e utilizar eficientemente as capacidades de controle de tempo do JavaScript é fundamental.
Nesta aula, vamos aprender sobre os recursos disponíveis para manipulação de tempo no Javascript.
A função
// greeting greetsgreeting é utilizada
a newly arrived como
person. callback na função welcomePerson.
function greeting(name) {
alert(`Olá, ${name}!`);
}
greetingCallback(personName)
}
Como exemplo, imagine que você esteja configurando um botão que, quando clicado chame uma rotina
qualquer. Também imagine que a rotina está suscetível a retornar erros. O botão deverá se comportar da
seguinte forma:
- se a execução da rotina ocorrer com sucesso, o botão deverá mostrar ao usuário a mensagem
"Rotina executada com sucesso!", e fazer com que a mensagem desapareça depois de 3
segundos
- se a execução da rotina resultar em erro, o botão deverá mostrar ao usuário a mensagem "Erro
ao executar a rotina! Tente novamente mais tarde!" , e fazer com que a mensagem
desapareça depois de 5 segundos
[Link]("button").addEventListener("click", function() {
try {
routine();
showSuccessMessage();
setTimeout(hideMessage,3000);
} catch {
showErrorMessage();
setTimeout(hideMessage,5000);
}
})
clearTimeout(timeoutID);
setInterval
O método setTimeout cria um temporizador e executa uma função repetidamente, com um tempo de
espera fixo entre cada execução.
Um bom exemplo de uso para o setInterval é um painel que se responsabiliza em mostrar a cotação
mais recente de uma moeda, tal como na figura abaixo:
fonte: [Link]
Essa cotação pode ser atualizada em um intervalo de tempo fixo, por exemplo, a cada 30 segundos.
As seguintes funções poderiam ser utilizadas para implementar a funcionalidade, considerando que as
informações necessárias são o valor da cota e o instante de tempo em que foi gerada:
setInterval(function (){
const quote = requestQuote();
[Link](
"#quote-value").textContent = [Link];
[Link](
"#quote-date").textContent = [Link];
}, 30000);
OBS: As funções acima não representam por completo o fluxo de uma requisição web e o tratamento
necessário para as informações recebidas. Essas funções apenas exemplificam a possível solução para o
problema.
clearInterval(intervalID);
O construtor Audio
Para conseguir fazer os exercícios da aula, você precisará conhecer um construtor específico do
Javascript: o construtor Audio.
O construtor Audio retorna um elemento de áudio (HTMLAudioElement) que pode ser anexado ao
documento ou utilizado fora da tela:
Os métodos play e pause do construtor podem ser utilizados, respectivamente, para iniciar e pausar a
execução do áudio.