Aqui está o resumo dos formulários padrão que criam funções: (Originalmente escrito para outra pergunta, mas adaptado após ser movido para a pergunta canônica).
Termos:
A lista rápida:
Declaração de Função
function
Expressão "anônima" (que, apesar do termo, às vezes cria funções com nomes)
function
Expressão nomeada
Inicializador da função de acessador (ES5 +)
Expressão de função de seta (ES2015 +) (que, como expressões de função anônimas, não envolve um nome explícito e ainda pode criar funções com nomes)
Declaração de método no Inicializador de Objetos (ES2015 +)
Declarações de construtor e método em class
(ES2015 +)
Declaração de Função
O primeiro formulário é uma declaração de função , com a seguinte aparência:
function x() {
console.log('x');
}
Uma declaração de função é uma declaração ; não é uma declaração ou expressão. Como tal, você não o segue com um ;
(embora isso seja inofensivo).
Uma declaração de função é processada quando a execução entra no contexto em que aparece, antes de qualquer código passo a passo ser executado. A função que ele cria recebe um nome próprio (x
no exemplo acima) e esse nome é colocado no escopo em que a declaração aparece.
Como é processado antes de qualquer código passo a passo no mesmo contexto, você pode fazer coisas como estas:
x(); // Works even though it's above the declaration
function x() {
console.log('x');
}
Até ES2015, a especificação não cobrir o que um motor de JavaScript deve fazer se você colocar uma declaração de função dentro de uma estrutura de controle como try
, if
, switch
, while
, etc., como este:
if (someCondition) {
function foo() { // <===== HERE THERE
} // <===== BE DRAGONS
}
E como eles são processados antes da execução do código passo a passo, é complicado saber o que fazer quando estão em uma estrutura de controle.
Embora isso não tenha sido especificado até o ES2015, era uma extensão permitida para suportar declarações de função em blocos. Infelizmente (e inevitavelmente), motores diferentes fizeram coisas diferentes.
No ES2015, a especificação diz o que fazer. De fato, ele fornece três coisas separadas para fazer:
- Se no modo loose não estiver em um navegador da web, o mecanismo JavaScript deve fazer uma coisa
- Se estiver no modo livre em um navegador da web, o mecanismo JavaScript deve fazer outra coisa
- Se no modo estrito (navegador ou não), o mecanismo JavaScript deve fazer outra coisa
As regras para os modos soltos são complicadas, mas, no modo estrito , as declarações de função nos blocos são fáceis: elas são locais para o bloco (elas têm escopo de bloco , o que também é novo no ES2015) e são levantadas no topo do bloco. Assim:
"use strict";
if (someCondition) {
foo(); // Works just fine
function foo() {
}
}
console.log(typeof foo); // "undefined" (`foo` is not in scope here
// because it's not in the same block)
function
Expressão "anônima"
A segunda forma comum é chamada expressão de função anônima :
var y = function () {
console.log('y');
};
Como todas as expressões, ela é avaliada quando é alcançada na execução passo a passo do código.
No ES5, a função que isso cria não tem nome (é anônimo). No ES2015, é atribuído um nome à função, se possível, deduzindo-o do contexto. No exemplo acima, o nome seria y
. Algo semelhante é feito quando a função é o valor de um inicializador de propriedade. (Para obter detalhes sobre quando isso acontece e sobre as regras, procure SetFunctionName
na especificação - ela aparece por toda parte o lugar.)
function
Expressão nomeada
A terceira forma é uma expressão de função nomeada ("NFE"):
var z = function w() {
console.log('zw')
};
A função que isso cria tem um nome próprio ( w
neste caso). Como todas as expressões, isso é avaliado quando é alcançado na execução passo a passo do código. O nome da função não é adicionado ao escopo em que a expressão aparece; o nome está no escopo dentro da própria função:
var z = function w() {
console.log(typeof w); // "function"
};
console.log(typeof w); // "undefined"
Observe que as NFEs frequentemente são uma fonte de bugs para implementações de JavaScript. O IE8 e versões anteriores, por exemplo, lidam com as NFEs completamente incorretamente , criando duas funções diferentes em dois momentos diferentes. As versões anteriores do Safari também tinham problemas. A boa notícia é que as versões atuais dos navegadores (IE9 e posterior, Safari atual) não têm mais esses problemas. (Mas, até o momento em que este artigo foi escrito, infelizmente, o IE8 permanece em uso generalizado e, portanto, o uso de NFEs com código para a Web em geral ainda é problemático.)
Inicializador da função de acessador (ES5 +)
Às vezes, as funções podem passar despercebidas; esse é o caso das funções de acessador . Aqui está um exemplo:
var obj = {
value: 0,
get f() {
return this.value;
},
set f(v) {
this.value = v;
}
};
console.log(obj.f); // 0
console.log(typeof obj.f); // "number"
Observe que, quando usei a função, não usei ()
! Isso ocorre porque é uma função de acessador para uma propriedade. Obtemos e configuramos a propriedade da maneira normal, mas nos bastidores, a função é chamada.
Você também pode criar acessores funções com Object.defineProperty
, Object.defineProperties
e o segundo argumento menos conhecido para Object.create
.
Expressão da função de seta (ES2015 +)
O ES2015 nos traz a função de seta . Aqui está um exemplo:
var a = [1, 2, 3];
var b = a.map(n => n * 2);
console.log(b.join(", ")); // 2, 4, 6
Vê aquela n => n * 2
coisa escondida na map()
ligação? Essa é uma função.
Algumas coisas sobre as funções de seta:
Eles não têm os seus próprios this
. Em vez disso, eles apertada sobre o this
do contexto onde estão definidos. (Eles também fecham arguments
e, quando relevante super
,.) Isso significa que o this
interior deles é igual ao local this
onde foram criados e não pode ser alterado.
Como você já deve ter notado, você não usa a palavra-chave function
; em vez disso, você usa =>
.
O n => n * 2
exemplo acima é uma forma deles. Se você tiver vários argumentos para passar a função, use parens:
var a = [1, 2, 3];
var b = a.map((n, i) => n * i);
console.log(b.join(", ")); // 0, 2, 6
(Lembre-se de que Array#map
passa a entrada como o primeiro argumento e o índice como o segundo.)
Nos dois casos, o corpo da função é apenas uma expressão; o valor de retorno da função será automaticamente o resultado dessa expressão (você não usa um explícito return
).
Se você estiver fazendo mais do que apenas uma única expressão, use {}
e um explícito return
(se precisar retornar um valor), como normal:
var a = [
{first: "Joe", last: "Bloggs"},
{first: "Albert", last: "Bloggs"},
{first: "Mary", last: "Albright"}
];
a = a.sort((a, b) => {
var rv = a.last.localeCompare(b.last);
if (rv === 0) {
rv = a.first.localeCompare(b.first);
}
return rv;
});
console.log(JSON.stringify(a));
A versão sem { ... }
é chamada de função de seta com um corpo de expressão ou corpo conciso . (Também: uma função de seta concisa .) Aquele que { ... }
define o corpo é uma função de seta com um corpo de função . (Também: uma função de seta detalhada .)
Declaração de método no Inicializador de Objetos (ES2015 +)
O ES2015 permite uma forma mais curta de declarar uma propriedade que faz referência a uma função chamada definição de método ; Se parece com isso:
var o = {
foo() {
}
};
o quase equivalente no ES5 e anterior seria:
var o = {
foo: function foo() {
}
};
a diferença (além da verbosidade) é que um método pode usar super
, mas uma função não. Portanto, por exemplo, se você tivesse um objeto que definisse (digamos) o valueOf
uso da sintaxe do método, ele poderia ser usado super.valueOf()
para obter o valor Object.prototype.valueOf
que retornaria (antes de presumivelmente fazer outra coisa com ele), enquanto a versão do ES5 deveria fazer isso Object.prototype.valueOf.call(this)
.
Isso também significa que o método tem uma referência ao objeto em que foi definido; portanto, se esse objeto é temporário (por exemplo, você o está passando Object.assign
como um dos objetos de origem), a sintaxe do método pode significar que o objeto é retido na memória, caso contrário, poderia ter sido coletado como lixo (se o mecanismo JavaScript não detectar essa situação e tratá-la se nenhum dos métodos o usar super
).
Declarações de construtor e método em class
(ES2015 +)
O ES2015 nos traz class
sintaxe, incluindo construtores e métodos declarados:
class Person {
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
getFullName() {
return this.firstName + " " + this.lastName;
}
}
Há duas declarações de função acima: uma para o construtor, que obtém o nome Person
, e outra para getFullName
, que é uma função atribuída Person.prototype
.