Em JavaScript, uma função não é uma “estrutura de linguagem mágica”, mas um tipo especial de valor.
A sintaxe que usamos antes chama-se Declaração de Função:
function sayHi() { alert( "Hello" );}
Há outra sintaxe para criar uma função que se chama Expressão de Função.
Fica assim:
let sayHi = function() { alert( "Hello" );};
Aqui, a função é criada e atribuída à variável explicitamente, como qualquer outro valor. Não importa como a função é definida, é apenas um valor armazenado na variável sayHi
.
O significado destes exemplos de código é o mesmo: “criar uma função e colocá-la na variável sayHi
“.
Podemos até imprimir esse valor usando alert
:
function sayHi() { alert( "Hello" );}alert( sayHi ); // shows the function code
Por favor note que a última linha não executa a função, porque não há parênteses depois de sayHi
. Existem linguagens de programação onde qualquer menção ao nome de uma função causa sua execução, mas o JavaScript não é assim.
Em JavaScript, uma função é um valor, então podemos lidar com ela como um valor. O código acima mostra sua representação em string, que é o código fonte.
Seguramente, uma função é um valor especial, no sentido de que podemos chamá-la como sayHi()
.
Mas ainda assim é um valor. Então podemos trabalhar com ela como com outros tipos de valores.
Podemos copiar uma função para outra variável:
function sayHi() { // (1) create alert( "Hello" );}let func = sayHi; // (2) copyfunc(); // Hello // (3) run the copy (it works)!sayHi(); // Hello // this still works too (why wouldn't it)
Aqui está o que acontece acima em detalhe:
- A Declaração de Função
(1)
cria a função e a coloca na variável chamadasayHi
. - Linha
(2)
copia-a para a variávelfunc
. Note novamente: não há parênteses apóssayHi
. Se houvesse, entãofunc = sayHi()
escreveria o resultado da chamadasayHi()
emfunc
, não a funçãosayHi
em si. - Agora a função pode ser chamada tanto como
sayHi()
efunc()
.
Nota que também poderíamos ter usado uma Expressão de Função para declarar sayHi
, na primeira linha:
let sayHi = function() { alert( "Hello" );};let func = sayHi;// ...
Tudo funcionaria da mesma maneira.
Pode perguntar-se, porque é que a Expressão de Funções tem um ponto-e-vírgula ;
no final, mas a Declaração de Funções não:
function sayHi() { // ...}let sayHi = function() { // ...};
A resposta é simples:
- Não há necessidade de
;
no final de blocos de código e estruturas de sintaxe que os utilizam comoif { ... }
,for { }
,function f { }
etc. - Uma expressão de função é usada dentro da instrução:
let sayHi = ...;
, como um valor. Não é um bloco de código, mas sim uma atribuição. O ponto-e-vírgula;
é recomendado no final das instruções, não importa qual seja o valor. Então o ponto-e-vírgula aqui não está relacionado com a expressão da função em si, ele apenas termina o comando.
Funções de retorno
Vejamos mais exemplos de passagem de funções como valores e uso de expressões de funções.
Escreveremos uma função ask(question, yes, no)
com três parâmetros:
question
Texto da perguntayes
Função a executar se a resposta for “Sim”no
Função a executar se a resposta for “Não”
A função deve perguntar a question
e, dependendo da resposta do usuário, chamar yes()
ou no()
:
function ask(question, yes, no) { if (confirm(question)) yes() else no();}function showOk() { alert( "You agreed." );}function showCancel() { alert( "You canceled the execution." );}// usage: functions showOk, showCancel are passed as arguments to askask("Do you agree?", showOk, showCancel);
Na prática, tais funções são bastante úteis. A maior diferença entre uma vida real ask
e o exemplo acima é que funções da vida real usam formas mais complexas de interagir com o usuário do que uma simples confirm
. No navegador, tal função normalmente desenha uma janela de perguntas agradável. Mas isso é outra história.
Os argumentos showOk
e showCancel
de ask
são chamados de funções de callback ou apenas callbacks.
A ideia é que passamos uma função e esperamos que ela seja “chamada de volta” mais tarde, se necessário. No nosso caso, showOk
torna-se a chamada de retorno para a resposta “sim”, e showCancel
para a resposta “não”.
Podemos usar Expressões de Funções para escrever a mesma função muito mais curta:
function ask(question, yes, no) { if (confirm(question)) yes() else no();}ask( "Do you agree?", function() { alert("You agreed."); }, function() { alert("You canceled the execution."); });
Aqui, as funções são declaradas mesmo dentro da chamada ask(...)
. Elas não têm nome, e por isso são chamadas anónimas. Tais funções não são acessíveis fora de ask
(porque não são atribuídas a variáveis), mas é isso mesmo que queremos aqui.
Esse código aparece em nossos scripts muito naturalmente, está no espírito do JavaScript.
Valores regulares como strings ou números representam os dados.
Uma função pode ser percebida como uma action.
Podemos passá-la entre variáveis e executá-la quando quisermos.
Expressão de função vs Declaração de função
Primeira, a sintaxe: como diferenciar entre elas no código.
-
Declaração de função: uma função, declarada como uma declaração separada, no fluxo do código principal.
// Function Declarationfunction sum(a, b) { return a + b;}
-
Expressão de função: uma função, criada dentro de uma expressão ou dentro de outra construção de sintaxe. Aqui, a função é criada no lado direito da “atribuição de expressão”
=
:// Function Expressionlet sum = function(a, b) { return a + b;};
A diferença mais subtil é quando uma função é criada pelo motor JavaScript.
Uma Expressão de Função é criada quando a execução a atinge e só é utilizável a partir desse momento.
A partir de agora o fluxo de execução passa para o lado direito da atribuição let sum = function…
– aqui vamos nós, a função é criada e pode ser usada (atribuída, chamada, etc. ).
As declarações de função são diferentes.
Uma Declaração de Função pode ser chamada mais cedo do que está definida.
Por exemplo, uma Declaração de Função global é visível em todo o script, não importa onde ele esteja.
Isso é devido aos algoritmos internos. Quando o JavaScript se prepara para executar o script, ele primeiro procura por Declarações de Função globais nele e cria as funções. Podemos pensar nele como um “estágio de inicialização”.
E depois de todas as Declarações de Função serem processadas, o código é executado. Então ele tem acesso a essas funções.
Por exemplo, isso funciona:
sayHi("John"); // Hello, Johnfunction sayHi(name) { alert( `Hello, ${name}` );}
A Declaração de Função sayHi
é criada quando o JavaScript está se preparando para iniciar o script e é visível em qualquer lugar nele.
…Se fosse uma Expressão de Função, então não funcionaria:
sayHi("John"); // error!let sayHi = function(name) { // (*) no magic any more alert( `Hello, ${name}` );};
Expressões de Função são criadas quando a execução as atinge. Isso aconteceria apenas na linha (*)
. Tarde demais.
Uma outra característica especial das Declarações de Funções é seu escopo de bloco.
Em modo restrito, quando uma Declaração de Funções está dentro de um bloco de código, ela é visível em todos os lugares dentro desse bloco. Mas não fora dele.
Por exemplo, vamos imaginar que precisamos declarar uma função welcome()
dependendo da variável age
que obtemos durante o tempo de execução. E então planejamos usá-la algum tempo depois.
Se usarmos a Declaração de Função, ela não funcionará como pretendido:
let age = prompt("What is your age?", 18);// conditionally declare a functionif (age < 18) { function welcome() { alert("Hello!"); }} else { function welcome() { alert("Greetings!"); }}// ...use it laterwelcome(); // Error: welcome is not defined
Isso porque uma Declaração de Função só é visível dentro do bloco de código em que ela reside.
Aqui está outro exemplo:
let age = 16; // take 16 as an exampleif (age < 18) { welcome(); // \ (runs) // | function welcome() { // | alert("Hello!"); // | Function Declaration is available } // | everywhere in the block where it's declared // | welcome(); // / (runs)} else { function welcome() { alert("Greetings!"); }}// Here we're out of curly braces,// so we can not see Function Declarations made inside of them.welcome(); // Error: welcome is not defined
O que podemos fazer para tornar welcome
visível fora de if
?
A abordagem correta seria usar uma Expressão de Função e atribuir welcome
à variável que é declarada fora de if
e tem a visibilidade apropriada.
Este código funciona como pretendido:
let age = prompt("What is your age?", 18);let welcome;if (age < 18) { welcome = function() { alert("Hello!"); };} else { welcome = function() { alert("Greetings!"); };}welcome(); // ok now
Or poderíamos simplificar ainda mais usando um operador de ponto de interrogação ?
:
let age = prompt("What is your age?", 18);let welcome = (age < 18) ? function() { alert("Hello!"); } : function() { alert("Greetings!"); };welcome(); // ok now
Como regra geral, quando precisamos declarar uma função, a primeira a considerar é a sintaxe da Declaração de Função. Ela dá mais liberdade em como organizar nosso código, porque podemos chamar tais funções antes de serem declaradas.
Isso também é melhor para a legibilidade, pois é mais fácil procurar function f(…) {…}
no código do que let f = function(…) {…};
. Function Declarations are more “eye-catching”.
…But if a Function Declaration does not suit us for some reason, or we need a conditional declaration (we’ve just seen an example), then Function Expression should be used.
Summary
- Functions are values. Eles podem ser atribuídos, copiados ou declarados em qualquer lugar do código.
- Se a função é declarada como uma declaração separada no fluxo do código principal, isso é chamado de “Function Declaration”.
- Se a função é criada como parte de uma expressão, ela é chamada de “Function Expression”.
- Function Declarations are processadas antes que o bloco de código seja executado. Elas são visíveis em qualquer lugar do bloco.
- Expressões de função são criadas quando o fluxo de execução chega até elas.
Na maioria dos casos quando precisamos declarar uma função, uma Declaração de Função é preferível, pois ela é visível antes da própria declaração. Isso nos dá mais flexibilidade na organização do código, e normalmente é mais legível.
Então devemos usar uma Expressão de Função somente quando uma Declaração de Função não é adequada para a tarefa. Vimos alguns exemplos disso neste capítulo, e veremos mais no futuro.