- Aprender
- Computação e TI
- Programação
- Programação em JavaScript
- Funções em JavaScript
Funções em JavaScript: escopo e fechamentos
return. Preveja onde uma variável pode ser usada com escopos de bloco, de função e global, use fechamentos para dar memória própria às funções e passe funções para outras funções como funções de retorno para escrever código flexível de ordem superior.BásicoAula 3 de 9 do curso Curso de Programação em JavaScript
O que você vai aprender
- Organize um trecho de código reutilizável como função, chame-o com argumentos e escolha entre uma declaração de função, uma expressão de função e uma função de seta.
- Declarar uma função e chamá-laDefina uma função nomeada com a palavra-chave
function, chame-a e explique por que funções tornam o código mais curto, claro e fácil de alterar. - Armazenar uma função em uma variável com uma expressão de funçãoTrate funções como valores: crie uma expressão de função, atribua-a a uma variável e explique como ela difere de uma declaração em elevação e nomeação.
- Escrever funções de setaUse a sintaxe curta de seta, saiba quando uma função de seta retorna um valor automaticamente e explique como ela difere de funções escritas com
function.
- Declarar uma função e chamá-la
- Planeje o que entra em uma função e o que sai dela: relacione argumentos a parâmetros, defina valores padrão, aceite qualquer quantidade de argumentos, retorne resultados e acompanhe como as chamadas são executadas umas dentro das outras.
- Relacionar argumentos a parâmetrosExplique como os valores de uma chamada chegam aos parâmetros da função, o que acontece quando há argumentos de menos ou de mais e quais dados de quem chama a função ela pode alterar.
- Definir valores padrão e aceitar qualquer quantidade de argumentosDefina valores padrão para parâmetros, reúna qualquer quantidade de argumentos com um parâmetro rest e espalhe um vetor em argumentos separados.
- Retornar resultados de uma funçãoRetorne um valor com
return, use o resultado no código seguinte e diferencie retornar um valor de imprimi-lo ou alterar algo fora da função. - Acompanhar chamadas de função pela pilha de chamadasRastreie a ordem em que chamadas aninhadas são executadas e retornam, e escreva uma função recursiva simples com um caso base.
- Relacionar argumentos a parâmetros
- Preveja onde uma variável fica visível aplicando as regras de escopo de bloco, de função e global e de funções escritas dentro de outras funções.
- Distinguir escopo de bloco, escopo de função e escopo globalExplique o escopo de uma variável declarada com
let,constouvare use variáveis locais para manter funções independentes umas das outras. - Prever a qual variável um nome se refereSiga a cadeia de escopos para encontrar a variável associada a um nome, reconheça o sombreamento e explique a elevação e a zona morta temporal.
- Distinguir escopo de bloco, escopo de função e escopo global
- Explique como uma função mantém acesso às variáveis ao seu redor depois que a função externa termina e use isso para construir fábricas de funções e estado privado.
- Explicar o que um fechamento mantém na memóriaDescreva como uma função mantém acesso às variáveis do escopo em que foi criada, mesmo depois que a função externa retorna.
- Usar fechamentos para criar fábricas de funções e estado privadoConstrua fábricas de funções, mantenha dados privados acessíveis por funções e evite o erro clássico de fechamento em um laço.
- Explicar o que um fechamento mantém na memória
- Use funções como valores: passe-as a outras funções como funções de retorno, use e escreva funções de ordem superior e construa novas funções a partir das existentes.
- Passar uma função de retorno para uma funçãoPasse uma função para outra como argumento, escreva funções que chamam uma função de retorno e passe a própria função em vez de seu resultado.
- Usar e escrever funções de ordem superiorReconheça funções de ordem superior, use os métodos de vetor que recebem funções de retorno e escreva sua própria função que recebe uma função como entrada.
- Construir novas funções a partir de funções existentesEscreva funções de ordem superior que envolvem, combinam ou preenchem antecipadamente argumentos de outras funções, usando fechamentos para manter na memória a função recebida.
- Passar uma função de retorno para uma função
Guia de estudo de Funções em JavaScript
1Definir e chamar funções de três formas
Funções transformam instruções repetidas em tarefas nomeadas. Você vai ler e escrever três formas comuns e decidir se uma linha cria uma função, armazena uma ou chama uma. Pequenas diferenças na sintaxe importam: uma declaração fica disponível antes de sua posição no código, enquanto uma expressão armazenada precisa ser inicializada primeiro, e o corpo de uma função de seta pode mudar se um resultado é retornado. Essas distinções preparam você para passar funções como valores comuns.
1.1 Declarar uma função e chamá-la
Uma função dá um nome a uma tarefa e armazena as instruções para executá-la. Defini-la não executa essas instruções. Uma chamada as executa, e outra chamada as executa novamente com novas entradas.
Escreva a definição
Uma declaração tem a palavra-chave function, um nome, parênteses para os parâmetros e chaves para o corpo:
function greet(name) {
console.log("Hello, " + name + "!");
}
Aqui, name é um parâmetro: um nome local para a entrada fornecida por cada chamada. O corpo junta essa entrada ao texto ao redor e imprime o resultado. A chave de fechamento marca o fim da definição; a execução continua com a próxima instrução fora dela.
Uma função sem entradas ainda precisa de parênteses. Por exemplo, function sayHi() { console.log("Hi"); } define uma tarefa com uma lista de parâmetros vazia. As chaves pertencem à definição; os parênteses depois de um nome aparecem tanto em definições quanto em chamadas, com funções diferentes.
Execute a tarefa
greet("Amira");
greet("Li");
A primeira chamada atribui a name o valor "Amira" e imprime “Hello, Amira!”. A segunda inicia uma nova chamada com name igual a "Li" e imprime “Hello, Li!”. A definição é compartilhada; a entrada pertence à chamada individual.
greet sozinho se refere ao valor da função. Ele não executa o corpo. Essa distinção importa quando uma linha parece não fazer nada: sayHi; lê um valor de função e o descarta, enquanto sayHi(); imprime “Hi”.
Substitua instruções copiadas
Suponha que vários relatórios precisem de um cabeçalho cercado por linhas:
function printHeading(title) {
console.log("-----");
console.log(title);
console.log("-----");
}
printHeading("Report");
printHeading("Totals");
A primeira chamada imprime uma linha, “Report” e outra linha. A segunda imprime a mesma moldura ao redor de “Totals”. Para alterar o estilo das linhas, edite as duas instruções que as imprimem nessa única definição. Sem a função, cada cabeçalho copiado precisaria de sua própria correção.
Escolha um nome que diga o que uma chamada faz. printHeading descreve uma ação; isValidEmail nomeia uma verificação de sim ou não. Use camelCase, com a primeira palavra em minúsculas e as palavras seguintes começando com maiúsculas. Se um nome proposto unir duas tarefas separadas com “e”, considere dar a cada tarefa sua própria função.
Leia código que chama uma declaração antes de sua posição
As declarações são preparadas antes da execução do código ao redor. Esse comportamento é chamado de elevação:
sayHi();
function sayHi() {
console.log("Hi");
}
A chamada funciona dentro do escopo da declaração, embora a definição escrita venha depois. A elevação não faz o corpo ser executado antes; ela disponibiliza a função para uma chamada.
Verifique a escrita e o uso de maiúsculas e minúsculas quando uma chamada falhar. Greet() e greet() são nomes diferentes. Chamar uma greeet() não declarada gera um ReferenceError; omitir os parênteses da chamada pode falhar silenciosamente, pois apenas ler uma função existente é código válido.
Principais conclusões
- Definir uma função armazena instruções; chamá-la as executa.
- Chame com parênteses, mesmo quando não houver argumentos.
- Uma declaração fica disponível antes de sua posição no código, dentro do mesmo escopo.
- Dê nome a uma tarefa clara para que o código repetido tenha um único lugar de alteração.
1.2 Armazenar uma função em uma variável com uma expressão de função
Funções são valores. Você pode manter uma em uma variável, copiar sua referência, passá-la para outra função ou retorná-la. Uma expressão de função cria esse valor onde se espera uma expressão.
Armazene uma função
const square = function (n) {
return n * n;
};
const answer = square(4);
A expressão depois de = cria uma função. A atribuição a armazena em square; a chamada posterior retorna 16. Essa instrução termina com ponto e vírgula, seguindo a convenção de formatação usada aqui.
A posição de function distingue as formas. Uma definição escrita como uma instrução independente, function square(n) { ... }, é uma declaração. Uma função escrita depois de = ou diretamente como argumento de uma chamada é uma expressão. Ambas as formas de função comum podem aceitar parâmetros e retornar valores.
Use const para uma variável que contém uma função quando seu valor deve permanecer o mesmo. Chamar square(5) é permitido; atribuir square = 5; gera um TypeError. A constante protege a variável contra substituição. Ela não impede chamadas repetidas.
Guarde uma função ou seu resultado
const sq = square;
const result = square(3);
sq se refere à mesma função que square. Chamar sq(3) retorna 9. result contém o número 9 retornado por uma chamada. Ele não pode ser chamado como função. typeof square e typeof sq são "function"; typeof result é "number".
Ler uma atribuição da direita para a esquerda ajuda: primeiro decida se o lado direito cria uma função, refere-se a uma função existente ou chama uma função. A variável à esquerda recebe esse valor. Copiar a referência não cria uma segunda implementação independente.
Aguarde a execução da atribuição
Uma declaração e uma expressão armazenada ficam disponíveis para chamada em momentos diferentes:
| Forma | Antes da definição ou atribuição escrita |
|---|---|
function square(n) { return n * n; } | Pode ser chamada dentro de seu escopo |
const square = function (n) { return n * n; }; | Ler square gera um ReferenceError |
var square = function (n) { return n * n; }; | square é undefined; chamá-la gera um TypeError |
Com const ou let, o nome não pode ser usado antes da inicialização. Com var, o nome existe desde o início, mas o valor da função só é atribuído quando a execução chega à expressão. A elevação da variável não eleva a atribuição.
Dê um nome interno a uma expressão
const factorial = function fact(n) {
return n <= 1 ? 1 : n * fact(n - 1);
};
O nome externo é factorial. O nome fact pertence ao interior da função e permite que ela chame a si mesma; o código externo não pode usar esse nome interno diretamente. O nome da expressão também pode facilitar a leitura de rastreamentos de pilha. Por exemplo, factorial(3) calcula 3 vezes 2 vezes 1 e retorna 6.
Declarações funcionam bem para as principais tarefas nomeadas em um script. Expressões se encaixam onde o código precisa de um valor de função, como em uma atribuição a variável ou em um argumento. Escolher uma expressão muda como o valor é criado e armazenado, não como os parênteses o chamam.
Principais conclusões
- Uma expressão de função cria um valor de função dentro de outra expressão.
- Atribuir
squarecopia uma referência à função; atribuirsquare(3)armazena seu resultado. - Uma expressão de função armazenada em
constsó pode ser chamada após a inicialização. - Uma variável de função declarada com
varcontémundefinedantes da execução de sua atribuição. - Uma expressão nomeada pode usar seu nome interno para chamar a si mesma.
1.3 Escrever funções de seta
Funções de seta são expressões de função com uma escrita mais curta. Seu comportamento de retorno depende do que vem depois da seta, então examine o corpo antes de decidir o que uma chamada produz.
Escolha a forma dos parâmetros
const sayHi = () => "hi";
const double = n => n * 2;
const add = (a, b) => a + b;
Sem parâmetros, são necessários parênteses vazios. Um parâmetro simples pode dispensar os parênteses. Dois ou mais exigem parênteses. Um parâmetro padrão ou rest também precisa de parênteses: (n = 1) => n * 2 e (...nums) => nums.length são válidos.
A seta separa as entradas do corpo. const double = n => n * 2; cria a função e a armazena; double(6) depois a chama e retorna 12. Como qualquer const que contém uma expressão de função, double não pode ser lida antes da execução de sua linha de inicialização.
Retorne uma expressão ou escreva um bloco
| Corpo | Exemplo | Resultado da chamada com 2 e 3 |
|---|---|---|
| Expressão concisa | (a, b) => a + b | 5 |
Bloco com return | (a, b) => { return a + b; } | 5 |
Bloco sem return | (a, b) => { a + b; } | undefined |
Um corpo conciso retorna sua expressão automaticamente. As chaves iniciam um bloco de instruções. Em um bloco, avaliar uma expressão não a envia de volta a quem chamou a função; escreva return explicitamente.
Use um bloco quando a tarefa precisar de etapas intermediárias:
const describe = (n) => {
const kind = n % 2 === 0 ? "even" : "odd";
return n + " is " + kind;
};
Para describe(7), a condição é falsa, então kind recebe "odd". O retorno junta o número e as palavras em “7 is odd”. Remover esse retorno manteria o cálculo local, mas entregaria undefined a quem chamou a função.
Retorne um literal de objeto
Um literal de objeto também usa chaves. Envolva-o em parênteses para que o analisador sintático o interprete como uma expressão:
const makePoint = (x, y) => ({ x: x, y: y });
makePoint(2, 5) retorna um objeto com x igual a 2 e y igual a 5. Sem os parênteses, a primeira chave inicia um bloco. A forma com uma propriedade, () => { value: 1 }, retorna undefined; a forma semelhante com duas propriedades separadas por vírgula gera um erro de sintaxe. Os parênteses mudam como o corpo é interpretado.
Conheça os limites da forma mais curta
Funções de seta não têm seus próprios this ou arguments e não podem ser chamadas com new para construir objetos. Para um cálculo simples que não usa this nem arguments, uma função de seta e uma expressão de função comum se comportam da mesma forma. Substituir todas as funções comuns por funções de seta pode alterar o código que depende desses recursos.
Funções de seta curtas são especialmente fáceis de ler como funções de retorno. [1, 2, 3].map(n => n * 10) passa uma função que retorna um elemento transformado, produzindo [10, 20, 30]. Alterá-la para n => { n * 10; } faz cada valor de retorno se perder silenciosamente.
Principais conclusões
- Um corpo conciso de função de seta retorna sua expressão automaticamente.
- Um corpo em bloco de função de seta precisa de
returnexplícito para retornar um valor calculado. - Use parênteses em torno de um literal de objeto retornado por uma função de seta com corpo conciso.
- Listas de parâmetros vazias, com vários parâmetros, com valor padrão ou com rest precisam de parênteses.
- Funções de seta não têm seus próprios
thisouargumentse não podem ser construtoras.
2Passar valores para funções e obter resultados
Uma chamada de função tem uma parte de entrada e uma de saída. Relacione argumentos a parâmetros, trate entradas opcionais ou flexíveis e acompanhe o valor retornado a quem chamou a função. Você também vai distinguir alterações locais de alterações em um vetor compartilhado e acompanhar chamadas aninhadas enquanto seu trabalho pausa e recomeça. O mesmo modelo de chamada e retorno explica a recursão simples, em que várias chamadas de uma função aguardam o fim de chamadas menores.
2.1 Relacionar argumentos a parâmetros
Os parâmetros de uma função dão nomes aos valores fornecidos por uma chamada. Para rastrear uma chamada, primeiro associe as entradas pela posição e depois acompanhe as operações no corpo.
Relacione posições e tipos
function area(width, height) {
return width * height;
}
area(4, 2.5);
width e height são parâmetros; 4 e 2,5 são argumentos. Durante essa chamada, width contém 4 e height contém 2,5, então o resultado é 10. Os nomes dos parâmetros não são associados aos nomes das variáveis de quem chama a função. Com divide(a, b), trocar divide(10, 2) por divide(2, 10) muda o resultado de 5 para 0,2.
JavaScript não exige uma quantidade específica de argumentos. area(4) deixa height como undefined; multiplicar por esse valor produz NaN. area(4, 2, 99) retorna 8 porque esse corpo não usa o terceiro argumento. Uma função pode coletar argumentos extras, mas argumentos extras não usados não causam um erro automático.
Os tipos também importam, mesmo quando a chamada é aceita. area("4", 2) retorna 8 porque a multiplicação converte o texto. Uma função de adição se comporta de forma diferente: add("4", 2) retorna “42” quando seu corpo é return a + b;. Se o texto representa um número, add(Number("4"), 2) retorna 6.
Reatribua um parâmetro local
Cada parâmetro é local à sua chamada. Atribuir um novo número a ele não reatribui a variável de quem chamou a função:
let score = 5;
function reset(s) {
s = 0;
}
reset(score);
A chamada copia o valor 5 para s. A atribuição muda s para 0, e score permanece 5. Números, cadeias de caracteres e booleanos seguem essa regra de cópia de valor. O código externo não pode ler s diretamente; uma função aninhada pode manter acesso a ele por meio de um fechamento.
Altere um vetor por meio de uma referência copiada
Um valor de vetor é uma referência a um vetor. Passar esse valor copia a referência, então quem chamou a função e o parâmetro podem acessar o mesmo vetor:
const list = [1, 2];
function addItem(arr) {
arr.push(3);
}
addItem(list);
list agora é [1, 2, 3]. A função não reatribuiu list; ela alterou o vetor que ambos os nomes acessam. Uma declaração const impede a substituição da referência armazenada, não a alteração do conteúdo do vetor.
Compare com uma reatribuição local:
function clearLocal(arr) {
arr = [];
}
clearLocal(list);
Isso faz apenas arr apontar para um novo vetor vazio. list ainda acessa [1, 2, 3]. Pergunte-se se uma operação altera um vetor existente, como faz push, ou atribui um valor diferente a um nome, como faz =.
Retorne um novo vetor quando a entrada deve permanecer intacta
function withItem(arr, item) {
return arr.concat(item);
}
withItem([1, 2], 3) produz [1, 2, 3] como um novo vetor. O vetor de entrada permanece [1, 2]. Isso torna a função útil quando quem a chama precisa tanto da lista antiga quanto da lista ampliada. Deixe claras pelo nome e pelo comportamento da função as alterações que ela pretende fazer na entrada, para que outras pessoas possam usá-la de forma previsível.
Principais conclusões
- Argumentos são associados a parâmetros pela posição.
- Um argumento ausente fornece
undefined; argumentos extras não usados são permitidos. - Reatribuir um parâmetro não reatribui a variável de quem chamou a função.
- Modificar um vetor por meio de um parâmetro altera o vetor compartilhado.
- Retornar um novo vetor pode preservar a entrada.
2.2 Definir valores padrão e aceitar qualquer quantidade de argumentos
Valores padrão tornam uma entrada opcional; parâmetros rest permitem que uma chamada forneça muitas entradas. A sintaxe de espalhamento conecta um vetor existente a uma função que espera argumentos separados.
Aplique um valor padrão apenas para undefined
function greet(name = "friend") {
return "Hello, " + name;
}
Tanto greet() quanto greet(undefined) retornam “Hello, friend”. greet(null) retorna “Hello, null”, e greet("") retorna “Hello, ”. Um valor padrão é aplicado para undefined, não para todo valor que parece vazio.
Valores padrão são avaliados quando uma chamada precisa deles. Eles podem usar parâmetros anteriores: function makeBox(width, height = width) { return width * height; } retorna 9 para makeBox(3) e 12 para makeBox(3, 4). O segundo argumento explícito substitui o valor padrão nessa chamada.
Coloque as entradas obrigatórias primeiro. Com function total(amount, rate = 0.25) { return amount + amount * rate; }, total(80) retorna 100 e total(80, 0.5) retorna 120. Inverter os parâmetros faria a chamada comum com a taxa padrão exigir total(undefined, 80).
Código antigo pode usar level = level || 50;. Isso também substitui 0, false e uma cadeia de caracteres vazia porque são valores avaliados como falsos. Um parâmetro padrão preserva esses valores: function setVolume(level = 50) { return level; } retorna 0 para setVolume(0) e 50 para setVolume(undefined).
Reúna argumentos com rest
function sum(...numbers) {
let total = 0;
for (const n of numbers) {
total += n;
}
return total;
}
sum(1, 2, 3) coloca [1, 2, 3] em numbers e retorna 6. sum() fornece a ele um vetor vazio, então o laço não adiciona nada e retorna 0. Um parâmetro rest é um vetor verdadeiro, pronto para métodos de vetor ou iteração.
Uma entrada fixa pode vir antes de rest: em function log(label, ...values) { console.log(label, values); }, log("scores", 7, 9) atribui a label a cadeia “scores” e a values o vetor [7, 9]. Apenas um parâmetro rest é permitido, e ele deve vir por último; caso contrário, JavaScript não consegue atribuir os argumentos restantes sem ambiguidade usando essa sintaxe.
Expanda argumentos com espalhamento
Posição de ... | Função | Exemplo |
|---|---|---|
| Lista de parâmetros da definição | Reunir argumentos restantes | function sum(...numbers) { ... } |
| Lista de argumentos da chamada | Expandir elementos de um vetor | sum(...values) |
Math.max(...[3, 7, 2]) recebe três números e retorna 7. Math.max([3, 7, 2]) recebe um vetor e retorna NaN. Os três pontos são idênticos na página, mas a sintaxe ao redor indica qual função eles desempenham.
Funções comuns também têm um objeto arguments que contém os argumentos passados a elas. Ele é semelhante a um vetor, com entradas indexadas e um tamanho, mas não é um vetor verdadeiro e não tem seus próprios métodos map ou filter. Funções de seta não têm seu próprio arguments. Prefira um parâmetro rest em código novo que precise de uma lista flexível de entradas.
Principais conclusões
- Valores padrão se aplicam a argumentos ausentes e a
undefinedexplícito, preservando 0 enull. - Uma expressão de valor padrão é avaliada para cada chamada que precisa dela.
- Um parâmetro rest reúne os argumentos restantes em um vetor e vem por último.
- O espalhamento expande um vetor em argumentos separados de uma chamada.
- O antigo objeto
argumentsé semelhante a um vetor; rest produz um vetor verdadeiro.
2.3 Retornar resultados de uma função
Um valor retornado é a saída que outro código recebe de uma chamada. A impressão pode mostrar o mesmo valor na tela, mas fornece um resultado diferente a quem chamou a função, a menos que ela também o retorne.
Use a chamada como valor
function double(n) {
return n * 2;
}
const x = double(5) + 1;
double(5) retorna 10, então a atribuição é avaliada como 10 mais 1 e armazena 11. Uma chamada pode aparecer em qualquer lugar onde seja necessário um valor apropriado. double(double(3)) primeiro obtém 6 da chamada interna e depois retorna 12 da externa. Math.round(double(2.3)) dobra o valor para 4,6 e arredonda para 5.
return também encerra imediatamente a chamada atual. Uma cláusula de guarda trata um caso especial antes do trabalho principal:
function safeDivide(a, b) {
if (b === 0) {
return null;
}
return a / b;
}
safeDivide(6, 0) retorna null e nunca chega à divisão. safeDivide(6, 2) pula a cláusula de guarda e retorna 3. Coloque a condição antes da operação que ela protege.
Separe o retorno da exibição
function addLog(a, b) {
console.log(a + b);
}
addLog(2, 3) imprime 5, mas seu valor de retorno é undefined. Consequentemente, addLog(2, 3) * 2 imprime 5 e é avaliado como NaN. Uma função que chega ao fim sem um retorno, ou executa apenas return;, retorna undefined. A exibição no console é um efeito colateral, não a saída numérica utilizável da chamada.
Se quem chama a função precisa de uma soma, escreva return a + b;. Depois, quem chama pode escolher entre imprimi-la, armazená-la ou usá-la em outro cálculo. Isso mantém as decisões de exibição fora de um cálculo reutilizável.
Mantenha a expressão retornada na mesma linha
function brokenAdd(a, b) {
return
a + b;
}
JavaScript encerra a instrução return na quebra de linha. A função retorna undefined; a expressão posterior nunca é alcançada. Para uma expressão longa, comece-a ou abra um parêntese na linha de return e continue abaixo.
Reúna vários resultados em um valor
Uma chamada retorna um valor, mas esse valor pode ser um vetor:
function minMax(nums) {
return [Math.min(...nums), Math.max(...nums)];
}
const [low, high] = minMax([4, 9, 1]);
O vetor retornado é [1, 9]. A desestruturação atribui sua primeira entrada a low e a segunda a high. Ainda há um único valor retornado, que contém dois resultados úteis.
Uma função pura calcula sua saída a partir de seus argumentos e não altera nada fora de si. double é pura. Imprimir, atualizar uma variável externa e modificar um vetor de entrada são efeitos colaterais. Cálculos puros são fáceis de verificar com exemplos de entrada e saída; efeitos colaterais devem ficar em funções com nomes claros, onde o programa precisa de alterações visíveis.
Principais conclusões
returnfornece o valor da chamada e encerra a chamada atual.- Apenas imprimir deixa o valor de retorno da função como
undefined. - Comece a expressão retornada na linha de
return. - Um único vetor retornado pode transportar vários resultados.
- Funções puras dependem de seus argumentos e não fazem alterações externas.
2.4 Acompanhar chamadas de função pela pilha de chamadas
Quando uma função chama outra, a função que fez a chamada pausa até que essa chamada retorne. A pilha de chamadas registra o trabalho não concluído para que a execução possa continuar no lugar certo.
Adicione e remova quadros de chamada
Cada chamada ativa tem um quadro que contém seus parâmetros, variáveis locais e o ponto de retorno. Uma nova chamada vai para o topo da pilha; um retorno remove esse quadro do topo.
function square(n) {
return n * n;
}
function sumOfSquares(a, b) {
return square(a) + square(b);
}
sumOfSquares(3, 4);
Primeiro, sumOfSquares começa com a igual a 3 e b igual a 4. Ela chama square(3), que retorna 9 e termina. Depois, square(4) é executada e retorna 16. A adição pausada pode agora terminar como 9 mais 16, e sumOfSquares retorna 25. As duas chamadas de square não se sobrepõem nesse rastreamento.
Cada chamada recebe novos parâmetros e variáveis locais. Chamar a mesma função duas vezes não reutiliza o estado numérico local de uma chamada anterior. Mesmo quando uma função chama a si mesma, a nova chamada tem seu próprio quadro.
Reduza um problema com recursão
Uma função recursiva chama a si mesma. Um caso base termina sem outra chamada a si mesma; um caso recursivo dá um passo em direção a esse caso base.
function factorial(n) {
if (n <= 1) {
return 1;
}
return n * factorial(n - 1);
}
Para entradas inteiras não negativas, isso multiplica os inteiros até chegar a 1. Rastreie factorial(4) em duas direções:
factorial(4)espera porfactorial(3).- Essa chamada espera por
factorial(2). - Essa chamada espera por
factorial(1). - O caso base retorna 1.
- As chamadas em espera retornam 2, depois 6 e depois 24.
A multiplicação na chamada com n igual a 4 acontece depois que a chamada menor retorna. Seu próprio n permaneceu 4 enquanto ela esperava.
Um caso base precisa ser alcançável. Usar apenas n === 1 falha para uma entrada inicial de 0 se cada chamada recursiva subtrair 1: os valores se tornam negativos e nunca chegam a 1. n <= 1 cobre tanto 0 quanto 1. Sem um caminho de parada, chamadas repetidas esgotam a pilha. Chrome e Node.js informam “RangeError: Maximum call stack size exceeded”; o Firefox usa “InternalError: too much recursion”.
Reconheça uma cópia menor da mesma tarefa
function countItems(list) {
let count = 0;
for (const item of list) {
if (Array.isArray(item)) {
count += countItems(item);
} else {
count += 1;
}
}
return count;
}
countItems([1, [2, 3], [4, [5]]]) retorna 5. Cada vetor aninhado faz a mesma pergunta de contagem para uma lista menor. Um laço também é adequado para um fatorial simples; a recursão é especialmente útil quando os próprios dados são aninhados.
Se uma chamada falhar, leia o rastreamento de pilha a partir da chamada mais recente no topo. As entradas seguintes mostram quais chamadas ainda não concluídas levaram até ela. Entradas repetidas para uma função podem revelar uma recursão que não parou.
Principais conclusões
- A pilha de chamadas acompanha chamadas não concluídas; retornos removem o quadro mais recente.
- Cada invocação tem seus próprios parâmetros e variáveis locais.
- A recursão precisa de um caso de parada e de progresso em direção a ele.
- Resultados recursivos retornam pelas chamadas em espera na ordem inversa.
- Um rastreamento de pilha mostra a chamada que falhou e as chamadas que levaram a ela.
- Dados aninhados podem conter instâncias menores da mesma tarefa.
3Explicar onde uma variável pode ser usada
Nomes de variáveis têm significado dentro de escopos. Os limites dos blocos, os limites das funções e o lugar onde uma função é definida determinam quais declarações ela pode acessar. Você vai acompanhar esses limites para prever um valor ou erro e depois distinguir o sombreamento da atualização de uma variável externa. A elevação e a zona morta temporal explicam por que um nome pode ser registrado antes de a execução chegar à sua linha e ainda assim estar indisponível para uso.
3.1 Distinguir escopo de bloco, escopo de função e escopo global
O escopo de uma variável é a região onde seu nome pode ser usado. O escopo permite que funções mantenham seus dados de trabalho separados e ainda usem entradas e valores do código ao redor.
Delimite as declarações
let e const têm escopo de bloco. Um bloco é uma região dentro de chaves, como um corpo de if, um corpo de laço ou um corpo de função:
if (true) {
const msg = "hi";
console.log(msg);
}
A impressão funciona dentro do bloco. Adicionar console.log(msg); depois dele gera um ReferenceError porque esse código externo não consegue acessar a declaração. Outra declaração externa com o mesmo nome seria uma variável diferente.
Parâmetros e variáveis dentro de uma função são locais a ela. Em function f() { let temp = 1; }, o código externo não consegue ler temp. Duas funções podem ter, cada uma, um total local sem compartilhá-lo. Cada chamada começa com novas variáveis locais; uma função aninhada retornada pode preservar o acesso posteriormente por meio de um fechamento.
Leia declarações antigas com var
| Declaração | Limite |
|---|---|
let ou const dentro de um bloco | Esse bloco |
var dentro de uma função | A função que o contém, mesmo quando escrito em um bloco interno |
| Declaração de nível superior em um script comum | O escopo global do script |
| Declaração de nível superior em um módulo | Esse módulo |
function test() {
if (true) {
var x = 1;
}
return x;
}
test() retorna 1 porque var ignora o limite do if. Substituí-lo por let x = 1; faz o retorno falhar: o x do bloco não está disponível ali. Da mesma forma, for (let i = 0; i < 3; i++) {} mantém i dentro do laço, enquanto um var i permanece disponível na função ou no script que o contém após o laço e contém 3.
Procure para fora a partir da posição em que a função foi escrita
Escopos são aninhados. Uma função interna pode usar nomes nos escopos que a envolvem, mas o código externo não pode procurar nas variáveis locais de uma função interna. Isso é escopo léxico: o lugar onde o código é escrito determina os nomes disponíveis.
const level = "global";
function show() {
return level;
}
function run() {
const level = "local";
return show();
}
run() retorna “global”. show foi definida fora de run, então o escopo que a envolve contém o level externo. Chamá-la de dentro de run não lhe dá acesso ao level local de quem a chamou.
Mantenha o estado compartilhado sob controle
Uma variável de nível superior em um script comum é global e pode ser acessada por funções desse script. Variáveis globais compartilhadas podem fazer tarefas independentes afetarem umas às outras; scripts na mesma página também podem entrar em conflito por causa de nomes. Passar entradas e retornar saídas mantém as dependências visíveis.
Declare toda variável. No modo estrito, count = 5; gera um ReferenceError se não houver uma declaração acessível. Sem o modo estrito, uma atribuição assim pode criar uma variável global acidental. Um script pode ativar o modo estrito com "use strict"; em seu início; módulos o ativam automaticamente. Prefira const para valores que não são reatribuídos e let quando a reatribuição for necessária.
Principais conclusões
leteconstobedecem aos limites do bloco;varobedece ao limite da função que o contém.- Parâmetros de função e variáveis locais não estão disponíveis para o código externo.
- O escopo léxico segue o lugar onde uma função é definida, não onde ela é chamada.
- Variáveis de nível superior de um módulo pertencem ao módulo.
- Declare nomes explicitamente para evitar atribuições globais acidentais.
3.2 Prever a qual variável um nome se refere
O mesmo nome pode aparecer em vários escopos. JavaScript encontra a declaração relevante procurando para fora a partir do código atual e parando na primeira correspondência.
Siga a cadeia de escopos
let x = 1;
function f() {
let x = 2;
return x;
}
f() retorna 2. A declaração local sombreia o x externo, então a busca para dentro de f. O valor externo permanece 1. Um parâmetro pode sombrear um nome da mesma maneira. Se nenhum escopo da cadeia declarar um nome, sua leitura gera um ReferenceError.
Uma atribuição não declara uma nova variável. Com let count = 0; function add() { count = count + 1; }, add encontra a declaração externa e a altera. Inserir let muda o significado: cria um novo nome local, que pode ocultar justamente o valor que você pretendia ler.
Separe o registro da inicialização
A elevação descreve como as declarações são registradas antes da execução de seu escopo. Seu comportamento antes da inicialização é diferente:
| Declaração | Antes de sua linha de inicialização |
|---|---|
| Declaração de função | Sua função está disponível |
var | Seu valor é undefined |
let ou const | O acesso gera um ReferenceError |
Por exemplo, console.log(a); var a = 5; imprime undefined. A atribuição de 5 ainda não aconteceu. Já console.log(b); let b = 5; falha. O intervalo em que o nome léxico existe, mas não pode ser acessado, é sua zona morta temporal, frequentemente abreviada como TDZ.
Rastreie o sombreamento antes de uma linha de declaração
let n = 10;
function f() {
console.log(n);
let n = 20;
}
f();
O n interno sombreia o externo em todo o corpo da função, inclusive antes da linha em que a declaração foi escrita. Em console.log(n), esse nome interno ainda está em sua TDZ, então a chamada gera um ReferenceError. JavaScript não ignora uma declaração local não inicializada para recorrer ao valor externo 10.
O mesmo problema aparece em let count = count + 1; dentro de add. O lado direito lê o novo count local antes de sua inicialização. Se a intenção for atualizar um contador externo, use uma atribuição sem uma nova declaração. Se a intenção for calcular um valor separado, dê a ele outro nome e leia explicitamente o contador externo.
Distinga reatribuição de mutação
let e const não podem declarar novamente o mesmo nome em um escopo; fazer isso gera um SyntaxError. Um escopo interno pode declarar seu próprio nome, o que é sombreamento. var permite declarações repetidas em seu escopo, tornando mais difícil perceber a reutilização acidental de nomes.
const impede a reatribuição de sua variável. Ele não congela um vetor:
const list = [];
list.push(1);
A referência armazenada em list permanece a mesma enquanto o vetor muda para [1]. list = []; tentaria substituir a referência armazenada e falharia.
Mantenha uma declaração no menor escopo que precisa dela. Nomes distintos para tarefas distintas reduzem a necessidade de acompanhar mentalmente o sombreamento, e inicializar antes do uso evita falhas na TDZ.
Principais conclusões
- A busca de um nome para na declaração mais próxima que o envolve.
- Uma declaração local sombreia um nome externo mesmo antes da inicialização.
varcomeça comoundefined;leteconstsão inacessíveis em sua TDZ.- Uma atribuição atualiza uma vinculação existente; uma declaração cria uma vinculação.
constimpede a reatribuição, mas o conteúdo de um vetor ainda pode mudar.- Uma nova declaração no mesmo escopo difere do sombreamento em um escopo aninhado.
4Usar fechamentos para dar memória própria às funções
Uma função retornada pode manter acesso às variáveis ao redor de sua definição. Essa ligação preservada dá memória a um contador e permite que uma fábrica construa funções com configurações próprias. Você vai rastrear chamadas separadas de uma fábrica, estado compartilhado e alterações posteriores em variáveis capturadas e depois usar as mesmas regras para explicar funções criadas em um laço. O estado privado decorre do escopo léxico comum, mesmo depois que a chamada externa retorna.
4.1 Explicar o que um fechamento mantém na memória
Uma função mantém uma ligação com o escopo onde foi criada. A função e esse acesso preservado formam um fechamento. A ligação se torna especialmente útil quando uma função aninhada é retornada e chamada depois que sua chamada externa terminou.
Dê memória a uma função retornada
function makeCounter() {
let count = 0;
return function () {
count = count + 1;
return count;
};
}
const counter = makeCounter();
A chamada de makeCounter cria count, cria uma função interna e retorna essa função. Ela ainda não executa o corpo interno. counter agora contém o valor da função. Sua primeira chamada altera o count preservado de 0 para 1 e retorna 1. Sua chamada seguinte altera a mesma variável para 2 e retorna 2.
O quadro de pilha da chamada externa terminou, mas a variável capturada permanece acessível. Uma variável local que não tem mais nenhum uso acessível pode ser liberada; aqui, a função interna que pode ser chamada ainda precisa dela. O acesso pelo fechamento permanece após um retorno sem tornar a variável global.
Crie memórias independentes com chamadas separadas
const a = makeCounter();
const b = makeCounter();
a();
a();
b();
Os resultados são 1, 2 e 1. Cada chamada da fábrica criou um count diferente. As duas funções retornadas por chamadas separadas têm o mesmo código, mas ambientes preservados diferentes.
Copiar uma referência a uma função retornada funciona de outra maneira. Se const alias = a;, então alias() usa o contador existente de a; não chama makeCounter nem cria outro ambiente.
Mantenha variáveis, não cópias fixas
let greeting = "Hi";
const say = () => greeting;
greeting = "Hello";
say() retorna “Hello”. O fechamento lê a variável quando é executado, então uma atualização posterior fica visível. Ele não armazena uma cópia fixa de “Hi” no momento da criação.
O código externo não pode acessar diretamente o count local, e counter.count é undefined. Apenas código com acesso a esse ambiente léxico pode lê-lo ou atualizá-lo. Chamar o contador de outra função não muda qual contador ele usa.
Compartilhe estado dentro de uma chamada externa
function makeWallet() {
let balance = 0;
return [
function deposit(n) { balance += n; return balance; },
function getBalance() { return balance; }
];
}
const [deposit, getBalance] = makeWallet();
deposit(50);
deposit(20);
getBalance() retorna 70 porque as duas funções retornadas foram criadas na mesma chamada e se referem ao mesmo balance. Outra chamada de makeWallet() cria um saldo separado.
Principais conclusões
- Um fechamento mantém acesso ao escopo onde sua função foi criada.
- Uma variável capturada pode continuar utilizável depois que sua chamada externa retorna.
- Chamadas separadas de uma fábrica criam ambientes separados.
- Fechamentos leem os valores atuais das variáveis, não cópias fixas do momento da criação.
- Funções da mesma chamada externa podem compartilhar estado privado.
4.2 Usar fechamentos para criar fábricas de funções e estado privado
Uma fábrica de funções usa o acesso de um fechamento para criar funções com configurações ou estado próprios. Quem chama a fábrica fornece a configuração uma vez e depois usa a função retornada muitas vezes.
Configure uma função com um parâmetro
function makeMultiplier(factor) {
return (n) => n * factor;
}
const double = makeMultiplier(2);
const triple = makeMultiplier(3);
double(5) retorna 10 e triple(5) retorna 15. Cada função de seta retornada mantém o factor de sua própria chamada da fábrica. A função externa configura o comportamento; a interna executa o trabalho repetido.
A forma compacta é const makeMultiplier = (factor) => (n) => n * factor;. Duas setas significam duas criações de função, não dois cálculos consecutivos sobre um número. makeMultiplier(4)(5) primeiro retorna uma função configurada com 4 e depois a chama com 5 para obter 20.
Aplique uma regra ao estado privado
function makeLimiter(max) {
let used = 0;
return function () {
if (used >= max) {
return false;
}
used = used + 1;
return true;
};
}
const tryLogin = makeLimiter(3);
As três primeiras chamadas retornam true e incrementam used. A quarta retorna false antes de incrementar. used é local à chamada da fábrica, então o código externo não pode redefini-la por esse nome. A função retornada controla quais alterações estão disponíveis. Outro limitador teria sua própria variável used e seu próprio máximo.
Analise funções criadas em laços
const fns = [];
for (var i = 0; i < 3; i++) {
fns.push(() => i);
}
O laço cria três funções, mas apenas um var i. Após o laço, essa variável contém 3. Chamar qualquer função armazenada agora retorna 3 porque cada fechamento lê a mesma vinculação atual. Criar uma função não avalia seu corpo nem guarda uma cópia fixa do i atual.
Alterar o cabeçalho para let i = 0 dá ao laço uma vinculação separada para cada iteração. As funções armazenadas retornam então 0, 1 e 2. A mesma distinção explica a impressão adiada: uma função de retorno de setTimeout que lê var i depois do laço vê o valor final, enquanto a versão com let mantém o valor de cada iteração.
Reconheça uma expressão imediatamente invocada
Código antigo muitas vezes cria um escopo de função privado e o executa imediatamente:
(function () {
var hidden = 1;
console.log(hidden);
})();
Isso é uma expressão de função imediatamente invocada, ou IIFE. Os parênteses externos criam uma expressão de função; os parênteses finais a chamam. hidden pertence a essa função, mantendo-se fora do escopo global ao redor. É um padrão comum em código escrito antes das declarações com escopo de bloco e dos módulos.
O acesso preservado também afeta a memória. Um fechamento que pode ser chamado e precisa de um vetor grande mantém esses dados acessíveis. Quando a função e seus dados capturados deixam de estar acessíveis, eles podem ser liberados. Armazene fechamentos de longa duração de forma consciente quando sua memória fizer parte da tarefa.
Principais conclusões
- Fábricas retornam funções configuradas por seus argumentos.
- Variáveis privadas podem ser alteradas por meio das operações retornadas.
- Fechamentos em laços com
varcompartilham uma vinculação; iterações comletrecebem vinculações separadas. - Uma IIFE cria um escopo de função e o executa imediatamente.
- Fechamentos acessíveis mantêm acessíveis os dados capturados de que precisam.
5Passar funções para outras funções
Passar uma função permite que quem faz a chamada escolha parte do comportamento de outra função. Você vai examinar como as funções receptoras invocam funções de retorno, quais argumentos fornecem e como usam o resultado. Métodos de vetor conhecidos se tornam exemplos de padrões reutilizáveis de ordem superior. Por fim, funções retornadas vão envolver chamadas, conectar saídas a entradas ou manter argumentos selecionados prontos para uso posterior, combinando valores de função com acesso por fechamento.
5.1 Passar uma função de retorno para uma função
Uma função de retorno é uma função fornecida como argumento para que outra função a chame. Ela permite que a função receptora cuide da tarefa geral enquanto quem faz a chamada fornece uma ação ou teste específico.
Passe uma ação para um laço
function repeat(times, action) {
for (let i = 0; i < times; i++) {
action(i);
}
}
repeat(3, (i) => console.log(i));
repeat controla o laço e escolhe os valores 0, 1 e 2. A função de seta determina o que fazer com cada valor: imprimi-lo. Cada action(i) chama a função fornecida com o número daquela iteração.
Você pode passar uma função existente ou escrever uma no ponto de uso. repeat(2, sayHi) passa a função nomeada. repeat(2, (i) => console.log("Round " + (i + 1))) imprime “Round 1” e “Round 2”. Uma função de retorno pode ignorar argumentos de que não precisa.
Passe o valor, sem chamar antes
repeat(3, sayHi()) avalia sayHi() ao preparar os argumentos. Se sayHi apenas imprime, seu valor de retorno é undefined, então repeat recebe esse valor como action. A chamada posterior de action(i) falha com um TypeError porque undefined não pode ser chamado.
Use repeat(3, sayHi) para passar a função. Se a chamada posterior precisar de argumentos escolhidos, passe uma função envoltória como () => sayHi() para que a chamada aconteça dentro do corpo da função de retorno. Criar uma função e invocá-la são etapas separadas.
Verifique quais argumentos a função receptora fornece
Funções de retorno de vetores muitas vezes recebem mais de um argumento: o elemento, o índice e o vetor inteiro. Passar uma função existente só é seguro quando essas entradas correspondem aos significados de seus parâmetros.
["1", "2", "3"].map(parseInt);
Isso retorna [1, NaN, NaN]. parseInt aceita um segundo argumento para a base numérica, então o índice do vetor altera seu comportamento. Use ["1", "2", "3"].map((s) => parseInt(s, 10)) para escolher explicitamente a base 10, ou .map(Number) para converter essas cadeias de caracteres.
Retorne uma resposta à função receptora
function countIf(arr, test) {
let n = 0;
for (const x of arr) {
if (test(x)) {
n++;
}
}
return n;
}
countIf([3, 8, 12, 5], (x) => x > 4) retorna 3. A função de retorno responde a uma pergunta para cada item; countIf usa cada resposta para decidir se incrementa o contador. Compare com repeat, cujo laço ignora o valor de retorno da função de retorno. O código ou a documentação da função receptora informa tanto os argumentos quanto a função do resultado.
Funções de retorno podem ser executadas imediatamente ou mais tarde. repeat e os métodos de vetor as chamam de forma síncrona antes de retornar. setTimeout(sayHi, 2000) agenda uma para mais tarde enquanto o código seguinte continua imediatamente. Ser uma função de retorno descreve como uma função é fornecida, não quando ela deve ser executada.
Principais conclusões
- Uma função de retorno é passada para que outra função a chame.
- A função receptora controla os argumentos da função de retorno e se seu resultado é usado.
- Adicionar os parênteses de chamada cedo demais pode passar um resultado em vez de uma função.
- Argumentos de funções de retorno de vetores podem alterar o comportamento de uma função existente.
- Funções de retorno podem ser síncronas ou adiadas.
5.2 Usar e escrever funções de ordem superior
Uma função de ordem superior recebe uma função, retorna uma função ou faz ambas as coisas. O valor de função fornece o comportamento, enquanto a função de ordem superior gerencia um padrão reutilizável.
Escreva o padrão uma vez
function myMap(arr, transform) {
const result = [];
for (const item of arr) {
result.push(transform(item));
}
return result;
}
myMap([1, 2, 3], (n) => n * n) retorna [1, 4, 9]. O laço e a construção do vetor de saída permanecem os mesmos; a função de retorno decide como cada entrada muda. Um filtro correspondente adiciona um item apenas quando test(item) é avaliado como verdadeiro. Com [4, 7, 10, 3] e (n) => n % 2 === 0, esse padrão mantém [4, 10].
Escolha o contrato de função de retorno necessário
| Método | Função da função de retorno | Resultado do método |
|---|---|---|
map | Retornar cada valor transformado | Um novo vetor do mesmo tamanho |
filter | Retornar um valor avaliado como verdadeiro para os itens a manter | Um novo vetor com os itens selecionados |
find | Testar se há um item correspondente | A primeira correspondência, ou undefined |
forEach | Executar uma ação para cada item | undefined |
reduce | Retornar o próximo acumulador | O acumulador final |
map, filter e find não modificam o vetor de entrada por si mesmos, mas as funções de retorno ainda podem modificá-lo. Retornar um novo vetor de um método não torna puras todas as funções de retorno fornecidas.
Transporte um resultado por uma redução
[5, 10, 15].reduce((total, n) => total + n, 0);
O acumulador inicial é 0. A função de retorno retorna 5 para o primeiro item, 15 para o segundo e 30 para o terceiro. Cada retorno se torna o total da chamada seguinte. O método retorna 30 após o último item. Um vetor vazio sem acumulador inicial faz reduce gerar um TypeError; um valor inicial explícito também define o resultado útil para uma entrada vazia.
Retornar vetores torna o encadeamento possível. [3, 8, 12, 5].filter((n) => n > 4).map((n) => n * 2) primeiro mantém [8, 12, 5] e depois o transforma em [16, 24, 10]. Leia a cadeia na ordem de execução, da esquerda para a direita.
Forneça uma regra de ordenação numérica
Sem um comparador, sort compara entradas como cadeias de caracteres. Portanto, [10, 9, 1].sort() produz [1, 10, 9]. Para ordem numérica crescente, use .sort((a, b) => a - b). Um retorno negativo coloca a primeiro, um retorno positivo coloca b primeiro e zero deixa o par equivalente para a ordenação. Ao contrário de map e filter, sort reorganiza seu vetor de entrada.
Verifique os valores retornados
[1, 2, 3].map((n) => { n * 2; }) produz [undefined, undefined, undefined] porque o bloco não tem retorno. Usar forEach para construir um novo resultado por atribuição também falha se você esperar que o próprio método retorne um vetor. Escolha map para transformação e forEach para ações cujas saídas coletadas não são necessárias.
Um método de ordem superior expressa claramente um padrão conhecido. Um laço simples continua útil quando o trabalho precisa de break ou não se encaixa no contrato do método.
Principais conclusões
- Funções de ordem superior recebem funções, retornam funções ou fazem ambas as coisas.
map,filterefindusam os resultados das funções de retorno de maneiras diferentes.forEachretornaundefined;reduceretorna o acumulador final.- Uma função de retorno de redução deve retornar o próximo acumulador.
- A ordenação numérica precisa de um comparador e modifica o vetor original.
- Um método que retorna um novo vetor ainda pode executar funções de retorno com efeitos colaterais.
5.3 Construir novas funções a partir de funções existentes
Uma função pode retornar uma nova função que mantém uma função existente na memória. Funções envoltórias, composição e aplicação parcial usam esse padrão para adaptar o comportamento sem copiar o corpo da função original.
Envolva uma chamada e preserve seu resultado
function logged(fn) {
return (...args) => {
console.log("Calling with", args);
const result = fn(...args);
console.log("Result", result);
return result;
};
}
const add = (a, b) => a + b;
logged(add) cria uma função que mantém add como fn. Chamar logged(add)(2, 3) reúne os argumentos, repassa-os para add, imprime o valor retornado e retorna 5. Rest reúne as entradas da função envoltória; o espalhamento as repassa. O retorno final mantém a saída disponível para cálculos posteriores.
Já logged(add(2, 3)) passa o número 5. Chamar essa função envoltória mais tarde tenta chamar 5 e gera um TypeError. Há duas etapas: configurar a função envoltória com uma função e depois chamar a função retornada com entradas.
Mantenha estado ao redor de uma operação
function once(fn) {
let done = false;
let result;
return (...args) => {
if (!done) {
done = true;
result = fn(...args);
}
return result;
};
}
Para uma operação que retorna normalmente, a primeira chamada executa fn e armazena seu resultado. Chamadas posteriores retornam esse valor armazenado sem executar fn novamente, mesmo que seus argumentos sejam diferentes. O booleano registra se a operação foi executada; verificar se o resultado é avaliado como verdadeiro estaria errado quando um resultado válido fosse 0 ou false. Cada chamada de once cria seu próprio indicador e resultado armazenado.
Passe um resultado para outra função
const compose = (f, g) => (x) => f(g(x));
const addOne = (n) => n + 1;
const square = (n) => n * n;
compose(square, addOne)(3) executa addOne(3) primeiro e depois eleva 4 ao quadrado para retornar 16. compose(addOne, square)(3) primeiro eleva 3 ao quadrado e depois adiciona 1, retornando 10. A função mais próxima da entrada é executada primeiro, então a ordem dos argumentos de compose importa.
Fixe alguns argumentos antecipadamente
function partial(fn, ...fixed) {
return (...rest) => fn(...fixed, ...rest);
}
const greetWith = (greeting, name) => greeting + ", " + name;
const hello = partial(greetWith, "Hello");
hello("Ana") chama greetWith("Hello", "Ana") e retorna “Hello, Ana”. Essa função auxiliar de aplicação parcial coloca os argumentos fixos primeiro e os argumentos posteriores depois; mudar essa ordem altera a chamada subjacente.
Principais conclusões
- Uma função envoltória mantém uma função na memória e acrescenta comportamento ao redor de sua chamada.
- Rest e espalhamento permitem que uma função envoltória repasse argumentos separados.
- Retornar o resultado da função envolvida mantém esse resultado utilizável por quem a chamou.
- Uma função envoltória de execução única mantém um indicador e o primeiro resultado na memória.
compose(f, g)aplicagantes def.- A aplicação parcial fixa argumentos selecionados antes de chamadas posteriores.
Exercícios resolvidos de Funções em JavaScript
1Criar, armazenar e chamar uma função
11 passos
- 1IntroduçãoSepare a receita da chamada
Uma função armazena instruções para que várias chamadas possam reutilizá-las. Sua definição descreve uma tarefa; uma chamada executa essa tarefa com entradas específicas.
Acompanhe uma saudação em três formas: uma declaração, uma expressão armazenada em uma variável e uma função de seta. Observe dois detalhes conforme a sintaxe muda: quando a função fica disponível e se seu corpo retorna um valor.
- 2ExemploDefina uma saudação
function greet(name) { console.log("Hello, " + name + "!"); }A palavra-chave, o nome, a lista de parâmetros e o corpo formam uma declaração. Nada foi impresso ainda: a definição armazena as instruções.
nameé um parâmetro cujo valor virá de uma chamada. O corpo pode ser reutilizado porque descreve como construir a saudação sem escolher antecipadamente o nome de uma pessoa. - 3ExemploExecute as instruções armazenadas
greet("Amira"); greet("Li");As chamadas imprimem “Hello, Amira!” e “Hello, Li!” em linhas separadas. Cada uma reinicia o mesmo corpo com sua própria entrada.
A expressão
greetsozinha é o valor da função. Os parênteses emgreet("Li")solicitam sua execução. Mesmo uma função sem entradas precisa de parênteses na chamada, como emsayHi(). - 4DesafioPreveja a chamada antecipada
O que é impresso quando este script é executado?
show(); function show() { console.log("Ready"); }---reveal---
Ele imprime “Ready” uma vez. A declaração passa por elevação dentro de seu escopo, então a função está disponível na primeira linha. A elevação não executa o corpo por si só: a chamada explícita
show()o executa. Uma linha que contivesse apenasshow;leria a função sem imprimir nada. - 5ExplicaçãoMantenha a função como valor
const square = function (n) { return n * n; }; const alias = square;A expressão cria uma função, e
squarea armazena. A segunda atribuição copia sua referência paraalias; ela não calcula um quadrado. Tantosquare(3)quantoalias(3)retornam 9.Em comparação,
const answer = square(3);chama a função e armazena o número retornado. - 6DesafioLocalize a primeira falha
Este script imprime 16?
console.log(square(4)); const square = function (n) { return n * n; };---reveal---
Não. Ler
squareantes de sua inicialização comconstgera umReferenceError. A atribuição não foi executada, então o valor de função da expressão está indisponível. Mova a chamada para baixo da atribuição ou use uma declaração de função se a função precisar estar disponível para chamada antes nesse escopo. - 7ExemploUse uma função de seta concisa
const square = (n) => n * n; const add = (a, b) => a + b;Um corpo conciso de função de seta retorna sua expressão, então
square(4)retorna 16 eadd(2, 3)retorna 5.Um parâmetro simples pode dispensar parênteses:
n => n * n. Listas vazias, vários parâmetros, parâmetros padrão e parâmetros rest precisam deles. Funções de seta são expressões, então essas variáveisconstainda precisam ser inicializadas antes do uso. - 8DesafioCorrija um resultado ausente
Por que
add(2, 3)retornaundefined, e que alteração faz com que retorne 5?const add = (a, b) => { a + b; };---reveal---
As chaves iniciam um bloco, então a expressão é avaliada, mas não retornada. Escreva
(a, b) => { return a + b; }ou remova as chaves para usar(a, b) => a + b. Ambas as correções retornam a soma. Apenas adicionar um ponto e vírgula depois da atribuição não muda o comportamento de retorno do corpo. - 9DicaRetorne uma expressão de objeto
const makePoint = (x, y) => ({ x: x, y: y });Os parênteses fazem do literal de objeto uma expressão retornada pelo corpo conciso. Uma chave de abertura sem parênteses iniciaria um bloco.
Funções de seta não têm seus próprios
thisouargumentse não podem ser construtoras. A forma curta funciona bem para cálculos simples, mas essas diferenças importam ao converter outros tipos de função. - 10DesafioAcompanhe uma função e seu segundo nome
O que
copy(5)retorna?const adjust = (n) => { return n + 2; }; const copy = adjust;---reveal---
Retorna 7. O bloco da função de seta contém um retorno explícito, e
copycontém a mesma função que pode ser chamada queadjust. A atribuição não chamou a função nem armazenou um resultado numérico. Substituirconst copy = adjust;porconst copy = adjust(5);armazenaria 7, que não pode ser chamado. - 11RecapitulaçãoLeia a operação em cada linha
Uma declaração cria uma tarefa nomeada que fica disponível desde o início de seu escopo. Uma expressão cria um valor de função quando a execução chega a ela. O corpo conciso de uma função de seta retorna automaticamente; seu corpo em bloco precisa de
return.Ao rastrear o código, distinga uma definição de função, uma atribuição de referência e uma chamada. São operações diferentes, mesmo quando usam o mesmo nome.
2Rastrear entradas, saídas e chamadas em espera
16 passos
Salve esta aula para seguir cada Lição Interativa passo a passo.
- 1IntroduçãoAcompanhe o que entra e sai de uma chamada
- 2ExemploRelacione argumentos pela posição
- 3ExemploDistinga mutação de reatribuição
- 4DesafioPreserve a entrada original
- 5ExplicaçãoO valor padrão se aplica a undefined
- 6ExemploReúna e depois expanda
- 7DesafioUse um valor padrão com rest
- 8ExemploRetorne o número necessário a quem chama a função
- 9ExemploInterrompa um caminho com um retorno antecipado
- 10DesafioAvalie de dentro para fora
- 11ExemploLembre-se da chamada não concluída
- 12ExplicaçãoDê à recursão um caminho de parada
- 13ExemploResolva os produtos em espera
- 14DesafioCorrija um limite da recursão
- 15ConexãoLeia uma falha pela pilha
- 16RecapitulaçãoAcompanhe um valor por cada limite
3Encontrar a declaração associada a um nome
10 passos
Salve esta aula para seguir cada Lição Interativa passo a passo.
- 1IntroduçãoPergunte onde o nome pertence
- 2ExemploCompare limites de bloco e de função
- 3ExemploUse o local da definição
- 4DesafioRastreie um valor local e um externo
- 5ExplicaçãoPare na declaração mais próxima
- 6Ideia-chaveRegistrado não significa inicializado
- 7DesafioPreveja a leitura de um nome sombreado
- 8DicaSepare reatribuição de mutação
- 9DesafioCombine uma função de retorno com o momento da busca
- 10RecapitulaçãoRastreie o local antes do valor
4Dar memória própria às funções retornadas
11 passos
Salve esta aula para seguir cada Lição Interativa passo a passo.
- 1IntroduçãoMantenha acesso após um retorno
- 2ExemploCrie um contador sem chamá-lo
- 3ExemploAtualize a variável preservada
- 4DesafioCompare contadores independentes
- 5Ideia-chaveMantenha a vinculação, não uma cópia fixa
- 6ExemploCrie um cálculo configurado
- 7ExemploMantenha uma contagem privada de usos
- 8DesafioExplique três resultados iguais
- 9ConexãoReconheça o escopo privado em código antigo
- 10DesafioCompartilhe uma operação configurada
- 11RecapitulaçãoAcompanhe quantos ambientes existem
5Passar comportamento e construir funções adaptadas
15 passos
Salve esta aula para seguir cada Lição Interativa passo a passo.
- 1IntroduçãoDeixe uma função fornecer o comportamento variável
- 2ExemploChame a ação fornecida
- 3DesafioUse o resultado de uma função de retorno
- 4DicaExamine todos os argumentos da função de retorno
- 5ExemploEscreva um padrão de transformação
- 6ExplicaçãoEscolha a forma do resultado
- 7DesafioRastreie uma seleção e uma transformação
- 8Ideia-chaveDefina a ordem numérica de forma consciente
- 9ExemploEnvolva e repasse uma chamada
- 10ExplicaçãoMantenha na memória se uma chamada foi executada
- 11ExemploFaça a composição de dentro para fora
- 12ExemploFixe entradas antes da chamada posterior
- 13DesafioPasse uma função de retorno configurada
- 14DesafioCorrija um resultado perdido em uma função envoltória
- 15RecapitulaçãoVerifique as duas etapas do código de ordem superior
Questões de Funções em JavaScript
Conjunto 1: Ler funções e acompanhar seus valores10 Questões de Prática
Questão 1 de 10
function showLabel() { console.log("Ready"); }, qual instrução chama a função?Mostrar respostaOcultar resposta
showLabel(); invoca a função, então seu corpo imprime “Ready”. Ler showLabel sem parênteses apenas produz o valor da função; atribuir esse valor a outro nome também deixa o corpo sem execução. Os parênteses de chamada são necessários mesmo quando não há argumentos.Questão 2 de 10
O que copy contém após este código?
const triple = function (n) { return n * 3; };
const copy = triple;
Mostrar respostaOcultar resposta
triple e armazena a mesma referência em copy. Não há chamada no lado direito, então nenhum resultado numérico é calculado. Uma chamada posterior de copy(4) invocaria essa função e retornaria 12; copiar sua referência não a executa.Questão 3 de 10
O que adjust(8) retorna?
const adjust = (n) => { n - 3; };
Mostrar respostaOcultar resposta
return. Ele avalia a subtração, mas termina com undefined. O resultado seria 5 com um corpo conciso, (n) => n - 3, ou com um return n - 3; explícito dentro do bloco.Questão 4 de 10
O que difference(12, 5) retorna?
function difference(first, second) {
return first - second;
}
Mostrar respostaOcultar resposta
first como 12, e o segundo define second como 5. Subtrair na ordem do corpo resulta em 7. O resultado -7 viria da inversão das posições dos argumentos; os nomes dos parâmetros não reorganizam os valores fornecidos pela chamada.Questão 5 de 10
O que readLevel(0) retorna?
function readLevel(level = 40) { return level; }
Mostrar respostaOcultar resposta
undefined, então permanece 0. Um parâmetro padrão não substitui todo valor avaliado como falso. A expressão antiga level || 40 substituiria 0 por 40, mas essa é uma regra diferente da usada pelo valor padrão na definição.Questão 6 de 10
Qual número é impresso e qual é o valor de result?
function displayTotal(a, b) { console.log(a + b); }
const result = displayTotal(6, 9);
Mostrar respostaOcultar resposta
undefined. Ver um número no console não significa que a chamada retornou esse número; para isso, o corpo precisaria de return a + b;.Questão 7 de 10
O que acontece quando getFlag() é chamada?
function getFlag() {
if (true) { let flag = "on"; }
return flag;
}
Mostrar respostaOcultar resposta
let pertence ao bloco if. No retorno fora desse bloco, não existe uma declaração acessível de flag, então a leitura gera um ReferenceError. Substituir let por var daria à variável escopo de função e permitiria que essa função retornasse “on”.Questão 8 de 10
Quais valores as duas chamadas retornam, em ordem?
function makeStep() {
let value = 4;
return () => { value += 2; return value; };
}
const step = makeStep();
step();
step();
Mostrar respostaOcultar resposta
value inicializada com 4. A função retornada mantém acesso a ela, então a primeira chamada a atualiza para 6 e a segunda atualiza a mesma vinculação para 8. O retorno da fábrica não redefine a variável capturada antes de cada chamada posterior.Questão 9 de 10
Qual é a sequência completa dos valores impressos?
function deliver(action) { action(7); }
console.log("start");
deliver((n) => console.log(n));
console.log("end");
Mostrar respostaOcultar resposta
deliver chama a função de seta fornecida imediatamente com 7, antes de retornar ao script ao redor. A saída é, portanto, “start”, 7 e depois “end”. Uma função de retorno não é necessariamente adiada; esta função receptora contém uma chamada direta comum e nenhuma operação de agendamento.Questão 10 de 10
Qual é o valor final de result?
const values = [2, 7, 4, 9];
const result = values
.filter((n) => n > 3)
.map((n) => n - 1)
.reduce((total, n) => total + n, 5);
Mostrar respostaOcultar resposta
[7, 4, 9], e o map o transforma em [6, 3, 8]. A redução a partir do valor inicial 5 resulta em 11, depois 14 e depois 22. Um resultado de 17 omite o acumulador inicial fornecido e soma apenas os elementos transformados, mudando o significado da chamada final.Conjunto 2: Escolher entradas e rastrear o estado preservado10 Questões de Prática
Salve esta aula para ver todas as respostas e praticar cada conjunto.
Questão 1 de 10
O que acontece quando read() é chamada?
let label = "outer";
function read() {
return label;
let label = "inner";
}
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 2 de 10
O que a chamada final retorna?
let word = "first";
const read = () => word;
word = "second";
function run() {
const word = "third";
return read();
}
run();
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 3 de 10
Qual vetor a expressão final produz?
const readers = [];
for (let i = 1; i <= 3; i++) {
let value = i;
readers.push(() => value);
value += 10;
}
readers.map((read) => read());
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 4 de 10
O que acontece quando este script é executado?
announce();
function announce() { console.log("Open"); }
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 5 de 10
value igual à sua entrada n?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 6 de 10
O que a chamada retorna?
function combine(first, ...rest) {
return first + rest.length;
}
const values = [8, 2, 6];
combine(...values);
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 7 de 10
O que guarded(0) retorna?
function guarded(n) {
if (n === 0) { return "empty"; }
return "filled";
}
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 8 de 10
O que totalDown(3) retorna?
function totalDown(n) {
if (n === 0) { return 0; }
return n + totalDown(n - 1);
}
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 9 de 10
O que acontece quando a última linha é executada?
function speak() { console.log("Hi"); }
function twice(action) { action(); action(); }
twice(speak());
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 10 de 10
Qual é o resultado final?
const compose = (f, g) => (x) => f(g(x));
const subtract = (n) => n - 2;
const triple = (n) => n * 3;
const first = compose(triple, subtract);
const second = compose(subtract, triple);
first(second(4));
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Conjunto 3: Relacionar chamadas, escopo e funções de retorno10 Questões de Prática
Salve esta aula para ver todas as respostas e praticar cada conjunto.
Questão 1 de 10
Quais são os valores de values e first depois disso?
const values = [12, 3, 20];
const first = values.sort((a, b) => a - b)[0];
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 2 de 10
O que as chamadas read(0) e read(9) retornam, em ordem?
function once(fn) {
let done = false;
let result;
return (...args) => {
if (!done) {
done = true;
result = fn(...args);
}
return result;
};
}
const read = once((n) => n * 2);
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 3 de 10
O que acontece quando este script é executado?
const saved = build;
const build = function () { return 9; };
console.log(saved());
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 4 de 10
Qual valor é atribuído a out?
const describe = (n) => {
const doubled = n * 2;
return doubled + 1;
};
const out = describe(6);
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 5 de 10
Quais são os valores de items e answer após este código?
const items = [5];
function change(list) {
list.push(8);
list = [1];
return list;
}
const answer = change(items);
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 6 de 10
O que countLeaves([0, [[], 2], [[3], 4]]) retorna?
function countLeaves(list) {
let count = 0;
for (const item of list) {
if (Array.isArray(item)) {
count += countLeaves(item);
} else {
count++;
}
}
return count;
}
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 7 de 10
O que readValue() retorna?
function readValue() {
if (true) { var value = 14; }
return value;
}
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 8 de 10
Quais são os dois valores impressos?
let count = 2;
function update() {
let count = 7;
count += 1;
return count;
}
console.log(update());
console.log(count);
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 9 de 10
Quais valores as três últimas chamadas retornam?
function makeCounter(start) {
let n = start;
return () => { n++; return n; };
}
const a = makeCounter(5);
const alias = a;
const b = makeCounter(5);
a();
alias();
b();
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 10 de 10
O que scale(7) retorna?
const makeScale = (factor) => (n) => n * factor;
const scale = makeScale(4);
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Conjunto 4: Analisar o comportamento de funções em pequenos programas10 Questões de Prática
Salve esta aula para ver todas as respostas e praticar cada conjunto.
Questão 1 de 10
function printLine() { console.log("---"); } e depois executa printline();. O que acontece?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 2 de 10
O que a chamada final retorna?
const climb = function step(n) {
return n === 0 ? 0 : 1 + step(n - 1);
};
const saved = climb;
saved(2);
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 3 de 10
Quais são os valores de data e result ao final?
const data = [2, 5];
function extend(list, item) {
const next = list.concat(item);
list.push(9);
return next;
}
const result = extend(data, 7);
result.push(1);
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 4 de 10
O que a chamada final retorna?
function total(base = 4, ...extras) {
return extras.reduce((sum, n) => sum + n, base);
}
const values = [undefined, 3, 0, 8];
total(...values);
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 5 de 10
Qual valor esta chamada produz?
function read() {
return;
}
read();
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 6 de 10
A entrada n é um inteiro não negativo. Qual substituição para o retorno recursivo faz a função contar corretamente até 0 tanto para entradas ímpares quanto pares?
function countDown(n) {
if (n === 0) { return 0; }
// recursive return goes here
}
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 7 de 10
O que a chamada final retorna?
const mode = "shared";
function readMode() { return mode; }
function run() {
const mode = "local";
return readMode();
}
run();
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 8 de 10
Quais valores jobs[0]() e jobs[1]() retornam após o laço?
const jobs = [];
for (var i = 0; i < 2; i++) {
const offset = i * 10;
jobs.push(() => offset + i);
}
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 9 de 10
Qual resultado esta expressão produz?
["6", "8", "10"].map((text) => parseInt(text, 10));
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 10 de 10
Quais são os valores impressos de out e seen?
let seen = 0;
const out = [3, 5].forEach((n) => {
seen += n;
return n * 2;
});
console.log(out, seen);
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Conjunto 5: Combinar fechamentos e funções de ordem superior10 Questões de Prática
Salve esta aula para ver todas as respostas e praticar cada conjunto.
Questão 1 de 10
O que acontece quando build() é chamada?
let amount = 6;
function build() {
const read = () => amount;
const result = read();
let amount = 9;
return result;
}
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 2 de 10
Quais valores change(3), read() e otherRead() retornam?
function makePair() {
let n = 1;
return [
(step) => { n += step; return n; },
() => n
];
}
const [change, read] = makePair();
const [, otherRead] = makePair();
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 3 de 10
O que a expressão final retorna?
function makeLimit(max) {
let used = 0;
return () => {
if (used >= max) { return false; }
used++;
return true;
};
}
const allow = makeLimit(2);
[allow(), allow(), allow()];
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 4 de 10
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 5 de 10
Qual é o resultado de add("6", 4) * 2?
function add(a, b) { return a + b; }
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 6 de 10
Qual é o resultado da expressão final?
function choose(n) {
if (n < 0) { return 0; }
return n + 2;
}
choose(choose(-4));
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 7 de 10
Quais são os valores de result e calls após este código?
let calls = 0;
function choose(values, test) {
for (const value of values) {
calls++;
if (test(value)) { return value; }
}
return null;
}
const result = choose([3, 8, 10, 5], (n) => n > 6 && n % 2 === 0);
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 8 de 10
Quais são os valores de items e out depois disso?
const items = [2, 4, 6];
const out = items.map((n, index, array) => {
array[index] = n + 1;
return n * 2;
});
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 9 de 10
O que doubleSaved(9) retorna após a chamada anterior mostrada?
function once(fn) {
let done = false;
let saved;
return (...args) => {
if (!done) { done = true; saved = fn(...args); }
return saved;
};
}
const compose = (f, g) => (x) => f(g(x));
const saved = once((n) => n + 3);
const doubleSaved = compose((n) => n * 2, saved);
saved(4);
doubleSaved(9);
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 10 de 10
Qual é o vetor retornado?
const partial = (fn, ...fixed) => (...later) => fn(...fixed, ...later);
const offset = (base, n) => base + n;
const addFive = partial(offset, 5);
const compose = (f, g) => (x) => f(g(x));
const transform = compose((n) => n * n, addFive);
[1, 3].map(transform);
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Travou em uma questão sua? Ajuda com Tarefas
Termos-Chave
A
Acumulador
[5, 10, 15].Aplicação parcial
Argumento
area(4, 2.5), os argumentos são 4 e 2,5, independentemente de como os parâmetros foram nomeados.C
Cadeia de escopos
amount local antes de considerar uma variável de mesmo nome em um escopo externo.Cadeia de métodos
Caso base
n <= 1 interrompe as chamadas em 0 ou 1.Caso recursivo
n * factorial(n - 1) espera pelo fatorial menor antes de multiplicar pelo n desta chamada.Chamada de função
sayHi() executa seu corpo, enquanto uma linha contendo apenas sayHi lê o valor da função.Cláusula de guarda
if (b === 0) { return null; } protege a divisão colocada depois dele em safeDivide.Comparador
(a, b) => a - b ordena números em ordem crescente enquanto sort reorganiza o vetor de entrada.Composição
f(g(x)), mudar a ordem para g(f(x)) pode alterar o resultado mesmo quando ambas as funções aceitam números.Corpo conciso
(a, b) => a + b retorna diretamente a soma dos dois argumentos fornecidos.Corpo da função
printHeading contém três instruções de impressão, todas as três são executadas novamente a cada chamada dessa função.Corpo em bloco
(n) => { n * 2; } calcula um produto, mas retorna undefined porque não tem uma instrução de retorno.D
Declaração de função
greet("Li") pode aparecer acima da declaração escrita de greet no mesmo escopo.Desestruturação de vetores
const [low, high] = minMax(values); extrai os dois elementos do vetor retornado.E
Efeito colateral
Elevação
var que passou por elevação antes de sua atribuição fornece undefined; uma chamada antecipada a uma declaração de função pode ser executada.Escopo de bloco
let message declarado em um corpo if não pode ser lido por uma instrução após o fim desse bloco.Escopo de função
var result atribuído em um bloco if pode ser retornado mais tarde pelo corpo da função que o contém.Escopo global
count de nível superior de um script pode se tornar estado compartilhado quando várias funções o leem ou atualizam.Escopo léxico
Estado compartilhado
balance privado.Estado privado
used inacessível diretamente.Expressão de função
const square = function (n) { return n * n; };, a expressão fornece o valor armazenado em square.Expressão de função imediatamente invocada (IIFE)
(function () { var hidden = 1; })(); invocam a função recém-criada.Expressão de função nomeada
const factorial = function fact(n) { /* body */ };, a variável externa é factorial, e o nome interno é fact.F
Fábrica de funções
makeMultiplier(3) retorna uma função que mantém 3 na memória enquanto aceita um número posterior para multiplicar.Fechamento
count privado depois que a fábrica que a criou terminou.Função
printHeading("Report") e printHeading("Totals") para reutilizar uma rotina de cabeçalho.Função anônima
function (n) { return n * n; } não tem um nome interno de função em sua sintaxe escrita.Função de ordem superior
Função de retorno
countIf chama sua função de retorno de teste para cada item e conta aqueles cujos resultados são avaliados como verdadeiros.Função de retorno adiada
sayHi para setTimeout permite que as instruções seguintes sejam executadas antes de a saudação agendada aparecer.Função de retorno de transformação
(n) => n * n transforma os elementos de [1, 2, 3] em quadrados por meio de uma operação de mapeamento.Função de retorno síncrona
Função de seta
(n) => n * 2 fornece uma função de retorno de transformação curta a um método de vetor como map.Função definida no ponto de uso
repeat(3, (i) => console.log(i)) fornece a ação sem uma declaração nomeada separada.Função envoltória
Função envoltória de execução única
Função pura
n * 2 é pura; imprimir o produto introduziria um efeito colateral.L
Literal de objeto
(n) => ({ value: n }) retorna um objeto em vez de tratar suas chaves como um bloco de instruções.M
Modo estrito
"use strict"; faz uma atribuição count = 5; sem declaração gerar um ReferenceError.Mutação
arr.push(3) altera o mesmo vetor que quem a chamou forneceu como argumento.N
Notação camelCase
calculateTotal descreve uma ação e separa suas duas palavras sem espaços ou sublinhados.O
Objeto arguments
arguments[0], mas não pode chamar arguments.map como um método comum de vetor.P
Parâmetro
width e height são parâmetros em function area(width, height) { return width * height; }.Parâmetro padrão
level = 50 mantém um 0 explícito, mas fornece 50 quando o argumento está ausente.Parâmetro rest
function log(label, ...values) { /* body */ }, o primeiro argumento fica separado do vetor rest.Pilha de chamadas
sumOfSquares espera por square(3), a chamada de square tem um quadro acima do quadro de quem a chamou e está em espera.Predicado
(n) => n > 4 seleciona quais números um filtro mantém sem alterar seus valores por si mesmo.Q
Quadro de pilha
n porque têm quadros separados.R
Rastreamento de pilha
Reatribuição
arr = [] redireciona o parâmetro sem substituir a referência ao vetor de quem chamou a função.Recursão
Referência a uma função
const alias = square;, chamar alias(4) invoca a função também acessada por square.Retorno antecipado
null para um divisor zero antes que a execução chegue à instrução de divisão.S
Sintaxe de espalhamento
Math.max(...[3, 7, 2]) passa três números em vez de passar um vetor para Math.max.Sombreamento
count oculta um count externo dentro dessa função sem alterar a vinculação externa.V
Valor avaliado como falso
level || 50 substitui 0, enquanto um parâmetro padrão preserva esse valor fornecido.Valor de retorno
double(5) retorna 10, a expressão double(5) + 1 pode usar esse valor imediatamente para produzir 11.Variável capturada
greeting externo de “Hi” para “Hello” muda o valor que uma chamada posterior a uma função que lê greeting retorna.Variável local
total local sem que suas adições afetem umas às outras.Vinculação
Vinculação por iteração
let i podem retornar valores diferentes após o término do laço.Z
Zona morta temporal
Sobre esta aula
Crie seus próprios Materiais de Estudo
Transforme suas anotações, slides e documentos em Guias de Estudo, Questões de Prática, Flashcards e mais 6 ferramentas.