034.3 Lição 1
Certificação: |
Web Development Essentials |
---|---|
Versão: |
1.0 |
Tópico: |
034 Programação em JavaScript |
Objetivo: |
034.3 Estruturas de controle e funções do JavaScript |
Lição: |
1 de 2 |
Introdução
Como qualquer outra linguagem de programação, o código JavaScript é uma coleção de declarações que informam a um interpretador de instruções o que fazer, em ordem sequencial. No entanto, isso não significa que todas as declarações devam ser executadas apenas uma vez ou que devam ser executadas obrigatoriamente. A maioria das declarações deve ser executada apenas quando determinadas condições específicas forem atendidas. Mesmo quando um script é disparado de forma assíncrona por eventos independentes, muitas vezes ele precisa verificar uma série de variáveis de controle para encontrar a parte certa do código a ser executada.
Declarações If
A estrutura de controle mais simples é fornecida pela instrução if
, que executará a instrução imediatamente posterior a ela se a condição especificada for verdadeira. O JavaScript considera as condições como verdadeiras se o valor avaliado for diferente de zero. Qualquer coisa entre parênteses após a palavra if
(os espaços são ignorados) será interpretado como uma condição. No exemplo a seguir, o número literal 1
é a condição:
if ( 1 ) console.log("1 is always true");
O número 1
foi explicitamente escrito nesta condição de exemplo, então ele é tratado como um valor constante (permanece o mesmo durante a execução do script) e sempre resultará em verdadeiro quando usado como uma expressão condicional. A palavra true
(sem as aspas duplas) também pode ser usada no lugar de 1
, pois também é tratada como um valor verdadeiro literal pela linguagem. A instrução console.log
imprime seus argumentos na janela do console do navegador.
Tip
|
O console do navegador exibe erros, avisos e mensagens informativas enviadas com a instrução |
Embora sintaticamente correto, o uso de expressões constantes em condições não é muito útil. Em uma aplicação real, é aconselhável testar a veracidade de uma variável:
let my_number = 3;
if ( my_number ) console.log("The value of my_number is", my_number, "and it yields true");
O valor atribuído à variável my_number
(3
) é diferente de zero e, portanto, resulta verdadeiro. Mas este exemplo não é de uso comum, porque é raro ser preciso testar se um número é igual a zero. É muito mais comum comparar um valor com outro e testar se o resultado é verdadeiro:
let my_number = 3;
if ( my_number == 3 ) console.log("The value of my_number is", my_number, "indeed");
O operador de comparação duplo igual é usado aqui porque o operador igual já está definido como operador de atribuição. O valor em cada lado do operador é chamado de operando. A ordem dos operandos não importa e qualquer expressão que retorne um valor pode ser um operando. Eis uma lista de outros operadores de comparação disponíveis:
value1 == value2
-
True if
value1
is equal tovalue2
. value1 != value2
-
True if
value1
is not equal tovalue2
. value1 < value2
-
True if
value1
is less thanvalue2
. value1 > value2
-
True if
value1
is greater thanvalue2
. value1 <= value2
-
True if
value1
is less than or equal tovalue2
. value1 >= value2
-
True if
value1
is grater than or equal tovalue2
.
Normalmente, não importa se o operando à esquerda do operador é uma string e o operando à direita é um número, desde que o JavaScript seja capaz de converter a expressão em uma comparação significativa. Portanto, a string que contém o caractere 1
será tratada como o número 1 quando comparada a uma variável numérica. Para garantir que a expressão seja verdadeira apenas se ambos os operandos forem exatamente do mesmo tipo e valor, o operador de igualdade estrita ===
deve ser usado em vez de ==
. Da mesma forma, o operador de desigualdade estrita !==
é avaliado como verdadeiro se o primeiro operando não for exatamente do mesmo tipo e valor do segundo operador.
Opcionalmente, a estrutura de controle if
pode executar uma instrução alternativa quando a expressão for avaliada como falsa:
let my_number = 4;
if ( my_number == 3 ) console.log("The value of my_number is 3");
else console.log("The value of my_number is not 3");
A instrução else
deve seguir imediatamente a instrução if
. Até agora, estamos executando apenas uma declaração quando a condição é atendida. Para executar mais de uma declaração, devemos colocá-las entre chaves:
let my_number = 4;
if ( my_number == 3 )
{
console.log("The value of my_number is 3");
console.log("and this is the second statement in the block");
}
else
{
console.log("The value of my_number is not 3");
console.log("and this is the second statement in the block");
}
Um grupo de uma ou mais declarações delimitadas por um par de chaves é conhecido como declaração de bloco. É comum usar declarações de bloco mesmo quando há apenas uma instrução a executar, a fim de tornar o estilo de codificação consistente em todo o script. Além disso, o JavaScript não exige que as chaves ou quaisquer declarações estejam em linhas separadas, mas esse procedimento melhora a legibilidade e facilita a manutenção do código.
As estruturas de controle podem ser aninhadas umas nas outras, mas é importante não misturar as chaves de abertura e fechamento de cada declaração de bloco:
let my_number = 4;
if ( my_number > 0 )
{
console.log("The value of my_number is positive");
if ( my_number % 2 == 0 )
{
console.log("and it is an even number");
}
else
{
console.log("and it is an odd number");
}
} // end of if ( my_number > 0 )
else
{
console.log("The value of my_number is less than or equal to 0");
console.log("and I decided to ignore it");
}
As expressões avaliadas pela instrução if
podem ser mais elaboradas do que simples comparações. Este é o caso do exemplo anterior, no qual a expressão aritmética my_number % 2
foi empregada entre parênteses no if
aninhado. O operador %
retorna o resto após dividir o número à sua esquerda pelo número à sua direita. Os operadores aritméticos, como %
, têm precedência sobre os operadores de comparação como ==
, de forma que a comparação usará o resultado da expressão aritmética como operando esquerdo.
Em muitas situações, é possível combinar estruturas condicionais aninhadas em uma única estrutura usando operadores lógicos. Se estivéssemos interessados apenas em números pares positivos, por exemplo, uma única estrutura if
poderia ser usada:
let my_number = 4;
if ( my_number > 0 && my_number % 2 == 0 )
{
console.log("The value of my_number is positive");
console.log("and it is an even number");
}
else
{
console.log("The value of my_number either 0, negative");
console.log("or it is a negative number");
}
O operador "e" comercial duplo &&
na expressão avaliada é o operador lógico AND. Ela é avaliada como verdadeira apenas se a expressão à sua esquerda e a expressão à sua direita forem avaliadas como verdadeiras. Se você quiser combinar números que sejam ou positivos ou pares, o operador ||
deve ser usado, representando o operador lógico OR:
let my_number = -4;
if ( my_number > 0 || my_number % 2 == 0 )
{
console.log("The value of my_number is positive");
console.log("or it is a even negative number");
}
Neste exemplo, apenas os números ímpares negativos não corresponderão aos critérios impostos pela expressão composta. Se você tiver a intenção oposta, ou seja, encontrar correspondências apenas dentre os números ímpares negativos, adicione o operador lógico NOT !
ao início da expressão:
let my_number = -5;
if ( ! ( my_number > 0 || my_number % 2 == 0 ) )
{
console.log("The value of my_number is an odd negative number");
}
O acréscimo do parêntese na expressão composta força a expressão que está entre eles a ser avaliada primeiro. Sem esses parênteses, o operador NOT se aplicaria apenas a my_number > 0
e, em seguida, a expressão OR seria avaliada. Os operadores &&
e ||
são conhecidos como operadores lógicos binários, porque requerem dois operandos. !
é conhecido como um operador lógico unário, porque requer apenas um operando.
Estruturas Switch
Embora a estrutura if
seja bastante versátil e suficiente para controlar o fluxo do programa, a estrutura de controle switch
pode ser mais adequada quando se trata de avaliar resultados diferentes de verdadeiro ou falso. Por exemplo, se quiséssemos realizar uma ação distinta para cada item escolhido em uma lista, seria necessário escrever uma estrutura if
para cada avaliação:
// Available languages: en (English), es (Spanish), pt (Portuguese)
let language = "pt";
// Variable to register whether the language was found in the list
let found = 0;
if ( language == "en" )
{
found = 1;
console.log("English");
}
if ( found == 0 && language == "es" )
{
found = 1;
console.log("Spanish");
}
if ( found == 0 && language == "pt" )
{
found = 1;
console.log("Portuguese");
}
if ( found == 0 )
{
console.log(language, " is unknown to me");
}
Neste exemplo, uma variável auxiliar found
é usada por todas as estruturas if
para descobrir se uma correspondência foi encontrada. Em um caso como este, a estrutura switch
realizaria a mesma tarefa, mas de forma mais sucinta:
switch ( language )
{
case "en":
console.log("English");
break;
case "es":
console.log("Spanish");
break;
case "pt":
console.log("Portuguese");
break;
default:
console.log(language, " not found");
}
Cada case
aninhado é chamado de cláusula. Quando uma cláusula corresponde à expressão avaliada, ela executa as instruções que estão após os dois pontos até a instrução break
. A última cláusula não precisa de uma instrução break
e é freqüentemente usada para definir a ação padrão quando nenhuma outra correspondência é encontrada. Como visto no exemplo, a variável auxiliar não é necessária na estrutura switch
.
Warning
|
O |
Se mais de uma cláusula disparar a mesma ação, é possível combinar duas ou mais condições de case
:
switch ( language )
{
case "en":
case "en_US":
case "en_GB":
console.log("English");
break;
case "es":
console.log("Spanish");
break;
case "pt":
case "pt_BR":
console.log("Portuguese");
break;
default:
console.log(language, " not found");
}
Laços
Nos exemplos anteriores, as estruturas if
e switch
eram adequadas para tarefas que precisam ser executadas apenas uma vez após passar por um ou mais testes condicionais. No entanto, existem situações em que uma tarefa deve ser executada repetidamente — no que se chama um laço, ou loop — enquanto a expressão condicional for avaliada como verdadeira. Se você precisa saber se um número é primo, por exemplo, será preciso verificar se a divisão desse número por qualquer inteiro maior que 1 e menor do que ele mesmo tem um resto igual a 0. Se for o caso, o número tem um fator inteiro e não é primo (este não é um método rigoroso ou eficiente para encontrar números primos, mas funciona como um exemplo simples). As estruturas de controle de laço são mais adequadas para esses casos, em particular a instrução while
:
// A naive prime number tester
// The number we want to evaluate
let candidate = 231;
// Auxiliary variable
let is_prime = true;
// The first factor to try
let factor = 2;
// Execute the block statement if factor is
// less than candidate and keep doing it
// while factor is less than candidate
while ( factor < candidate )
{
if ( candidate % factor == 0 )
{
// The remainder is zero, so the candidate is not prime
is_prime = false;
break;
}
// The next factor to try. Simply
// increment the current factor by one
factor++;
}
// Display the result in the console window.
// If candidate has no integer factor, then
// the auxiliary variable is_prime still true
if ( is_prime )
{
console.log(candidate, "is prime");
}
else
{
console.log(candidate, "is not prime");
}
A declaração de bloco após a instrução while
será executada repetidamente enquanto a condição factor < candidate
for verdadeira. Ela será executada pelo menos uma vez, desde que inicializemos a variável factor
com um valor inferior a candidate
. A estrutura if
aninhada na estrutura while
avalia se o resto de candidate
dividido por factor
é zero. Se for o caso, o número candidato não é primo e o loop pode terminar. A declaração break
encerra o laço e a execução pula para a primeira instrução após o bloco while
.
Note que o resultado da condição usada pela declaração while
deve mudar a cada loop, caso contrário a declaração de bloco vai rodar “para sempre”. No exemplo, incrementamos a variável factor
— o próximo divisor que queremos testar — garantindo que o laço terminará em algum momento.
Esta implementação simples de um código para testar números primos funciona conforme o esperado. No entanto, sabemos que um número que não é divisível por dois não será divisível por nenhum outro número par. Portanto, poderíamos simplesmente pular os números pares adicionando outra instrução if
:
while ( factor < candidate )
{
// Skip even factors bigger than two
if ( factor > 2 && factor % 2 == 0 )
{
factor++;
continue;
}
if ( candidate % factor == 0 )
{
// The remainder is zero, so the candidate is not prime
is_prime = false;
break;
}
// The next number that will divide the candidate
factor++;
}
A declaração continue
é semelhante à declaração break
, mas ao invés de encerrar esta iteração do laço, ela ignora o resto do bloco do laço e inicia uma nova iteração. Observe que a variável factor
foi modificada antes da declaração continue
; caso contrário, o loop teria o mesmo resultado novamente na iteração seguinte. Este exemplo é demasiado simples e pular parte do loop não vai melhorar muito o desempenho, mas a possibilidade de pular instruções redundantes é importantíssima para se escrever aplicativos eficientes.
Os loops são tão comumente usados que eles existem em muitas variantes diferentes. O loop for
é especialmente adequado para iterar valores sequenciais, pois ele permite definir as regras do laço em uma única linha:
for ( let factor = 2; factor < candidate; factor++ )
{
// Skip even factors bigger than two
if ( factor > 2 && factor % 2 == 0 )
{
continue;
}
if ( candidate % factor == 0 )
{
// The remainder is zero, so the candidate is not prime
is_prime = false;
break;
}
}
Este exemplo produz exatamente o mesmo resultado que o exemplo anterior com while
, mas a expressão entre parênteses inclui três partes, separadas por ponto e vírgula: a inicialização (let factor = 2
), a condição de loop (factor < candidate
) e a expressão final a ser avaliada no final de cada iteração do loop (factor+`). As instruções `continue` e `break` também se aplicam aos loops `for`. A expressão final entre parênteses (`factor+
) será avaliada após a declaração continue
e, portanto, não deve estar dentro da declaração de bloco, ou será incrementada duas vezes antes da próxima iteração.
O JavaScript tem tipos especiais de loops for
para trabalhar com objetos do tipo array (matriz). Poderíamos, por exemplo, verificar uma matriz de variáveis candidatas em vez de apenas uma:
// A naive prime number tester
// The array of numbers we want to evaluate
let candidates = [111, 139, 293, 327];
// Evaluates every candidate in the array
for (candidate of candidates)
{
// Auxiliary variable
let is_prime = true;
for ( let factor = 2; factor < candidate; factor++ )
{
// Skip even factors bigger than two
if ( factor > 2 && factor % 2 == 0 )
{
continue;
}
if ( candidate % factor == 0 )
{
// The remainder is zero, so the candidate is not prime
is_prime = false;
break;
}
}
// Display the result in the console window
if ( is_prime )
{
console.log(candidate, "is prime");
}
else
{
console.log(candidate, "is not prime");
}
}
A declaração for (candidate of candidates)
atribui um elemento da matriz candidates
à variável candidate
e o usa na declaração de bloco, repetindo o processo para cada elemento da matriz. Não é necessário declarar candidate
separadamente, porque o loop for
o define. Finalmente, o mesmo código do exemplo anterior foi aninhado nesta nova declaração de bloco, mas desta vez testando cada candidato da matriz por sua vez.
Exercícios Guiados
-
Quais valores da variável
my_var
correspondem à condiçãomy_var > 0 && my_var < 9
? -
Quais valores da variável
my_var
correspondem à condiçãomy_var > 0 || my_var < 9
? -
How many times does the following
while
loop execute its block statement?let i = 0; while ( 1 ) { if ( i == 10 ) { continue; } i++; }
Exercícios Exploratórios
-
O que acontece se o operador de atribuição igual
=
for usado em vez do operador de comparação igual==
? -
Escreva um fragmento de código usando a estrutura de controle
if
, no qual uma comparação de igualdade comum retorne verdadeiro, mas uma comparação de igualdade estrita não. -
Reescreva a seguinte declaração
for
usando o operador lógico unário NOT na condição de loop. O resultado da condição deve ser o mesmo.for ( let factor = 2; factor < candidate; factor++ )
-
Com base nos exemplos desta lição, escreva uma estrutura de controle de loop que imprima todos os fatores inteiros de um determinado número.
Resumo
Esta lição aborda o uso de estruturas de controle no código JavaScript. Estruturas condicionais e de laço (loop) são elementos essenciais de qualquer paradigma de programação, e o desenvolvimento web em JavaScript não é exceção. A lição trata dos seguintes conceitos e procedimentos:
-
A declaração
if
e os operadores de comparação. -
Como usar a estrutura
switch
comcase
,default
ebreak
. -
A diferença entre comparações comuns e estritas.
-
Estruturas de controle de laço:
while
efor
.
Respostas aos Exercícios Guiados
-
Quais valores da variável
my_var
correspondem à condiçãomy_var > 0 && my_var < 9
?Somente números maiores que 0 e menores que 9. O operador lógico
&&
(AND) requer que ambas as comparações correspondam. -
Quais valores da variável
my_var
correspondem à condiçãomy_var > 0 || my_var < 9
?O operador lógico
||
(OR) fará com que qualquer número corresponda à condição, já que qualquer número será maior que 0 ou menor que 9. -
Quantas vezes o loop
while
a seguir executa sua declaração de bloco?let i = 0; while ( 1 ) { if ( i == 10 ) { continue; } i++; }
A declaração de bloco será repetida para sempre, pois não foi fornecida nenhuma condição de interrupção.
Respostas aos Exercícios Exploratórios
-
O que acontece se o operador de atribuição igual
=
for usado em vez do operador de comparação igual==
?O valor à direita do operador é atribuído à variável à esquerda e o resultado é passado para a comparação, o que pode não ser o comportamento desejado.
-
Escreva um fragmento de código usando a estrutura de controle
if
, no qual uma comparação de igualdade comum retorne verdadeiro, mas uma comparação de igualdade estrita não.let a = "1"; let b = 1; if ( a == b ) { console.log("An ordinary comparison will match."); } if ( a === b ) { console.log("A strict comparison will not match."); }
-
Reescreva a seguinte declaração
for
usando o operador lógico unário NOT na condição de loop. O resultado da condição deve ser o mesmo.for ( let factor = 2; factor < candidate; factor++ )
Answer:
for ( let factor = 2; ! (factor >= candidate); factor++ )
-
Com base nos exemplos desta lição, escreva uma estrutura de controle de loop que imprima todos os fatores inteiros de um determinado número.
for ( let factor = 2; factor <= my_number; factor++ ) { if ( my_number % factor == 0 ) { console.log(factor, " is an integer factor of ", my_number); } }