A diferença entre funções tradicionais e arrow functions no javascript

Não se engane, não é só a compactibilidade e sintaxe elegante que diferem as maneiras de se declarar funções no Javascript.

As arrow functions é um recurso relativamente novo implementando no ES6 (ECMAScript 6) que, aos nossos olhos, se trata apenas de uma sintaxe mais concisa e elegante para declarar expressões de função no Javascript. Embora as funções tradicionais e as arrow functions funcionem de maneira semelhante, devemos tomar cuidado com certas diferenças que podem ser imperceptíveis.

Sintaxe

É notória a diferença da sintaxe entre os dois modelos, já que nas arrow function se torna possível reduzir consideravelmente o número de linhas presentes na declaração função, principalmente se já se trata de uma função simples. Veja exemplos:

Imagine uma função que recebe o nome de um usuário e o imprime no console. Na maneira tradicional, poderíamos declará-la da seguinte maneira:

function sayMyName(name){
  console.log(`My name is ${name}!`);
}

sayMyName('Ernane'); // => My name is Ernane.

Já com as arrow functions presentes a partir do ES6, poderíamos fazer da seguinte maneira:

const sayMyName = (name) => {
  console.log(`My name is ${name}`);
}

sayMyName('Ernane'); // => My name is Ernane.

Lembrando que para o caso das arrow functions, as chaves só são necessárias se uma expressão estiver presente, valendo a mesma regra para os parêntesis, já que são necessários apenas se houver mais de um argumento a ser passado. Dessa forma, poderíamos reduzir ainda mais e escrever o exemplo acima da seguinte maneira:

const sayMyName = name => console.log(`My name is ${name}`); 

sayMyName('Ernane'); // => My name is Ernane.

Simples, não é? ✨ dessa forma, podemos ver como uma arrow function pode facilitar a declaração de uma determinada função por causa de sua sintaxe e ainda devolver o mesmo resultado que uma declaração comum.

Utilização da palavra-chave this

Ao contrário da declaração tradicional, as arrow functions não possuem seu próprio this elemento, pois o valor de this dentro de uma arrow function permanece o mesmo dentro de todo o ciclo de vida da função e está sempre vinculado ao valor de this na função pai tradicional mais próxima.

Isso ficou um pouco estranho? 🤔 deixe-me tentar descomplicar com um exemplo:

Retomando o exemplo utilizado no tópico anterior, imagine que possuímos um objeto pessoa que tem seu nome definido como um dos seus atributos e possui uma função que imprime no console o nome dessa determinada pessoa. A depender do tipo de função utilizada, ela não conseguirá acessar corretamente o objeto pai que possui o atributo name procurado e, dessa forma, seu retorno será undefined.

let person = {
  name: "Ernane Ferreira",
  sayMyName: () => console.log(`My name is ${this.name}.`)
};

person.sayMyName(); // => My name is .

Já para o caso da função ser declarada no modelo tradicional, o this funcionará como o esperado e obteremos corretamente o atributo buscado. 🤗

let person = {
  name: "Ernane Ferreira",
  sayMyName: function() {
    console.log(`My name is ${this.name}.`);
  }
};

person.sayMyName(); // => My name is Ernane Ferreira.

Acesso aos argumentos

O objeto arguments é uma variável local disponível dentro de todas as funções e é ela que torna possível a referência dos argumentos de uma função dentro da mesma utilizando o objeto arguments. No entanto, as arrow functions não possuem vinculo com o objeto arguments:

const showArguments = () => console.log(arguments);

showArguments(1, 2, 3) // => ReferenceError: arguments is not defined.

No caso de uma função regular, podemos facilmente acessar uma listagem dos argumentos passados como parâmetro no momento da chamada da função:

function showArguments(){ 
  console.log(arguments); 
}

showArguments(1, 2, 3) // => Arguments(3) [1, 2, 3]

Utilizando-se do operador new

O operador new permite a criação da instância de um tipo de objeto definido pelo usuário ou de um dos tipos de objetos internos que tenham uma função construtora. As funções tradicionais são construtíveis e podem ser chamadas através do operador new. Em contrapartida, as arrow functions são chamáveis e não construtíveis, ou seja, essas funções nunca podem ser utilizadas como funções construtoras e nunca poderão ser invocadas com o operador new.

Portanto, para esse tipo de execução em funções tradicionais, obtemos o seguinte resultado na execução:

function sayMyName(){
  console.log(`My name is ${name}`); 
}

new sayMyName('Ernane'); // => Ernane

Já para as arrow functions:

const sayMyName = () => console.log(`My name is ${name}`);

new sayMyName('Ernane'); // => Uncaught TypeError: sayMyName is not a constructor

Parâmetros com nomeclatura duplicada

As arrow functions não permitem o nome de parâmetros duplicados, mas as funções tradicionais permitem dependendo da aplicação ou não aplicação do modo estrito (Strict Mode) na implementação do código. Por exemplo, o javascript abaixo é totalmente válido:

function addTwoNumbers(x, x){
  console.log(x+x);
}

addTwoNumbers(1,1); // => 2

Entretanto, o mesmo código com o modo estrito aplicado já não é mais válido:

'use strict';

function addTwoNumbers(x, x){
  console.log(x+x);
} 

// => Uncaught SyntaxError: Duplicate parameter name not allowed in this context

Na utilização de arrow functions, isso ocorre independentemente da aplicação do modo estrito. Em ambos os casos, a execução é inválida:

const addTwoNumbers = (x, x) => console.log(x+x); 

// => SyntaxError: Uncaught SyntaxError: Duplicate parameter name not allowed in this context.

Dessa forma, é sempre bom termos bastante atenção quanto a utilização das arrow functions no lugar das funções tradicionais. Apesar de sua sintaxe ser muito agradável, elas possuem alguns pontos que devemos tomar cuidado para não deixar passar.

Em todo caso, é aconselhável um estudo mais aprofundado sobre o assunto. Como sempre, deixarei abaixo alguns links de recomendação para o aprofundamento da temática.

Espero que tenha gostado dessa postagem e que ela tenha te ajudado a encontrar o que você procurava! 💙

Links

Muito bacana o post, bem completo Eu só sabia sobre a relação com o objeto this, bom saber sobre a relação com os argumentos

Abraço

Fico feliz que tenha achado o post completo e que tenha aprendido algo novo com ele. Significa que meu objetivo aqui foi alcançado. Obrigado! É sempre interessante descobrir diferentes aspectos e formas de se trabalhar dentro de uma linguagem. Abraço!

Muito bom, é importante apontar essas diferenças, pois tenho visto gente que usa sempre uma delas porque acha que é a única forma (ou "a melhor").

Sendo que o correto é avaliar caso a caso e usar um ou outro de acordo com o que precisa.

Exatamente! Eu mesmo sempre preferi as arrow functions devido sua praticidade de criação. Mas, em um certo dia me surgiu esse questionamento. Então, houve a necessidade do estudo que deu origem a esse post. É realmente crucial reconhecer essas diferenças para evitar a mentalidade de que uma forma é a única ou a melhor opção. Cada situação deve ser avaliada individualmente, escolhendo a abordagem mais adequada de acordo com as necessidades específicas. A flexibilidade e a adaptação são essenciais para obter os melhores resultados e evitar problemas.

qual a vantagem da arrow function entao?

Várias dessas já são vantagens por evitar más práticas. A existência de parâmetros com nomes duplicados é algo que obviamente não deveria ocorrer, tanto que não é permitido no modo estrito. O uso de "this" é algo que você deve evitar, pois torna o código mais imprevisível e menos legível. No exemplo mencionado no post, é melhor simplesmente fazer: ```javascript let person = { name: "Ernane Ferreira", sayMyName: () => console.log(My name is ${person.name}.) }; person.sayMyName(); // => My name is Ernane Ferreira. ``` Outra diferença que não foi mencionada no post e deve ser evitada é o hoisting. Funções clássicas podem ser utilizadas antes de serem declaradas, o que pode parecer bom inicialmente, mas torna o código menos legível e mais imprevisível, uma vez que o JavaScript é uma linguagem scriptada que é executada de cima para baixo. Outra diferença não mencionada que deve ser evitada é a sobrecarga de funções (function overloading). Funções clássicas podem ser sobrescritas simplesmente declarando uma nova função com o mesmo nome. Não é preciso dizer que isso também torna o código mais imprevisível e menos legível. Quanto ao restante das diferenças, acredito que sejam insignificantes. Por fim, também acredito que usar apenas arrow functions torna o código mais coeso, uma vez que as arrow functions são algo que você inevitavelmente usará em callbacks eventualmente.
Sendo bem sincero as vantagens que você listou, na minha opinião não compensão a pior legibilidade do código, talvez por eu ser acostumado com linguagens compiladas, mas para mim arrow functions são extremamente situacionais e deveriam ser usadas em contextos muito especificos e não como se fossem funções comuns, alem de impedirem o reaproveitamento de código etc.

nunca na minha vida que eu iria suspeitar que javascript permitira repetir o parametro na função tradicional. 😬

Só um toque para n confundir. função tradicional no modo não estrito suporta várivaveis com o mesmo nome. Mas é uma coincidência do exemplo citado: ``` function addTwoNumbers(x, x){ console.log(x+x); } addTwoNumbers(1,1); // => 2 ``` Neste exemplo o segundo x dos argumentos sobreescreve o x do primeiro argumento. A chamada addTwoNumbers(1,1) o resultado é 2 pois se está somando o 2 argumento com ele próprio. Se a chamada for addTwoNumbers(1,5) o resultado será 10. Utilizar o modo não estrito é uma armadilha! rsrrsrsrs