Var functionName = function () {} vs função functionName () {}

Recentemente, comecei a oferecer suporte ao código JavaScript de outro usuário. Corrigir erros, adicionar funções e também tentar arrumar o código e torná-lo mais consistente.

O desenvolvedor anterior usa duas maneiras de declarar funções e não consigo descobrir se há uma razão para isso ou não.

Duas maneiras são:

 var functionOne = function() { // Some code }; 
 function functionTwo() { // Some code } 

Quais são as razões para usar esses dois métodos diferentes e quais são os prós e contras de cada um deles? Existe algo que pode ser feito com um método que não pode ser feito com outro?

6296
03 дек. Richard Garside pediu 03 de dezembro 2008-12-03 14:31 '08 at 2:31 pm 2008-12-03 14:31
@ 37 respostas
  • 1
  • 2

A diferença é que functionOne é uma expressão de uma função e, portanto, é determinada somente quando esta linha é alcançada, enquanto functionTwo é uma declaração de função e é determinada assim que a função ou script circundante é executado (devido ao levantamento ).

Por exemplo, a função de expressão:

 // Outputs: "Hello!" functionTwo(); function functionTwo() { console.log("Hello!"); } 

Isso também significa que você não pode definir funções condicionalmente usando declarações de função:

 if (test) { // Error or misbehavior function functionThree() { doSomething(); } } 

O acima, na verdade, define functionThree independentemente do valor do test - a menos que o use strict ação use strict , caso em que simplesmente causa um erro.

4669
03 дек. resposta dada por Greg 03 dez. 2008-12-03 14:37 '08 at 2:37 pm 2008-12-03 14:37

Primeiro eu quero consertar o Greg: a function abc(){} também function abc(){} limitada - o nome abc é definido na área onde esta definição é encontrada. Exemplo:

 function xyz(){ function abc(){}; // abc is defined here... } // ...but not here 

Em segundo lugar, você pode combinar os dois estilos:

 var xyz = function abc(){}; 

xyz será definido como de costume, abc - indefinido em todos os navegadores, mas o Internet Explorer - não depende de sua definição. Mas ele será definido dentro de seu corpo:

 var xyz = function abc(){ // xyz is visible here // abc is visible here } // xyz is visible here // abc is undefined here 

Se você quiser usar pseudônimos em todos os navegadores, use esse tipo de anúncio:

 function abc(){}; var xyz = abc; 

Neste caso, ambos xyz e abc são aliases do mesmo objeto:

 console.log(xyz === abc); // prints "true" 

Uma das razões convincentes para usar o estilo combinado é o atributo "name" para objetos de função ( não suportado pelo Internet Explorer ). Basicamente, quando você define uma função como

 function abc(){}; console.log(abc.name); // prints "abc" 

seu nome é atribuído automaticamente. Mas quando você define como

 var abc = function(){}; console.log(abc.name); // prints "" 

seu nome está vazio - criamos uma função anônima e atribuímos a ela alguma variável.

Outro bom motivo para usar o estilo combinado é usar um nome interno curto para fazer referência a ele, fornecendo um nome longo e sem conflito para usuários externos:

 // Assume really.long.external.scoped is {} really.long.external.scoped.name = function shortcut(n){ // Let it call itself recursively: shortcut(n - 1); // ... // Let it pass itself as a callback: someFunction(shortcut); // ... } 

No exemplo acima, podemos fazer o mesmo com o nome externo, mas será muito pesado (e mais lento).

(Outra maneira de endereçar a si mesmo é usar arguments.callee , que ainda é relativamente longo e não suportado no modo estrito.)

Abaixo, o JavaScript manipula as duas instruções de maneira diferente. Esta é uma declaração de função:

border=0
 function abc(){} 

abc aqui é definido em todos os lugares na área atual:

 // We can call it here abc(); // Works // Yet, it is defined down there. function abc(){} // We can call it again abc(); // Works 

Além disso, ele subiu usando a return :

 // We can call it here abc(); // Works return; function abc(){} 

Esta é uma expressão de função:

 var xyz = function(){}; 

xyz é definido aqui a partir do destino:

 // We can't call it here xyz(); // UNDEFINED!!! // Now it is defined xyz = function(){} // We can call it here xyz(); // works 

A declaração de função e a expressão de função são a verdadeira razão de que existe uma diferença, demonstrada por Greg.

Curiosidade:

 var xyz = function abc(){}; console.log(xyz.name); // Prints "abc" 

Pessoalmente, eu prefiro a declaração "expressão de função", porque desta forma eu posso controlar a visibilidade. Quando eu defino uma função de tipo

 var abc = function(){}; 

Eu sei que defini a função localmente. Quando eu defino uma função de tipo

 abc = function(){}; 

Eu sei que defini globalmente, indicando que não defini abc em nenhuma parte da cadeia de regiões. Esse estilo de definição é estável mesmo quando usado dentro de eval() . Embora a definição

 function abc(){}; 

depende do contexto e pode deixá-lo perguntando onde ele está definido, especialmente no caso de eval() - Resposta: Depende do navegador.

1846
03 дек. A resposta é dada por Eugene Lazutkin 03 dez. 2008-12-03 20:43 '08 at 8:43 pm 2008-12-03 20:43

Aqui está um resumo dos formulários padrão que criam funções: (Originalmente escrito para outra pergunta, mas adaptado após a transição para a questão canônica.)

Termos:

Lista rápida:

  • Declaração de função

  • function "Anônimo" Expressão (que, apesar do termo, às vezes cria funções com nomes)

  • function nomeada Expression

  • Inicializador de Função de Acesso (ES5 +)

  • Expressão de função de seta (ES2015 +) (que, como expressões de função anônima, não contém um nome explícito e pode criar funções com nomes)

  • Declaração de método no inicializador de objetos (ES2015 +)

  • Construtor e declarações de método em class (ES2015 +)

Declaração de função

A primeira forma é uma declaração de função que se parece com isso:

 function x() { console.log('x'); } 

Uma declaração de função é um anúncio; não é uma declaração ou expressão. Então você não o segue com ; (embora seja inofensivo).

Uma declaração de função é processada quando a execução entra no contexto em que aparece antes de executar qualquer código de etapa. A função que cria é atribuída a seu próprio nome ( x no exemplo acima), e esse nome é colocado na área em que a declaração aparece.

Como ele é processado antes de qualquer código de etapa no mesmo contexto, você pode fazer algo assim:

 x(); // Works even though it above the declaration function x() { console.log('x'); } 

Antes do ES2015, a especificação não cobria o que o mecanismo JavaScript deveria fazer se você colocasse uma declaração de função dentro de uma estrutura de controle, por exemplo, try , if , switch , while , etc. Por exemplo:

 if (someCondition) { function foo() { // <===== HERE THERE } // <===== BE DRAGONS } 

E, como eles são processados ​​antes de executar o código passo a passo, é difícil saber o que fazer quando estão na estrutura de gerenciamento.

Embora não tenha sido indicado antes do ES2015, era uma extensão válida para suportar declarações de função em blocos. Infelizmente (e inevitavelmente), diferentes motores fizeram coisas diferentes.

Começando com o ES2015, a especificação diz o que fazer. Na verdade, ele fornece três ações separadas:

  1. Se no modo livre não está no navegador da web, o mecanismo de JavaScript deve fazer uma coisa.
  2. Se estiver no modo livre em um navegador da Web, o mecanismo JavaScript deve fazer outra coisa.
  3. Se no modo estrito (navegador ou não), o mecanismo de JavaScript deve fazer mais uma coisa.

As regras para modos livres são complicadas, mas no modo estrito, as declarações de função nos blocos são simples: elas são locais para o bloco (elas têm o escopo de bloco, que também é novo no ES2015), e vão para o bloco. Então:

 "use strict"; if (someCondition) { foo(); // Works just fine function foo() { } } console.log(typeof foo); // "undefined" ('foo' is not in scope here // because it not in the same block) 

function expressão "anônima"

A segunda forma comum é chamada de expressão de função anônima:

 var y = function () { console.log('y'); }; 

Como todas as expressões, é calculado ao atingir a execução de código passo a passo.

No ES5, a função que está sendo criada não tem nome (é anônimo). No ES2015, uma função recebe um nome sempre que possível, tirando-a do contexto. No exemplo acima, o nome será y . Algo como isso acontece quando a função é o valor do inicializador de propriedade. (Para obter mais informações sobre quando isso acontece e sobre as regras, localize SetFunctionName na especificação - ele aparece em todos os lugares.)

function nomeada Expression

A terceira forma é uma expressão com uma função nomeada ("NFE"):

 var z = function w() { console.log('zw') }; 

A função que cria possui seu próprio nome (nesse caso, w ). Como todas as expressões, isso é avaliado quando é obtido com a execução de código passo a passo. O nome da função não é adicionado à área na qual a expressão aparece; o nome está no escopo da própria função:

 var z = function w() { console.log(typeof w); // "function" }; console.log(typeof w); // "undefined" 

Por favor, note que os NFEs são muitas vezes uma fonte de erros para implementações de JavaScript. Por exemplo, o IE8 e versões anteriores manipulam o NFE completamente errado , criando duas funções diferentes em dois pontos diferentes no tempo. As primeiras versões do Safari também tiveram problemas. A boa notícia é que nas versões atuais dos navegadores (IE9 e superior, o atual Safari), esses problemas não existem mais. (Mas, infelizmente, no momento da redação deste artigo, o IE8 ainda é amplamente usado e, portanto, usar o NFE com código para a Internet como um todo ainda é problemático.)

Inicializador de Função de Acesso (ES5 +)

Às vezes, as funções podem penetrar em grande parte despercebidas; o que acontece com as funções de acesso. 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" 

Por favor note que quando eu usei a função, eu não usei () ! Isto é porque é uma função de acesso para uma propriedade. Obtemos e definimos a propriedade da maneira usual, mas nos bastidores é chamada uma função.

Você também pode criar funções de acesso usando Object.defineProperty , Object.defineProperties e o segundo argumento Object.defineProperties menos conhecido.

Expressão de função de seta (ES2015 +)

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 

Veja o que n => n * 2 está oculto na chamada map() ? Esta é uma função.

Algumas coisas sobre as funções das setas:

  1. Eles não têm this . Em vez disso, eles fecham this contexto no qual eles são definidos. (Eles também estão próximos de arguments e, quando apropriado, super .) Isso significa que this neles exatamente assim, onde eles são criados e não podem ser alterados.

  2. Como você observou acima, você não está usando a function palavra function chave; em vez disso, você usa => .

Exemplo n => n * 2 acima é uma das suas formas. Se você tiver vários argumentos para passar uma 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, Array#map passa o registro como o primeiro argumento e o índice como o segundo.)

Em ambos os 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 return explícito).

Se você fizer mais do que apenas uma expressão, use {} e um return explícito (se precisar retornar um valor), como de costume:

 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)); 

Uma versão sem {... } é chamada de uma função de seta com um corpo de expressão ou corpo curto. (Também: Função breve da seta.) A função com {... } definindo o corpo é a função da seta com o corpo da função. (Também: a função de seta verbal).

Declaração de método no inicializador de objetos (ES2015 +)

O ES2015 permite um formulário de declaração de propriedades mais curto que se refere a uma função chamada definição de método; parece assim:

 var o = { foo() { } }; 

quase equivalente em ES5 e versões anteriores:

 var o = { foo: function foo() { } }; 

A diferença (exceto a verbosidade) é que o método pode usar super , mas a função não pode. Então, por exemplo, se você tivesse um objeto que definisse (digamos) valueOf usando a sintaxe de um método, ele poderia usar super.valueOf() para obter o valor de Object.prototype.valueOf que deve ser retornado (antes de supostamente fazer algo alguma outra coisa com ele), enquanto a versão ES5 teria que Object.prototype.valueOf.call(this) fazer Object.prototype.valueOf.call(this) .

Isso também significa que o método tem uma referência ao objeto para o qual foi definido, portanto, se esse objeto for temporário (por exemplo, você o passar para Object.assign como um dos objetos originais), a sintaxe do método pode significar que ele é salvo memória, caso contrário, ele poderia ser coletado pelo coletor de lixo (se o mecanismo JavaScript não detectar essa situação e não lidar com isso, se nenhum dos métodos usar super ).

Construtor e declarações de método em class (ES2015 +)

O ES2015 nos fornece a sintaxe de class , incluindo os construtores e métodos declarados:

 class Person { constructor(firstName, lastName) { this.firstName = firstName; this.lastName = lastName; } getFullName() { return this.firstName + " " + this.lastName; } } 

Acima estão duas declarações de função: uma para o construtor, que é chamada Person , e getFullName para getFullName , que é uma função atribuída a Person.prototype .

574
04 марта '14 в 16:35 2014-03-04 16:35 Resposta é dada por TJ Crowder 04 de março '14 at 16:35 2014-03-04 16:35

Falando em contexto global, as instruções var e FunctionDeclaration no final criam uma propriedade não deletável para o objeto global, mas o valor de ambos pode ser sobrescrito.

A diferença sutil entre as duas formas é que quando o processo de instanciação variável é iniciado (antes da execução do código), todos os identificadores declarados com var serão inicializados com undefined , e aqueles usados ​​por FunctionDeclaration estarão disponíveis a partir de agora, por exemplo:

  alert(typeof foo); // 'function', it already available alert(typeof bar); // 'undefined' function foo () {} var bar = function () {}; alert(typeof bar); // 'function' 

A atribuição da bar FunctionExpression é feita antes da execução.

A propriedade global criada por FunctionDeclaration pode ser sobrescrita sem problemas da mesma maneira que o valor de uma variável, por exemplo:

  function test () {} test = null; 

Outra diferença óbvia entre seus dois exemplos é que a primeira função não tem um nome, mas o segundo tem, o que pode ser realmente útil ao depurar (por exemplo, verificar a pilha de chamadas).

Sobre o seu primeiro exemplo editado ( foo = function() { alert('hello!'); }; ), Esta é uma tarefa não declarada, eu recomendo fortemente que você sempre use a palavra-chave var .

Quando atribuído sem um operador var , se o identificador de referência não for encontrado na cadeia de escopo, ele se tornará uma propriedade removível do objeto global.

Além disso, tarefas não declaradas lançam um ReferenceError no ECMAScript 5 no modo Strict .

Um deve ler:

Nota : Esta resposta foi combinada a partir de outra questão , em que a principal dúvida e equívoco do OP era que os identificadores declarados com uma FunctionDeclaration não poderiam ser sobrescritos, o que não é o caso.

137
08 авг. a resposta é dada pelo CMS 08 ago. 2010-08-08 22:32 '10 às 10:32 2010-08-08 22:32

Os dois trechos de código que você colocar lá se comportarão da mesma forma para quase todos os propósitos.

No entanto, a diferença de comportamento é que, com a primeira opção ( var functionOne = function() {} ), essa função só pode ser chamada após esse ponto no código.

Na segunda variante ( function functionTwo() ), a função está disponível para o código que é executado acima, onde a função é declarada.

Isso se deve ao fato de que na primeira variante a função é atribuída à variável foo no tempo de execução. Na segunda função, esse identificador é atribuído a foo durante a análise.

Informação técnica adicional

JavaScript tem três maneiras de definir funções.

  • Em seu primeiro fragmento, a expressão de função é mostrada. Isto é devido ao uso do operador "função" para criar uma função - o resultado deste operador pode ser armazenado em qualquer variável ou objeto. A expressão de função é tão poderosa. Uma expressão de função é freqüentemente chamada de "função anônima" porque não deve ter um nome,
  • O segundo exemplo é uma declaração de função. . Para criar uma função, use o operador "function". A função é fornecida durante a análise e pode ser chamada em qualquer lugar nesta área. Você pode salvá-lo posteriormente em uma variável ou objeto.
  • A terceira maneira de definir uma função é o construtor "Function ()" , que não é mostrado na mensagem original. Não é recomendado usá-lo, pois funciona da mesma forma que o eval() , que tem seus próprios problemas.
115
20 апр. resposta foi dada por thomasrutter Apr 20 2010-04-20 07:54 '10 às 7:54 2010-04-20 07:54

Greg responde melhor explicação

 functionTwo(); function functionTwo() { } 

Por que não há bugs? Sempre fomos ensinados que as expressões são executadas de cima para baixo (??)

Porque:

Declarações de função e declarações de variáveis ​​são sempre movidas ( hoisted ) de forma invisível para o topo de sua região usando o interpretador JavaScript. Parâmetros funcionais e nomes de idiomas, obviamente, já existem. ben cereja

Isso significa que esse código:

 functionOne(); --------------- var functionOne; | is actually | functionOne(); var functionOne = function(){ | interpreted |--> }; | like | functionOne = function(){ --------------- }; 

Por favor, note que parte da atribuição de declarações não foi levantada. Apenas o nome é gerado.

Mas no caso de declarações de função, o corpo da função inteira também será gerado:

 functionTwo(); --------------- function functionTwo() { | is actually | }; function functionTwo() { | interpreted |--> } | like | functionTwo(); --------------- 
96
09 авг. a resposta é dada em simple_human 09 ago. 2014-08-09 05:45 '14 às 5:45 am 2014-08-09 05:45

Outros comentaristas já consideraram a diferença semântica entre as duas opções listadas acima. Gostaria de salientar uma diferença estilística: apenas uma variação de "destino" pode estabelecer a propriedade de outro objeto.

Costumo criar módulos JavaScript com esse padrão:

 (function(){ var exports = {}; function privateUtil() { ... } exports.publicUtil = function() { ... }; return exports; })(); 

Usando esse modelo, suas funções públicas usarão o destino, enquanto suas funções privadas usarão o anúncio.

(Observe também que a atribuição deve conter um ponto-e-vírgula após a instrução, enquanto o anúncio proíbe isso.)

87
03 марта '11 в 22:19 2011-03-03 22:19 respondeu a Sean McMillan em 03 de março '11 às 22:19 2011-03-03 22:19

Uma ilustração de quando é melhor usar o primeiro método para o segundo é quando você precisa evitar a substituição das funções das definições anteriores.

Com

 if (condition){ function myfunction(){ // Some code } } 

Esta definição myfunction substituirá qualquer definição anterior, uma vez que será executada durante a análise.

Enquanto

 if (condition){ var myfunction = function (){ // Some code } } 

faz o trabalho correto de definir myfunction somente quando a condition é executada.

73
29 марта '13 в 16:26 2013-03-29 16:26 Resposta dada por Mbengue Assane em 29 de março de 2013 às 16:26 2013-03-29 16:26

Uma razão importante é a adição de uma e apenas uma variável como a "raiz" do seu namespace ...

 var MyNamespace = {} MyNamespace.foo= function() { } 

ou

 var MyNamespace = { foo: function() { }, ... } 

Existem muitos métodos para o namespace. Isso se torna mais importante com os muitos módulos JavaScript disponíveis.

Veja também: Como declarar namespace em javascript?

59
08 авг. a resposta é dada por Rob 08 ago. 2010-08-08 22:44 '10 às 22:44 2010-08-08 22:44

O içamento é uma ação de intérprete de JavaScript para mover todas as declarações de variáveis ​​e funções para o início do escopo atual.

No entanto, apenas as declarações reais são geradas. deixando atribuições onde estão.

  • переменная/функция, объявленная внутри страницы, глобально доступна для доступа в любой точке этой страницы.
  • переменные/функции, объявленные внутри функции, имеют локальную область. означает, что они доступны/доступны внутри тела функции (scope), они недоступны вне тела функции.

Variable

Javascript называется свободно типизированным языком. Это означает, что переменные Javascript могут содержать значение любого Data-Type . Javascript автоматически позаботится об изменении типа переменной на основе значения/литерала, предоставленного во время выполнения.

 global_Page = 10; var global_Page; « undefined « Integer literal, Number Type. ------------------- global_Page = 10; « Number global_Page = 'Yash'; | Interpreted | global_Page = 'Yash'; « String « String literal, String Type. « AS « global_Page = true; « Boolean var global_Page = true; | | global_Page = function (){ « function « Boolean Type ------------------- var local_functionblock; « undefined global_Page = function (){ local_functionblock = 777;« Number var local_functionblock = 777; }; // Assigning function as a data. }; 

Функция

 function Identifier_opt ( FormalParameterList_opt ) { FunctionBody | sequence of statements « return; Default undefined « return 'some data'; } 
  • функции, объявленные внутри страницы, поднимаются на верх страницы, имеющей глобальный доступ.
  • функции, объявленные внутри функционального блока, поднимаются до вершины блока.
  • Возвращаемое значение по умолчанию функции: undefined ', Variable значение по умолчанию объявления также 'undefined'

     Scope with respect to function-block global. Scope with respect to page undefined | not available. 

Объявление функции

 function globalAccess() { function globalAccess() { } ------------------- } globalAccess(); | | function globalAccess() { « Re-Defined / overridden. localAccess(); « Hoisted As « function localAccess() { function globalAccess() { | | } localAccess(); ------------------- localAccess(); « function accessed with in globalAccess() only. function localAccess() { } } globalAccess(); } localAccess(); « ReferenceError as the function is not defined 

Выражение функции

  10; « literal (10); « Expression (10).toString() -> '10' var a; a = 10; « Expression var a.toString() -> '10' (function invoke() { « Expression Function console.log('Self Invoking'); (function () { }); }) () -> 'Self Invoking' var f; f = function (){ « Expression var Function console.log('var Function'); f () -> 'var Function' };