5 curiosidades sobre funções em JavaScript

Brennda
6 min readJul 16, 2019

--

Photo by Max Chen on Unsplash

Em algumas linguagens de programação, funções são como blocos de código que executam uma determinada tarefa e que podem ser reaproveitados. Em JavaScript, isso também é verdade, porém, as funções possuem um certo poder na linguagem que permite que existam algumas peculiaridades bem interessantes. Então, pega o café e bora começar!

DICA: acompanhe esse artigo com um console ou terminal aberto e vá testando os códigos para ficar mais divertido 😁

#1 Funções são valores

COMO ASSIM!? Em JavaScript funções também são valores?

Observe o exemplo:

function nomeQualquer () {
// código qualquer
}

Isso é uma função em JavaScript, e talvez não pareça ser tão complicado, pois a maioria das linguagens possuem uma sintaxe semelhante na hora de declarar funções. Massss, você também pode fazer isso aqui:

let minhaFuncao = function () {
// código qualquer
}

Você acabou de declarar uma variável e atribuiu a ela uma função. Agora o mais legal, execute typeof minhaFunction. BAM! A variável possui o tipo "function", logo, funções podem ser valores, assim como string, number, boolean, etc. Isso não ocorre em outras linguagens, como Java, por exemplo.

#2 Arguments

Observe a seguinte função:

function soma (a, b) {
return a + b
}

Se você executar soma(2, 2) o retorno será igual a 4, mas e se você passar mais argumentos, soma(2, 2, 3, 4, 5), consegue adivinhar o resultado?

Não, não será um erro, os argumentos extras serão simplesmente ignorados e o retorno continuará sendo 4.

Para acessar os argumentos extras você pode usar o objeto arguments, ele funciona como uma variável de escopo local dentro de todas as funções.

function mostraArgumentos (...arguments) {
console.log(arguments)
}

Ao invocar a função acima você poderá observar o seguinte resultado:

mostraArgumentos(2, 2, 3, 4, 5)
//output
[2, 2, 3, 4, 5]

Argumentsnão é um Array. Parece um Array, mas possui apenas a propriedade length. E o interessante é que você pode referenciar os argumentos passados em uma função acessando pelo índice de cada um, começando pelo 0:

function soma (a, b) {
return arguments[0] + arguments[arguments.length - 1]
}

soma(2,3,4,5)
//output
7

No código acima, a função somafoi reescrita e os argumentos foram acessados pelo objeto arguments. Observe que o retorno será a soma entre o primeiro argumento passado e o último.

#3 call() e apply()

Podemos invocar funções em JavaScript de várias formas diferentes:

— Diretamente, nomeDaFuncao(); passando uma função como parâmetro de outra função, funcaoA(arg, funcaoB); ou retornando uma função de dentro de outra, funcaoA()().

Imagine que você tenha a função mostraIdade:

let mostraIdade = function () {
return this.idade;
};

Sabemos que this, nessa situação, refere-se ao escopo da função mostraIdade.

Agora vamos criar um objeto pessoa:

let pessoa = {
nome :'Maria',
idade : 30
};

Podemos acessar a propriedade idade do objeto pessoa com a função mostraIdade ? Sim! Basta utilizar:

mostraIdade.call(pessoa); 

ou

mostraIdade.apply(pessoa);

call() e apply()são propriedades que permitem que você indique em qual escopo que a função deve ser executada. No exemplo acima, o escopo passado foi o objeto pessoa, no qual a função mostraIdade buscou o atributo idade.

Mas e por que dois, se eles fazem exatamente a mesma coisa?

A única diferença entre call()e apply() está em como você passa os argumentos, além do argumento de escopo. Com call() você pode passar um conjunto de argumentos nomeados, funcao(escopo, arg1, arg2). Com apply(), funcao(escopo, [arg1, arg2, ...]), os argumentos são passados como um único array ou um objeto semelhante a um array. Por exemplo, você pode usar um NodeList ou um objeto com índices personalizados como { '0': 'laranja', '1': 'abacaxi' }.

# 4 bind()

O conceito da propriedade bind() talvez seja um dos mais intrigantes em JavaScript. Segundo a documentação oficial:

A função bind() cria uma nova função vinculada (bound function). Uma função vinculada é um objeto de função exótico (termo da ECMAScript 2015) que encapsula o objeto de função original. Chamar uma função vinculada geralmente resulta na execução de sua função encapsulada.

Confuso, né? Mas ele basicamente cria uma nova função que vincula uma função A a outra função B, e ainda vincula o contexto dessa outra função B. Vamos ao exemplo.

Você tem o seguinte objeto cachorro:

let cachorro = {
som : "auau",
latir : function () {
return this.som
}
}

Note que, se você chamar cachorro.latir() receberá a string "auau", isso porque o método latir retorna o atributo som. Mas agora vamos fazer alguns teste:

let funcaoLatir = cachorro.latir

No código acima, o valor da variável funcaoLatir é o método latir, certo?Então, isso significa que se invocarmos funcaoLatir() vamos obter "auau", certo? Não!

O resultado será undefined, mas por quê? Note que o método latir possui a palavra-chave this, isso significa que ele retorna a propriedade som no contexto do objeto em que está inserido. A variável funcaoLatir não possui o contexto do objeto cachorro e muito menos um atributo som, é apenas uma variável que armazena um método.

Para fazer com que funcaoLatir() de fato retorne "auau" temos que vincular seu contexto ao do objeto cachorro e é aí que a propriedade bind() entra:

let funcaoLatir = cachorro.latir.bind(cachorro)

Ficou um pouco esquisito, né? Vamos fazer um exemplo que pode ocorrer na vida real:

let botao = document.getElementById('meuBotao')// agora você que adiciona um evento a esse botãobotao.addEventListener('click', cachorro.latir)

Quando você clicar no botao, o this do método latir não será no contexto do cachorro, mas sim a janela do navegador em que o addEventListener está sendo chamado. Para resolver isso, bastar chamar a propriedade bind() e passar o contexto desejado:

botao.addEventListener('click', cachorro.latir.bind(cachorro))

Agora, o que foi passado ao addEventListener não foi a janela do navegador, mas sim uma nova função que conecta cachorro.latir ao contexto do objeto cachorro. Bizarro, né? Mas é assim que o bind() funciona, cria uma nova função que serve para conectar uma função a outra.

Se você for uma pessoa esperta, já deve ter testado o exemplo acima usando apply() ou call() e pensado: “por que existe o bind() se eu usei o call() e funcionou?”. E, realmente, funciona como o bind(), mas diferente dele, call() e apply() executam a função vinculada imediatamente. Talvez, em algum momento da sua vida, você não vai querer que isso ocorra e vai usar o bind().

# 5 Function declaration x Function expression

Se você já criou uma função em JavaScript deve ter se deparado com essas duas formas aqui:

function declarationFun () {
//algum código aqui
}
// invocando a função
declarationFun()

e

let expressionFun = function () {
// algum código aqui
}
// invocando a função
expressionFun()

Com certeza, em algum momento da sua vida, já deve ter ouvido alguém falar “ah, não faz diferença a ordem em que você declara e executa funções”.

Será mesmo?

declarationFun()function declarationFun () {
//algum código aqui
}

Se você rodar o código acima ele será executado, e é por isso que existe esse mito de que “não importa a ordem de declaração das coisas” em JavaScript. Mas e se a gente executar isso aqui:

expressionFun()let expressionFun = function () {
// algum código aqui
}

Se você tentou aí, viu que o código gera um TypeError. Afunção que você invocou não existe, logo, você precisa inverter a ordem e invocar a função após ela ser declarada, ou seja, a ordem, nesse caso, importa sim.

Mas por que com a declarationFun() funciona?

A function declaration é carregada sempre antes do código ser interpretado, é como se ela fosse mandada para o topo do código. Por isso, realmente não importa se você invoca antes ou depois da declaração.

Com a function expression ocorre de maneira diferente, ela é carregada durante a interpretação do código, portanto, você deve seguir a ordem de interpretação na hora de invocar funções criadas dessa forma.

Qual a melhor forma? Não existe melhor forma, o importante é saber que existem diferenças entre as duas formas, não apenas na declaração, mas também na hora de invocar as funções. E da próxima vez que alguém chegar com essa de ser tudo bagunçado com funções, você pode dizer “DEPENDE!” 😎.

--

--