JavaScript: Introdução e funções de alta ordem
JavaScript: Introdução e funções de alta ordem
JavaScript (JS) é uma linguagem de programação dinâmica, de tipificação fraca e interpretada. Ela foi criada em 1995 pela Netscape Communications, empresa que desenvolveu um dos primeiros navegadores web. Mais tarde, a linguagem foi formalizada na especificação ECMAScript (ES), que vem evoluindo desde então. No momento em que este texto foi escrito, a versão mais recente da especificação é a ECMAScript 2016 (anteriormente conhecida como ECMAScript 7), mas mesmo o ECMAScript 2015, ou ECMAScript 6 não é suportado completamente por todos os navegadores. Projetos como o Babel permitem transformar código de uma versão mais recente para uma versão mais antiga da especificação.
Estruturas básicas
Strings
Strings podem ser escritas com "
, '
ou `
. Em qualquer caso, você pode usar a barra invertida (\
) para “escapar” um caractere que do contrário teria um significado especial. Exemplo:
Apenas as strings com acento grave (`
) permitem interpolação de strings:
Se o let
e o `
não funcionarem, seu navegador pode estar desatualizado. Você pode conseguir rodar trocando por var
e "
(embora o resultado não seja exatamente igual).
Tópico opcional: diferença entre let e var
JavaScript originalmente não permitia declarar variáveis com escopo de bloco; assim, todas as declarações de variáveis feitas com var
são movidas para o início da função, o que é conhecido como hoisting.
Alguns exemplos (experimente trocar var
por let
e vice-versa e veja como o comportamento muda):
Objetos
Objetos em JavaScript são estruturas chave-valor, similar a estruturas que em outras linguagens são chamadas de hash, mapa ou dicionário. As chaves são chamadas propriedades do objeto. Exemplo:
Para saber mais: Working with objects (Mozilla Developer Network).
Arrays
Um array em Javascript (ex.: [1, 2, 3]
) é um objeto mutável, isto é, ele pode ser modificado. As funções que modificam o array são chamadas de funções destrutivas. As funções não-destrutivas são aquelas que não modificam o array; em vez disso, elas retornam um novo array que é construído a partir de um array pré-existente.
Do ponto de vista das linguagens funcionais, uma função deve apenas receber valores como parâmetro e retornar um valor. Se a função modifica algum parâmetro, altera variáveis globais, ou acessa entrada/saída (ex.: modifica um arquivo), esses comportamentos são considerados efeitos colaterais de se chamar a função, e a função é dita não-pura.
Uma função pura, sem efeitos colaterais, vai sempre retornar o mesmo resultado para uma determinada entrada, não importa quantas vezes a função seja chamada.
A seguir, algumas operações sobre arrays. Nos exemplos, considere que a
é um array.
Operação | destrutiva | não-destrutiva |
---|---|---|
Obter primeiro elemento | x = a[0] |
|
Obter restante da lista | a.shift() |
l = a.slice(1) |
Adicionar x ao final | a.push(x) |
l = a.concat([x]) |
Comprimento do array | x = a.length |
|
Criar uma cópia da lista | l = a.slice() |
Funções como cidadãos de primeira classe
Como já vimos, JavaScript é uma linguagem na qual funções são cidadãos de primeira classe, isto é, elas podem ser atribuídas a variáveis, passadas como parâmetro e retornadas de outras funções.
Exemplo:
function cumprimenta() {
alert('Oi, tudo bom?');
}
let botao = document.getElementById('botao1');
botao.addEventListener('click', cumprimenta);
Nesse exemplo, a função cumprimenta
foi passada como parâmetro para a função addEventListener
(tecnicamente, addEventListener
é um método – já que JavaScript é orientada a objetos – mas na prática é muito semelhante a uma função).
Funções de alta ordem
Exemplo de função que recebe função como parâmetro, executando a função 3 vezes, cada vez passando um número:
Note que, desta vez, definimos uma função anônima, function (x) { console.log('Dou-lhe ' + x); }
.
JavaScript já define algumas funções de alta ordem importantes, como a função map
:
Ou a função forEach
, para iterar sobre os elementos:
Funções de alta ordem em JavaScript para arrays: forEach
, filter
, map
, reduce
, some
, every
, find
, findIndex
.
Funções anônimas (sintaxe nova)
Há uma notação compacta para definir funções anônimas: x => x * 2
(função que recebe um argumento, x
e retorna o seu dobro). Outros exemplos:
(x, y) => x + y
: função soma (para funções com dois ou mais argumentos, eles precisam ser envoltos em parênteses)() => 42
: função que não recebe argumentos e sempre retorna 42.x => { console.log(x); console.log(x + 1); }
: função sucessor com impressão no console (para rodar duas ou mais instruções, envolva-as com chaves; ao usar chaves, nenhum valor é retornado)(n, a) => ({nome: n, altura: a})
: função que retorna um objeto construído a partir de dois parâmetros (para retornar um objeto/hash, envolva as chaves em parênteses, para evitar ambiguidade com o caso anterior)
(Experimente converter essa notação para JavaScript antigo)
Exemplo:
Exercícios
map
Implemente a função meuMap
em JavaScript:
(Duas implementações possíveis: criando um array e modificando-o com operações destrutivas, ou usando apenas operações não-destrutivas; )
Filmes
(Baseado em http://reactivex.io/learnrx/.)
Nos exemplos a seguir, considere os seguintes dados (execute o código para carregar a variável filmes
na memória):
map
Use a função map
para mapear o array em um novo array contendo somente os títulos dos filmes
filter
Use a função filter
para selecionar apenas os filmes com notas superior a 4.
map e filter
Agora combine map e filter para retornar apenas os títulos dos filmes com nota superior a 4.
reduce
A função reduce
aplica uma função dada a um acumulador e cada elemento do array para reduzir o array a um único valor. A sintaxe é:
arr.reduce(f, valorInicial)
, onde
valorInicial
é o valor inicial do acumulador- se
valorInicial
não for passado, a função assume que o valor do inicial do acumulador é igual ao valor do primeiro elemento dearr
.
- se
f
é uma função que recebe dois parâmetros:acum
: o valor atual do acumuladorx
: o elemento sendo processado atualmente no array- (na verdade a função
f
recebe 4 argumentos, mas vamos ignorar os outros dois. Se quiser mais detalhes, clique aqui)
- o retorno da função
f
é atribuído ao acumulador para ser usado na próxima invocação def
reduce
retorna o valor final do acumulador
Por exemplo, considere a função somatório, que retorna o somatório dos elementos de um array, escrito da forma tradicional, com for
:
A função pode ser escrita de forma mais compacta usando o reduce
:
Exemplo para somar as notas dos filmes:
reduce (máximo)
Agora use reduce para retornar a maior nota do conjunto.
Dica: use o operador ternário ?:
. A expressão condicao ? x : y
retorna x
se a condicao
for verdadeira, e y
caso contrário.
(Você também pode tentar calcular a maior nota sem usar funções de alta ordem. O código será muito mais longo.)