var functionName = function () {} vs function functionName () {}

6874

Recentemente, comecei a manter o código JavaScript de outra pessoa. Estou corrigindo bugs, adicionando recursos e também tentando arrumar o código e torná-lo mais consistente.

O desenvolvedor anterior usou duas maneiras de declarar funções e não consigo descobrir se há uma razão por trás disso ou não.

As 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 os contras de cada um? Existe algo que possa ser feito com um método que não possa ser feito com o outro?

Richard Garside
fonte
198
permadi.com/tutorial/jsFunc/index.html é muito boa página sobre funções javascript
uzay95
67
Relacionado é este excelente artigo sobre Expressões de funções nomeadas .
Phrogz
12
O @CMS faz referência a este artigo: kangax.github.com/nfe/#expr-vs-decl
Upperstage
106
Você precisa estar ciente de duas coisas: # 1 No JavaScript, as declarações são hasteadas. Significado que var a = 1; var b = 2;se torna var a; var b; a = 1; b = 2. Portanto, quando você declara functionOne, ele é declarado, mas seu valor não é definido imediatamente. Considerando que functionTwo é apenas uma declaração, ela é colocada no topo do escopo. # 2 functionTwo permite acessar a propriedade name e isso ajuda muito ao tentar depurar alguma coisa.
Xmlm02
65
Ah, e btw, a sintaxe correta está com um ";" após a atribuição e sem após a declaração. Por exemplo, function f(){}vs var f = function(){};.
Xmlm02

Respostas:

5045

A diferença é que functionOneé uma expressão de função e, portanto, é definida apenas quando essa linha é atingida, enquanto que functionTwoé uma declaração de função e é definida assim que sua função ou script circundante é executado (devido à elevação ).

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

// TypeError: functionOne is not a function
functionOne();

var functionOne = function() {
  console.log("Hello!");
};

E, uma declaração de função:

// Outputs: "Hello!"
functionTwo();

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

Historicamente, as declarações de funções definidas nos blocos eram tratadas de maneira inconsistente entre os navegadores. O modo estrito (introduzido no ES5) resolveu isso definindo o escopo das declarações de função para seu bloco anexo.

'use strict';    
{ // note this block!
  function functionThree() {
    console.log("Hello!");
  }
}
functionThree(); // ReferenceError

Greg
fonte
632
@ Greg: A propósito, a diferença não é apenas o fato de serem analisados ​​em momentos diferentes. Essencialmente, você functionOneé apenas uma variável que possui uma função anônima atribuída, enquanto functionTwona verdade é uma função nomeada. Chame os .toString()dois para ver a diferença. Isso é significativo em alguns casos em que você deseja obter o nome de uma função programaticamente.
Jason Bunting
6
@ Jason Bunting .. não sei o que você está chegando aqui, .toString () parece retornar essencialmente o mesmo valor (a definição da função) para ambos: cl.ly/2a2C2Y1r0J451o0q0B1B
Jon z
124
Existem dois diferentes. O primeiro é um function expressiono segundo é um function declaration. Você pode ler mais sobre o assunto aqui: javascriptweblog.wordpress.com/2010/07/06/…
Michal Kuklis 14/11/11
127
@ Greg A parte da sua resposta sobre o tempo de análise versus o tempo de execução não está correta. No JavaScript, as declarações de função não são definidas durante o tempo de análise, mas durante o tempo de execução. O processo é assim: O código-fonte é analisado -> o programa JavaScript é avaliado -> O contexto de execução global é inicializado -> a instanciação de ligação da declaração é executada. Durante esse processo, as declarações da função são instanciadas (consulte a etapa 5 do capítulo 10.5 ).
Šime Vidas
102
A terminologia para esse fenômeno é conhecida como içamento.
Colin Pear
1942

Primeiro, quero corrigir Greg: também function abc(){}tem escopo definido - o nome abcé definido no escopo em que essa definição é encontrada. Exemplo:

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

Em segundo lugar, é possível combinar os dois estilos:

var xyz = function abc(){};

xyzserá definido como de costume, abcé indefinido em todos os navegadores, exceto no Internet Explorer - não confie na definição. Mas 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ê deseja aliasar funções em todos os navegadores, use este tipo de declaração:

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

Nesse caso, ambos xyze abcsão aliases do mesmo objeto:

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

Um motivo convincente para usar o estilo combinado é o atributo "name" dos objetos de função ( não suportados 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 a atribuímos a alguma variável.

Outro bom motivo para usar o estilo combinado é usar um nome interno curto para se referir a si mesmo, fornecendo um nome longo e não conflitante 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 um nome externo, mas será muito difícil (e mais lento).

(Outra maneira de se referir a si mesma é usar arguments.callee, que ainda é relativamente longo e não é suportado no modo estrito.)

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

function abc(){}

abc aqui é definido em qualquer lugar no escopo 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 foi içado através de uma returndeclaração:

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

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

var xyz = function(){};

xyz aqui é definido a partir do ponto de atribuição:

// We can't call it here
xyz(); // UNDEFINED!!!

// Now it is defined
xyz = function(){}

// We can call it here
xyz(); // works

Declaração de função vs. expressão de função é a verdadeira razão pela qual há uma diferença demonstrada por Greg.

Fato engraçado:

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

Pessoalmente, prefiro a declaração "expressão da função" porque dessa forma posso controlar a visibilidade. Quando eu defino a função como

var abc = function(){};

Eu sei que defini a função localmente. Quando eu defino a função como

abc = function(){};

Sei que o defini globalmente, desde que não tenha definido abcem nenhum lugar da cadeia de escopos. Esse estilo de definição é resistente, mesmo quando usado dentro eval(). Enquanto a definição

function abc(){};

depende do contexto e pode deixá-lo adivinhando onde ele está realmente definido, especialmente no caso de eval()- a resposta é: depende do navegador.

Eugene Lazutkin
fonte
69
Refiro-me a RoBorg, mas ele não está em lugar nenhum. Simples: RoBorg === Greg. É assim que a história pode ser reescrita na era da internet. ;-)
Eugene Lazutkin
10
var xyz = função abc () {}; console.log (xyz === abc); Todos os navegadores que testei (Safari 4, Firefox 3.5.5, Opera 10.10) fornecem "Variável indefinida: abc".
NVI
7
No geral, acho que este post explica bem as diferenças e as vantagens de utilizar a declaração de função. Concordo em discordar quanto aos benefícios da utilização de atribuições de expressão de função a uma variável, especialmente porque o "benefício" parece ser uma promoção da declaração de uma entidade global ... e todo mundo sabe que você não deve desorganizar o espaço para nome global , certo? ;-)
natlee75
83
Um grande motivo para usar a função nomeada é porque os depuradores podem usar o nome para ajudá-lo a entender a pilha de chamadas ou o rastreamento de pilha. é chato quando você olha para a pilha de chamadas e ver "função anônima" 10 níveis profundos ...
cabra
3
var abc = function(){}; console.log(abc.name);não produz ""mais, mas "abc"sim.
Qwerty 04/04
632

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

  • functionExpressão "anônima" (que, apesar do termo, às vezes cria funções com nomes)

  • functionExpressã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:

  1. Se no modo loose não estiver em um navegador da web, o mecanismo 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 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)

functionExpressã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 SetFunctionNamena especificação  - ela aparece por toda parte o lugar.)

functionExpressã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 ( wneste 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.definePropertiese 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 * 2coisa escondida na map()ligação? Essa é uma função.

Algumas coisas sobre as funções de seta:

  1. Eles não têm os seus próprios this. Em vez disso, eles apertada sobre o thisdo contexto onde estão definidos. (Eles também fecham argumentse, quando relevante super,.) Isso significa que o thisinterior deles é igual ao local thisonde foram criados e não pode ser alterado.

  2. Como você já deve ter notado, você não usa a palavra-chave function; em vez disso, você usa =>.

O n => n * 2exemplo 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#mappassa 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 valueOfuso da sintaxe do método, ele poderia ser usado super.valueOf()para obter o valor Object.prototype.valueOfque 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.assigncomo 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 classsintaxe, 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.

TJ Crowder
fonte
3
então o nome wé simplesmente ignorado?
BiAiB
8
@PellePenna: Os nomes das funções são úteis para muitas coisas. Os dois figurões, na minha opinião, são recursão e o nome da função sendo mostrado em pilhas de chamadas, rastreamentos de exceção e outros.
TJ Crowder
4
@ChaimEliyah - "Aceitar não significa que é a melhor resposta, apenas significa que funcionou para a pessoa que pediu." fonte
ScrapCode
6
@AR: É verdade. Divertidamente, porém, logo acima, ele diz "As melhores respostas aparecem primeiro para que sejam sempre fáceis de encontrar". Como a resposta aceita aparece primeiro mesmo nas respostas com maior número de votos, o passeio pode ser um pouco contraditório. ;-) Também um pouco impreciso, se determinarmos "melhor" por votos (o que não é confiável, é exatamente o que temos), as respostas "melhores" só aparecerão primeiro se você estiver usando a guia "Votos" - caso contrário, as respostas primeiro são as ativas ou as mais antigas.
TJ Crowder
1
@TJCrowder: De acordo. 'organizado por data' às vezes é irritante.
ScrapCode
144

Falando sobre o contexto global, tanto a varinstrução quanto a FunctionDeclarationno final criarão uma propriedade não excluível no objeto global, mas o valor de ambos pode ser substituído .

A diferença sutil entre as duas maneiras é que, quando o processo de instanciação variável for executado (antes da execução real do código), todos os identificadores declarados com varserão inicializados com undefinede os usados ​​pelos FunctionDeclarationestarão disponíveis a partir desse momento, por exemplo:

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

A atribuição do bar FunctionExpressionocorre até o tempo de execução.

Uma propriedade global criada por a FunctionDeclarationpode ser substituída sem problemas, assim como um valor 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 a segunda possui, o que pode ser realmente útil na depuração (por exemplo, inspecionando uma pilha de chamadas).

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

Com uma atribuição, sem a varinstrução, se o identificador referenciado não for encontrado na cadeia de escopo, ele se tornará uma propriedade deletável do objeto global.

Além disso, atribuições não declaradas lançam um ReferenceErrorno ECMAScript 5 no Modo Estrito .

Uma leitura obrigatória:

Nota : Esta resposta foi mesclada de outra pergunta , na qual a principal dúvida e o equívoco do OP eram que os identificadores declarados com a FunctionDeclarationnão podiam ser substituídos, o que não é o caso.

CMS
fonte
Eu não sabia que as funções poderiam ser substituídas em JavaScript! Além disso, essa ordem de análise é o grande ponto de venda para mim. Acho que preciso observar como crio funções.
Xeoncross
2
+0 no artigo "Expressões da função de nomes desmistificados", pois está 404ing. Espelho possível ?: kangax.github.com/nfe
Mr_Chimp
@CMS Nice. Tenha em mente que eu nunca vi o original, então não sei se isso é um espelho ou apenas outro artigo com o mesmo título!
Mr_Chimp
@Mr_Chimp Tenho certeza de que a thewaybackmachine está dizendo que recebeu um 302 no momento do rastreamento e o redirecionamento foi para o link que você forneceu.
John
124

Os dois trechos de código publicados por você, para quase todos os fins, se comportam da mesma maneira.

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

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

Isso ocorre porque, com a primeira variante, a função é atribuída à variável foono tempo de execução. No segundo, a função é atribuída a esse identificador foo,, no momento da análise.

Mais informações técnicas

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

  1. Seu primeiro trecho mostra uma expressão de função . Isso envolve o uso do operador "function" para criar uma função - o resultado desse operador pode ser armazenado em qualquer propriedade de variável ou objeto. A expressão da função é poderosa dessa maneira. A expressão da função é freqüentemente chamada de "função anônima", porque não precisa ter um nome,
  2. Seu segundo exemplo é uma declaração de função . Isso usa a instrução "function" para criar uma função. A função é disponibilizada no momento da análise e pode ser chamada em qualquer lugar desse escopo. Você ainda pode armazená-lo em uma variável ou propriedade de objeto posteriormente.
  3. A terceira maneira de definir uma função é o construtor "Function ()" , que não é mostrado na sua postagem original. Não é recomendável usar isso, pois funciona da mesma maneira que eval(), que tem seus problemas.
thomasrutter
fonte
103

Uma explicação melhor para a resposta de Greg

functionTwo();
function functionTwo() {
}

Por que nenhum erro? Sempre fomos ensinados que expressões são executadas de cima para baixo (??)

Porque:

Declarações de função e variáveis ​​são sempre movidas ( hoisted) invisivelmente para o topo do escopo que o contém pelo interpretador JavaScript. Os parâmetros de função e os nomes definidos pelo idioma já estão obviamente disponíveis. Ben Cherry

Isso significa que um código como este:

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

Observe que a parte da atribuição das declarações não foi içada. Somente o nome é içado.

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

functionTwo();              ---------------      function functionTwo() {
                            | is actually |      };
function functionTwo() {    | interpreted |-->
}                           |    like     |      functionTwo();
                            ---------------
suhailvs
fonte
Olá, obrigado por informações claras sobre o tópico da função. Agora, minha pergunta é qual será a primeira declaração na hierarquia de declarações, seja declaração de variável (functionOne) ou declaração de função (functionTwo)?
Sharathi RB 02/02
91

Outros comentaristas já abordaram a diferença semântica das duas variantes acima. Eu queria observar uma diferença estilística: apenas a variação da "atribuição" pode definir uma propriedade de outro objeto.

Costumo construir módulos JavaScript com um padrão como este:

(function(){
    var exports = {};

    function privateUtil() {
            ...
    }

    exports.publicUtil = function() {
            ...
    };

    return exports;
})();

Com esse padrão, todas as suas funções públicas usarão a atribuição, enquanto as suas funções privadas usarão a declaração.

(Observe também que a atribuição deve exigir um ponto-e-vírgula após a declaração, enquanto a declaração a proíbe.)

Sean McMillan
fonte
4
yuiblog.com/blog/2007/06/12/module-pattern é a referência primordial para o padrão do módulo, até onde eu sei. (Apesar de que o artigo utiliza a var foo = function(){...}sintaxe mesmo para variáveis privadas.
Sean McMillan
Isso não é inteiramente verdade em algumas versões mais antigas do IE, na verdade. ( function window.onload() {}foi uma coisa.)
Ry-
77

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

Com

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

, essa definição myfunctionsubstituirá qualquer definição anterior, pois será feita no momento da análise.

Enquanto

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

faz o trabalho correto de definir myfunctionapenas quando conditioné atendido.

Mbengue Assane
fonte
1
este exemplo é bom e está quase perfeito, mas poderia ser melhorado. o melhor exemplo seria definido var myFunc = null;fora de um loop ou fora de um bloco if / elseif / else. Em seguida, você pode atribuir funções diferentes à mesma variável condicionalmente. Em JS, é uma convenção melhor atribuir um valor ausente a nulo e depois a indefinido. Portanto, você deve declarar myFunction como nulo primeiro e depois atribuí-lo mais tarde, condicionalmente.
Alexander Mills
62

Um motivo importante é adicionar uma e apenas uma variável como a "Raiz" do seu espaço para nome ...

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

}

ou

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

Existem muitas técnicas para namespacing. Tornou-se mais importante com a infinidade de módulos JavaScript disponíveis.

Consulte também Como declaro um espaço para nome em JavaScript?

Roubar
fonte
3
Parece que esta resposta foi mesclada a essa pergunta a partir de outra pergunta, e o texto pode parecer um pouco pouco relacionado a essa pergunta. Você consideraria editar a resposta para que ela pareça mais direcionada especificamente a esta pergunta? (para reiterar; isso não é sua culpa ... apenas um efeito colateral de uma pergunta mesclada). Você também pode excluí-lo e acho que manteria sua reputação. Ou você pode deixar; como é antigo, pode não fazer uma grande diferença.
Andrew Barber
55

A elevação é a ação do intérprete JavaScript de mover todas as declarações de variáveis ​​e funções para o topo do escopo atual.

No entanto, apenas as declarações reais são içadas. deixando as tarefas onde estão.

  • As variáveis ​​/ funções declaradas dentro da página são globais e podem acessar em qualquer lugar nessa página.
  • As variáveis ​​/ funções declaradas dentro da função estão tendo escopo local. significa que eles estão disponíveis / acessados ​​dentro do corpo da função (escopo), não estão disponíveis fora do corpo da função.

Variável

Javascript é chamado de linguagem de tipo livre. O que significa que as variáveis ​​Javascript podem conter o valor de qualquer Tipo de dados . O Javascript cuida automaticamente da alteração do tipo de variável com base no valor / literal fornecido durante o tempo de execução.

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.
};  

Função

function Identifier_opt ( FormalParameterList_opt ) { 
      FunctionBody | sequence of statements

      « return;  Default undefined
      « return 'some data';
}
  • funções declaradas dentro da página são içadas para o topo da página com acesso global.
  • funções declaradas dentro do bloco de função são hasteadas na parte superior do bloco.
  • O valor de retorno padrão da função é ' indefinido ', o valor padrão da declaração variável também 'indefinido'

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

Declaração de Função

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

Função Expressão

        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'
    };

Função atribuída à variável Exemplo:

(function selfExecuting(){
    console.log('IIFE - Immediately-Invoked Function Expression');
}());

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

var namedExpression = function for_InternalUSE(fact){
    if(fact === 1){
        return 1;
    }

    var localExpression = function(){
        console.log('Local to the parent Function Scope');
    };
    globalExpression = function(){ 
        console.log('creates a new global variable, then assigned this function.');
    };

    //return; //undefined.
    return fact * for_InternalUSE( fact - 1);   
};

namedExpression();
globalExpression();

javascript interpretado como

var anonymous;
var namedExpression;
var globalExpression;

anonymous = function (){
    console.log('anonymous function Expression');
};

namedExpression = function for_InternalUSE(fact){
    var localExpression;

    if(fact === 1){
        return 1;
    }
    localExpression = function(){
        console.log('Local to the parent Function Scope');
    };
    globalExpression = function(){ 
        console.log('creates a new global variable, then assigned this function.');
    };

    return fact * for_InternalUSE( fact - 1);    // DEFAULT UNDEFINED.
};

namedExpression(10);
globalExpression();

Você pode verificar a declaração de função, teste de expressão em diferentes navegadores usando jsperf Test Runner


Classes de função do construtor ES5 : objetos de função criados usando Function.prototype.bind

O JavaScript trata as funções como objetos de primeira classe; portanto, sendo um objeto, você pode atribuir propriedades a uma função.

function Shape(id) { // Function Declaration
    this.id = id;
};
    // Adding a prototyped method to a function.
    Shape.prototype.getID = function () {
        return this.id;
    };
    Shape.prototype.setID = function ( id ) {
        this.id = id;
    };

var expFn = Shape; // Function Expression

var funObj = new Shape( ); // Function Object
funObj.hasOwnProperty('prototype'); // false
funObj.setID( 10 );
console.log( funObj.getID() ); // 10

O ES6 introduziu a função Seta : uma expressão de função seta possui uma sintaxe mais curta, é mais adequada para funções que não são de método e não pode ser usada como construtora.

ArrowFunction : ArrowParameters => ConciseBody.

const fn = (item) => { return item & 1 ? 'Odd' : 'Even'; };
console.log( fn(2) ); // Even
console.log( fn(3) ); // Odd
Yash
fonte
3
ahm, sua resposta ... não é ambígua? bem escrito, portanto, +1 para gastar e escrever muitas informações.
dinamarquês
40

Estou adicionando minha própria resposta apenas porque todo mundo cobriu a peça de elevação completamente.

Eu me pergunto sobre qual caminho é melhor há muito tempo e agora, graças a http://jsperf.com, eu sei :)

insira a descrição da imagem aqui

As declarações de função são mais rápidas, e é isso que realmente importa no desenvolvedor da Web, certo? ;)

Leon Gaban
fonte
8
Eu diria que a manutenção é o aspecto mais importante da maioria dos códigos. O desempenho é importante, mas, na maioria dos casos, é provável que o IO seja um gargalo maior que o modo como você define suas funções. No entanto, existem alguns problemas nos quais você precisa de todo o desempenho possível e isso é útil nesses casos. Também é bom ter uma resposta aqui que responda claramente uma parte bem definida da pergunta.
Richard Garside
3
Bem, achei o contrário com o Firefox. jsperf.com/sandytest
Sandeep Nayak
Apenas uma atualização, desde que eu adotei o estilo de programação funcional completo em JavaScript agora, nunca uso declarações, apenas expressões de função para poder encadear e chamar minhas funções pelos nomes de variáveis. Dê uma olhada no RamdaJS ...
Leon Gaban
1
@SandeepNayak Acabei de executar seu próprio teste no Firefox 50.0.0 / Windows 7 0.0.0, e na verdade é da mesma maneira que o de Leon. Portanto, se o seu teste estiver correto, eu concluiria que os testes do jsperf não são indicativos e tudo depende do navegador e / ou da versão do sistema operacional ou do estado específico da máquina atual naquele momento.
Ocramot
33

Uma declaração de função e uma expressão de função atribuída a uma variável se comportam da mesma maneira quando a ligação é estabelecida.

Porém, há uma diferença de como e quando o objeto de função está realmente associado à sua variável. Essa diferença se deve ao mecanismo chamado de elevação variável no JavaScript.

Basicamente, todas as declarações de funções e variáveis ​​são colocadas na parte superior da função em que a declaração ocorre (é por isso que dizemos que o JavaScript tem escopo de função ).

  • Quando uma declaração de função é içada, o corpo da função "segue"; assim, quando o corpo da função é avaliado, a variável será imediatamente ligada a um objeto de função.

  • Quando uma declaração de variável é hasteada, a inicialização não segue, mas é "deixada para trás". A variável é inicializada para undefinedno início do corpo da função, e será atribuído um valor em sua posição original no código. (Na verdade, será atribuído um valor em todos os locais em que ocorre uma declaração de uma variável com o mesmo nome.)

A ordem de elevação também é importante: as declarações de função têm precedência sobre as declarações de variável com o mesmo nome e a última declaração de função tem precedência sobre as declarações de função anteriores com o mesmo nome.

Alguns exemplos...

var foo = 1;
function bar() {
  if (!foo) {
    var foo = 10 }
  return foo; }
bar() // 10

A variável fooé içada na parte superior da função, inicializada para undefined, de modo que !fooé true, portanto, fooé atribuída 10. A parte fooexterna do barescopo não desempenha nenhum papel e é intocada.

function f() {
  return a; 
  function a() {return 1}; 
  var a = 4;
  function a() {return 2}}
f()() // 2

function f() {
  return a;
  var a = 4;
  function a() {return 1};
  function a() {return 2}}
f()() // 2

As declarações de função têm precedência sobre as declarações de variável e a última declaração de função "adere".

function f() {
  var a = 4;
  function a() {return 1}; 
  function a() {return 2}; 
  return a; }
f() // 4

Neste exemplo, aé inicializado com o objeto de função resultante da avaliação da segunda declaração de função e, em seguida, é atribuído 4.

var a = 1;
function b() {
  a = 10;
  return;
  function a() {}}
b();
a // 1

Aqui, a declaração da função é içada primeiro, declarando e inicializando a variável a. Em seguida, essa variável é atribuída 10. Em outras palavras: a atribuição não atribui à variável externa a.

eljenso
fonte
3
Você tem uma maneira um pouco estranha de colocar as chaves de fechamento. Você é um codificador Python? Parece que você tenta fazer o Javascript parecer Python. Receio que seja confuso para outras pessoas. Se eu tivesse que manter seu código JavaScript, eu o deixaria passar por uma impressora bonita automática primeiro.
Nalply 14/05
1
Excelente post. Uma 'função de auto-execução' ou 'expressão da função imediatamente chamada' deve ser fácil de ver e sua preferência de estilo não deve prejudicar seu post - o que é preciso e resume 'içar' perfeitamente. +1
Ricalsin
32

O primeiro exemplo é uma declaração de função:

function abc(){}

O segundo exemplo é uma expressão de função:

var abc = function() {};

A principal diferença é como eles são içados (levantados e declarados). No primeiro exemplo, toda a declaração da função é içada. No segundo exemplo, apenas o var 'abc' é içado, seu valor (a função) será indefinido e a própria função permanece na posição em que é declarada.

Simplificando:

//this will work
abc(param);
function abc(){}

//this would fail
abc(param);
var abc = function() {}

Para estudar mais sobre este tópico, recomendo vivamente este link

sla55er
fonte
1
Seu exemplo é o mesmo que a resposta principal
GôTô
O principal motivo para postar esta resposta foi fornecer o link na parte inferior. Esta foi a peça que faltava para eu entender completamente a pergunta acima.
sla55er
1
É muito legal que você queira compartilhar o link. Mas os links para informações adicionais, no SO, devem ser apenas um comentário sobre a pergunta ou sua resposta favorita. É muito ruim otimizar uma página longa e complicada como essa com informações repetidas apenas para adicionar um único link útil ao final dela. Não, você não receberá pontos de representante por fornecer o link, mas estará ajudando a comunidade.
XML
31

Em termos de custo de manutenção de código, as funções nomeadas são mais preferíveis:

  • Independente do local onde são declarados (mas ainda limitados pelo escopo).
  • Mais resistente a erros como a inicialização condicional (você ainda pode substituir, se quiser).
  • O código se torna mais legível alocando funções locais separadamente da funcionalidade do escopo. Normalmente, no escopo, a funcionalidade vai primeiro, seguida pelas declarações das funções locais.
  • Em um depurador, você verá claramente o nome da função na pilha de chamadas, em vez de uma função "anônima / avaliada".

Suspeito que mais PROS para funções nomeadas sejam seguidos. E o que está listado como uma vantagem das funções nomeadas é uma desvantagem para as funções anônimas.

Historicamente, funções anônimas apareciam pela incapacidade do JavaScript como idioma para listar membros com funções nomeadas:

{
    member:function() { /* How do I make "this.member" a named function? */
    }
}
Sasha Firsov
fonte
2
Há o teste para confirmar: blog.firsov.net/2010/01/… JS performance test - escopo e funções nomeadas - Analytics
Sasha Firsov
25

Em termos de ciência da computação, falamos sobre funções anônimas e funções nomeadas. Eu acho que a diferença mais importante é que uma função anônima não está vinculada a um nome, daí o nome da função anônima. Em JavaScript, é um objeto de primeira classe declarado dinamicamente em tempo de execução.

Para mais informações sobre funções anônimas e cálculo lambda, a Wikipedia é um bom começo ( http://en.wikipedia.org/wiki/Anonymous_function ).

Kafka
fonte
No ES2015 (seis anos e meio após a publicação da sua resposta), as duas funções da pergunta são nomeadas.
TJ Crowder
25

Uso a abordagem variável no meu código por um motivo muito específico, cuja teoria foi abordada de uma maneira abstrata acima, mas um exemplo pode ajudar algumas pessoas como eu, com conhecimentos limitados de JavaScript.

Eu tenho um código que preciso executar com 160 marcas projetadas de forma independente. A maior parte do código está em arquivos compartilhados, mas os itens específicos da marca estão em um arquivo separado, um para cada marca.

Algumas marcas exigem funções específicas e outras não. Às vezes, tenho que adicionar novas funções para fazer coisas específicas da marca. É um prazer alterar o código compartilhado, mas não quero alterar todos os 160 conjuntos de arquivos de marca.

Usando a sintaxe da variável, posso declarar a variável (um ponteiro de função essencialmente) no código compartilhado e atribuir uma função de stub trivial ou definir como nulo.

As uma ou duas marcas que precisam de uma implementação específica da função podem definir sua versão da função e atribuí-la à variável, se quiserem, e o restante não faz nada. Posso testar uma função nula antes de executá-la no código compartilhado.

Pelos comentários das pessoas acima, concluo que também é possível redefinir uma função estática, mas acho que a solução variável é boa e clara.

Herc
fonte
25

A resposta de Greg é boa o suficiente, mas eu ainda gostaria de acrescentar algo que aprendi agora mesmo assistindo aos vídeos de Douglas Crockford .

Expressão da função:

var foo = function foo() {};

Instrução de função:

function foo() {};

A declaração de função é apenas uma abreviação de vardeclaração com umfunction valor.

assim

function foo() {};

expande para

var foo = function foo() {};

O que se expande ainda mais para:

var foo = undefined;
foo = function foo() {};

E ambos são elevados ao topo do código.

Captura de tela do vídeo

Rohan
fonte
7
Desculpe, mas isso está incorreto - não sei o que Crockford está tentando dizer nesse slide. As declarações de função e variável são sempre içadas no topo de seu escopo. A diferença é que as atribuições de variáveis ​​(independentemente de você estar atribuindo-a com uma string, booleano ou função) não são içadas para o topo, enquanto os corpos das funções (usando declaração de função) são.
Thomas Heymann
Dê uma olhada nestes exemplos de código: gist.github.com/cyberthom/36603fbc20de8e04fd09
Thomas Heymann
24

Há quatro comparações dignas de nota entre as duas declarações diferentes de funções, conforme listado abaixo.

  1. Disponibilidade (escopo) da função

O seguinte funciona porque function add()está com escopo definido para o bloco mais próximo:

try {
  console.log("Success: ", add(1, 1));
} catch(e) {
  console.log("ERROR: " + e);
}

function add(a, b){
  return a + b;
}

O seguinte não funciona porque a variável é chamada antes que um valor da função seja atribuído à variável add.

try {
  console.log("Success: ", add(1, 1));
} catch(e) {
  console.log("ERROR: " + e);
}

var add=function(a, b){
  return a + b;
}

O código acima é idêntico em funcionalidade ao código abaixo. Observe que atribuir explicitamente add = undefinedé supérfluo porque simplesmente fazer var add;é exatamente o mesmo que var add=undefined.

var add = undefined;

try {
  console.log("Success: ", add(1, 1));
} catch(e) {
  console.log("ERROR: " + e);
}

add = function(a, b){
  return a + b;
}

O seguinte não funciona porque var add=substitui o function add().

try {
  console.log("Success: ", add(1, 1));
} catch(e) {
  console.log("ERROR: " + e);
}

var add=function add(a, b){
  return a + b;
}

  1. (função) .name

O nome de uma função function thefuncname(){}é thefuncname quando é declarado desta forma.

function foobar(a, b){}

console.log(foobar.name);

var a = function foobar(){};

console.log(a.name);

Caso contrário, se uma função for declarada como function(){}, a função .name será a primeira variável usada para armazenar a função.

var a = function(){};
var b = (function(){ return function(){} });

console.log(a.name);
console.log(b.name);

Se não houver variáveis ​​definidas para a função, o nome da função será a sequência vazia ( "").

console.log((function(){}).name === "");

Por fim, enquanto a variável à qual a função é atribuída define inicialmente o nome, as variáveis ​​sucessivas definidas para a função não alteram o nome.

var a = function(){};
var b = a;
var c = b;

console.log(a.name);
console.log(b.name);
console.log(c.name);

  1. atuação

No V8 do Google e no Spidermonkey do Firefox, pode haver uma diferença de compilação no JIST de alguns microssegundos, mas, em última análise, o resultado é exatamente o mesmo. Para provar isso, vamos examinar a eficiência do JSPerf nas marcas de microbench comparando a velocidade de dois trechos de código em branco. Os testes JSPerf são encontrados aqui . E os testes do jsben.ch são encontrados aqui . Como você pode ver, há uma diferença notável quando não deve haver nenhuma. Se você é realmente um fanático por desempenho como eu, vale a pena tentar reduzir o número de variáveis ​​e funções no escopo e principalmente eliminar o polimorfismo (como usar a mesma variável para armazenar dois tipos diferentes).

  1. Mutabilidade Variável

Ao usar a varpalavra-chave para declarar uma variável, você pode reatribuir um valor diferente à variável dessa forma.

(function(){
    "use strict";
    var foobar = function(){}; // initial value
    try {
        foobar = "Hello World!"; // new value
        console.log("[no error]");
    } catch(error) {
        console.log("ERROR: " + error.message);
    }
    console.log(foobar, window.foobar);
})();

No entanto, quando usamos a declaração const, a referência da variável se torna imutável. Isso significa que não podemos atribuir um novo valor à variável. Observe, no entanto, que isso não torna imutável o conteúdo da variável: se você o fizer const arr = [], poderá fazê-lo arr[10] = "example". Apenas fazer algo como arr = "new value"ou arr = []geraria um erro, como visto abaixo.

(function(){
    "use strict";
    const foobar = function(){}; // initial value
    try {
        foobar = "Hello World!"; // new value
        console.log("[no error]");
    } catch(error) {
        console.log("ERROR: " + error.message);
    }
    console.log(foobar, window.foobar);
})();

Curiosamente, se declararmos a variável como function funcName(){}, então a imutabilidade da variável é a mesma que a declarar var.

(function(){
    "use strict";
    function foobar(){}; // initial value
    try {
        foobar = "Hello World!"; // new value
        console.log("[no error]");
    } catch(error) {
        console.log("ERROR: " + error.message);
    }
    console.log(foobar, window.foobar);
})();

Qual é o "bloco mais próximo"

O "bloco mais próximo" é a "função" mais próxima (incluindo funções assíncronas, funções de gerador e funções de gerador assíncronas). No entanto, curiosamente, um a function functionName() {}se comporta como um var functionName = function() {}quando em um bloco de não fechamento para itens fora do referido fechamento. Observar.

  • Normal var add=function(){}

try {
  // typeof will simply return "undefined" if the variable does not exist
  if (typeof add !== "undefined") {
    add(1, 1); // just to prove it
    console.log("Not a block");
  }else if(add===undefined){ // this throws an exception if add doesn't exist
    console.log('Behaves like var add=function(a,b){return a+b}');
  }
} catch(e) {
  console.log("Is a block");
}
var add=function(a, b){return a + b}

  • Normal function add(){}

try {
  // typeof will simply return "undefined" if the variable does not exist
  if (typeof add !== "undefined") {
    add(1, 1); // just to prove it
    console.log("Not a block");
  }else if(add===undefined){ // this throws an exception if add doesn't exist
    console.log('Behaves like var add=function(a,b){return a+b}')
  }
} catch(e) {
  console.log("Is a block");
}
function add(a, b){
  return a + b;
}

  • Função

try {
  // typeof will simply return "undefined" if the variable does not exist
  if (typeof add !== "undefined") {
    add(1, 1); // just to prove it
    console.log("Not a block");
  }else if(add===undefined){ // this throws an exception if add doesn't exist
    console.log('Behaves like var add=function(a,b){return a+b}')
  }
} catch(e) {
  console.log("Is a block");
}
(function () {
    function add(a, b){
      return a + b;
    }
})();

  • Declaração (tais como if, else, for, while, try/ catch/ finally, switch, do/ while, with)

try {
  // typeof will simply return "undefined" if the variable does not exist
  if (typeof add !== "undefined") {
    add(1, 1); // just to prove it
    console.log("Not a block");
  }else if(add===undefined){ // this throws an exception if add doesn't exist
    console.log('Behaves like var add=function(a,b){return a+b}')
  }
} catch(e) {
  console.log("Is a block");
}
{
    function add(a, b){
      return a + b;
    }
}

  • Função Seta com var add=function()

try {
  // typeof will simply return "undefined" if the variable does not exist
  if (typeof add !== "undefined") {
    add(1, 1); // just to prove it
    console.log("Not a block");
  }else if(add===undefined){ // this throws an exception if add doesn't exist
    console.log('Behaves like var add=function(a,b){return a+b}')
  }
} catch(e) {
  console.log("Is a block");
}
(() => {
    var add=function(a, b){
      return a + b;
    }
})();

  • Função de seta com function add()

try {
  // typeof will simply return "undefined" if the variable does not exist
  if (typeof add !== "undefined") {
    add(1, 1); // just to prove it
    console.log("Not a block");
  }else if(add===undefined){ // this throws an exception if add doesn't exist
    console.log('Behaves like var add=function(a,b){return a+b}')
  }
} catch(e) {
  console.log("Is a block");
}
(() => {
    function add(a, b){
      return a + b;
    }
})();

Jack Giffin
fonte
Isso merece ser a resposta aceita e a mais votada
Aaron John Sabu
18

@EugeneLazutkin dá um exemplo em que ele nomeia uma função atribuída para poder usarshortcut() como referência interna a si mesma. John Resig dá outro exemplo - copiar uma função recursiva atribuída a outro objeto em seu tutorial Aprendendo Javascript Avançado . Embora a atribuição de funções a propriedades não seja estritamente a questão aqui, recomendo experimentar o tutorial - execute o código clicando no botão no canto superior direito e clique duas vezes no código para editar a seu gosto.

Exemplos do tutorial: chamadas recursivas em yell():

Os testes falham quando o objeto ninja original é removido. (página 13)

var ninja = { 
  yell: function(n){ 
    return n > 0 ? ninja.yell(n-1) + "a" : "hiy"; 
  } 
}; 
assert( ninja.yell(4) == "hiyaaaa", "A single object isn't too bad, either." ); 

var samurai = { yell: ninja.yell }; 
var ninja = null; 

try { 
  samurai.yell(4); 
} catch(e){ 
  assert( false, "Uh, this isn't good! Where'd ninja.yell go?" ); 
}

Se você nomear a função que será chamada recursivamente, os testes serão aprovados. (página 14)

var ninja = { 
  yell: function yell(n){ 
    return n > 0 ? yell(n-1) + "a" : "hiy"; 
  } 
}; 
assert( ninja.yell(4) == "hiyaaaa", "Works as we would expect it to!" ); 

var samurai = { yell: ninja.yell }; 
var ninja = {}; 
assert( samurai.yell(4) == "hiyaaaa", "The method correctly calls itself." );
Joel Purra
fonte
17

Outra diferença que não é mencionada nas outras respostas é que, se você usar a função anônima

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

e use isso como construtor como em

var one = new functionOne();

então one.constructor.namenão será definido.Function.namenão é padrão, mas é suportado pelo Firefox, Chrome, outros navegadores derivados do Webkit e IE 9+.

Com

function functionTwo() {
    // Some code
}
two = new functionTwo();

é possível recuperar o nome do construtor como uma string com two.constructor.name.

Ingo Kegel
fonte
O nome no primeiro caso não será definido porque é uma função anônima atribuída a uma variável. Acho que a palavra anônimo foi inventado para as coisas que não têm o seu nome definido :)
Om Shankar
Neste exemplo os dois = new torna-se uma função global porque nenhum var
Waqas Tahir
16

O primeiro (função doSomething (x)) deve fazer parte de uma notação de objeto.

O segundo ( var doSomething = function(x){ alert(x);}) é simplesmente criar uma função anônima e atribuí-la a uma variável doSomething,. Então doSomething () chamará a função

Você pode querer saber o que é uma declaração e expressão de função .

Uma declaração de função define uma variável de função nomeada sem exigir a atribuição de variáveis. As declarações de função ocorrem como construções independentes e não podem ser aninhadas em blocos não funcionais.

function foo() {
    return 3;
}

O ECMA 5 (13.0) define a sintaxe como
identificador da função (FormalParameterList opt ) {FunctionBody}

Na condição acima, o nome da função é visível dentro de seu escopo e do escopo de seu pai (caso contrário, seria inacessível).

E em uma expressão de função

Uma expressão de função define uma função como parte de uma sintaxe de expressão maior (geralmente uma atribuição de variável). As funções definidas por meio de expressões de funções podem ser nomeadas ou anônimas. As expressões de função não devem começar com "function".

// Anonymous function expression
var a = function() {
    return 3;
}

// Named function expression
var a = function foo() {
    return 3;
}

// Self-invoking function expression
(function foo() {
    alert("hello!");
})();

O ECMA 5 (13.0) define a sintaxe como
identificador da função opt (FormalParameterList opt ) {FunctionBody}

NullPoiиteя
fonte
16

Estou listando as diferenças abaixo:

  1. Uma declaração de função pode ser colocada em qualquer lugar do código. Mesmo que seja invocada antes que a definição apareça no código, ela é executada quando a declaração da função é confirmada na memória ou de uma maneira que é içada, antes de qualquer outro código na página iniciar a execução.

    Dê uma olhada na função abaixo:

    function outerFunction() {
        function foo() {
           return 1;
        }
        return foo();
        function foo() {
           return 2;
        }
    }
    alert(outerFunction()); // Displays 2

    Isso ocorre porque, durante a execução, parece:

    function foo() {  // The first function declaration is moved to top
        return 1;
    }
    function foo() {  // The second function declaration is moved to top
        return 2;
    }
    function outerFunction() {
        return foo();
    }
    alert(outerFunction()); //So executing from top to bottom,
                            //the last foo() returns 2 which gets displayed

    Uma expressão de função, se não for definida antes de chamá-la, resultará em um erro. Além disso, aqui a própria definição de função não é movida para o topo ou confirmada na memória, como nas declarações de função. Mas a variável à qual atribuímos a função é içada e indefinida é atribuída a ela.

    Mesma função usando expressões de função:

    function outerFunction() {
        var foo = function() {
           return 1;
        }
        return foo();
        var foo = function() {
           return 2;
        }
    }
    alert(outerFunction()); // Displays 1

    Isso ocorre porque durante a execução, parece com:

    function outerFunction() {
       var foo = undefined;
       var foo = undefined;
    
       foo = function() {
          return 1;
       };
       return foo ();
       foo = function() {   // This function expression is not reachable
          return 2;
       };
    }
    alert(outerFunction()); // Displays 1
  2. Não é seguro escrever declarações de função em blocos não funcionais como se não estivessem acessíveis.

    if (test) {
        function x() { doSomething(); }
    }
  3. Expressão de função nomeada como a abaixo, pode não funcionar nos navegadores Internet Explorer anteriores à versão 9.

    var today = function today() {return new Date()}
varna
fonte
1
@Arjun Qual é o problema se uma pergunta foi feita anos antes? Uma resposta não beneficia apenas o OP, mas potencialmente todos os usuários de SO, não importa quando a pergunta foi feita. E o que há de errado em responder perguntas que já têm uma resposta aceita?
SantiBailors
1
@Arjun você entende que responder perguntas antigas não é ruim. Se assim fosse, o SO teria tido essa barreira. Imagine que há uma alteração na API (embora não no contexto desta pergunta) e alguém a identifique e forneça uma resposta com a nova API, isso não deveria ser permitido? Até que, a menos que a resposta não faça sentido e não pertença aqui, ela será reduzida e removida automaticamente. Você não precisa se preocupar com isso !!!!
Sudhansu Choudhary
15

Se você usasse essas funções para criar objetos, obteria:

var objectOne = new functionOne();
console.log(objectOne.__proto__); // prints "Object {}" because constructor is an anonymous function

var objectTwo = new functionTwo();
console.log(objectTwo.__proto__); // prints "functionTwo {}" because constructor is a named function
Pawel Furmaniak
fonte
Não consigo reproduzir isso. console.log(objectOne.__proto__);imprime "functionOne {}" no meu console. Alguma idéia de por que isso pode ser o caso?
22315 Mike
Não consigo reproduzi-lo também.
precisa saber é
1
Essa é uma capacidade do seu depurador (para exibir a "classe" do objeto registrado) e a maioria deles pode derivar um nome mesmo para expressões de função anônimas atualmente. Btw, você deve deixar claro que não há diferença funcional entre as duas instâncias.
Bergi 21/01
12

À luz do argumento "funções nomeadas aparecem nos rastreamentos de pilha", os modernos mecanismos JavaScript são realmente capazes de representar funções anônimas.

No momento da redação deste documento, V8, SpiderMonkey, Chakra e Nitro sempre se referem às funções nomeadas por seus nomes. Eles quase sempre se referem a uma função anônima pelo seu identificador, se houver.

SpiderMonkey pode descobrir o nome de uma função anônima retornada de outra função. O resto não pode.

Se você realmente queria que seu iterador e retornos de sucesso aparecessem no rastreamento, também poderia nomear esses ...

[].forEach(function iterator() {});

Mas, na maioria das vezes, não vale a pena enfatizar.

Arnês ( Fiddle )

'use strict';

var a = function () {
    throw new Error();
},
    b = function b() {
        throw new Error();
    },
    c = function d() {
        throw new Error();
    },
    e = {
        f: a,
        g: b,
        h: c,
        i: function () {
            throw new Error();
        },
        j: function j() {
            throw new Error();
        },
        k: function l() {
            throw new Error();
        }
    },
    m = (function () {
        return function () {
            throw new Error();
        };
    }()),
    n = (function () {
        return function n() {
            throw new Error();
        };
    }()),
    o = (function () {
        return function p() {
            throw new Error();
        };
    }());

console.log([a, b, c].concat(Object.keys(e).reduce(function (values, key) {
    return values.concat(e[key]);
}, [])).concat([m, n, o]).reduce(function (logs, func) {

    try {
        func();
    } catch (error) {
        return logs.concat('func.name: ' + func.name + '\n' +
                           'Trace:\n' +
                           error.stack);
        // Need to manually log the error object in Nitro.
    }

}, []).join('\n\n'));

V8

func.name: 
Trace:
Error
    at a (http://localhost:8000/test.js:4:11)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: b
Trace:
Error
    at b (http://localhost:8000/test.js:7:15)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: d
Trace:
Error
    at d (http://localhost:8000/test.js:10:15)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: 
Trace:
Error
    at a (http://localhost:8000/test.js:4:11)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: b
Trace:
Error
    at b (http://localhost:8000/test.js:7:15)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: d
Trace:
Error
    at d (http://localhost:8000/test.js:10:15)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: 
Trace:
Error
    at e.i (http://localhost:8000/test.js:17:19)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: j
Trace:
Error
    at j (http://localhost:8000/test.js:20:19)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: l
Trace:
Error
    at l (http://localhost:8000/test.js:23:19)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: 
Trace:
Error
    at http://localhost:8000/test.js:28:19
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: n
Trace:
Error
    at n (http://localhost:8000/test.js:33:19)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: p
Trace:
Error
    at p (http://localhost:8000/test.js:38:19)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27 test.js:42

Macaco aranha

func.name: 
Trace:
a@http://localhost:8000/test.js:4:5
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: b
Trace:
b@http://localhost:8000/test.js:7:9
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: d
Trace:
d@http://localhost:8000/test.js:10:9
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: 
Trace:
a@http://localhost:8000/test.js:4:5
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: b
Trace:
b@http://localhost:8000/test.js:7:9
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: d
Trace:
d@http://localhost:8000/test.js:10:9
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: 
Trace:
e.i@http://localhost:8000/test.js:17:13
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: j
Trace:
j@http://localhost:8000/test.js:20:13
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: l
Trace:
l@http://localhost:8000/test.js:23:13
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: 
Trace:
m</<@http://localhost:8000/test.js:28:13
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: n
Trace:
n@http://localhost:8000/test.js:33:13
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: p
Trace:
p@http://localhost:8000/test.js:38:13
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1

Chakra

func.name: undefined
Trace:
Error
   at a (http://localhost:8000/test.js:4:5)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at b (http://localhost:8000/test.js:7:9)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at d (http://localhost:8000/test.js:10:9)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at a (http://localhost:8000/test.js:4:5)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at b (http://localhost:8000/test.js:7:9)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at d (http://localhost:8000/test.js:10:9)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at e.i (http://localhost:8000/test.js:17:13)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at j (http://localhost:8000/test.js:20:13)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at l (http://localhost:8000/test.js:23:13)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at Anonymous function (http://localhost:8000/test.js:28:13)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at n (http://localhost:8000/test.js:33:13)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at p (http://localhost:8000/test.js:38:13)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)

Nitro

func.name: 
Trace:
a@http://localhost:8000/test.js:4:22
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: b
Trace:
b@http://localhost:8000/test.js:7:26
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: d
Trace:
d@http://localhost:8000/test.js:10:26
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: 
Trace:
a@http://localhost:8000/test.js:4:22
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: b
Trace:
b@http://localhost:8000/test.js:7:26
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: d
Trace:
d@http://localhost:8000/test.js:10:26
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: 
Trace:
i@http://localhost:8000/test.js:17:30
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: j
Trace:
j@http://localhost:8000/test.js:20:30
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: l
Trace:
l@http://localhost:8000/test.js:23:30
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: 
Trace:
http://localhost:8000/test.js:28:30
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: n
Trace:
n@http://localhost:8000/test.js:33:30
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: p
Trace:
p@http://localhost:8000/test.js:38:30
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33
Jackson
fonte
12

No JavaScript, existem duas maneiras de criar funções:

  1. Declaração de função:

    function fn(){
      console.log("Hello");
    }
    fn();

    Isso é muito básico, autoexplicativo, usado em muitos idiomas e padrão na família C de idiomas. Declaramos que uma função a definiu e a executou chamando-a.

    O que você deveria saber é que funções são realmente objetos em JavaScript; internamente, criamos um objeto para a função acima e atribuímos a ele um nome chamado fn ou a referência ao objeto é armazenada em fn. Funções são objetos em JavaScript; uma instância da função é na verdade uma instância do objeto.

  2. Expressão da função:

    var fn=function(){
      console.log("Hello");
    }
    fn();

    O JavaScript possui funções de primeira classe, ou seja, crie uma função e atribua-a a uma variável, assim como você cria uma sequência ou número e atribui-a a uma variável. Aqui, a variável fn é atribuída a uma função. A razão para esse conceito é que funções são objetos em JavaScript; fn está apontando para a instância do objeto da função acima. Inicializamos uma função e a atribuímos a uma variável. Não está executando a função e atribuindo o resultado.

Referência: sintaxe da declaração da função JavaScript: var fn = function () {} vs function fn () {}

Anoop Rai
fonte
1
e a terceira opção var fn = function fn() {...}?
chharvey
Oi Chharvey, não tenho certeza sobre a sua pergunta, eu acho que você está falando sobre a expressão da função que eu já mencionei. No entanto, se ainda houver alguma confusão, seja mais elaborado.
Anoop Rai
sim, eu estava perguntando sobre uma expressão de função nomeada . é semelhante à sua opção nº 2, exceto que a função tem um identificador. geralmente esse identificador é o mesmo que a variável à qual está sendo atribuída, mas esse nem sempre é o caso.
precisa saber é o seguinte
1
Sim A expressão da função nomeada é semelhante à minha opção nº 2. Ter um identificador não é obrigatório, pois não é usado. Sempre que você estiver executando a expressão da função, usará a variável que contém o objeto da função. Identificador não serve para nada.
Anoop Rai
11

Ambas são formas diferentes de definir uma função. A diferença é como o navegador as interpreta e as carrega em um contexto de execução.

O primeiro caso é de expressões de função que são carregadas apenas quando o intérprete atinge essa linha de código. Portanto, se você fizer o seguinte, você receberá um erro dizendo que a functionOne não é uma função .

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

O motivo é que, na primeira linha, nenhum valor é atribuído a functionOne e, portanto, é indefinido. Estamos tentando chamá-lo como uma função e, portanto, estamos recebendo um erro.

Na segunda linha, estamos atribuindo a referência de uma função anônima a functionOne.

O segundo caso é de declarações de função que são carregadas antes que qualquer código seja executado. Portanto, se você gosta do seguinte, não receberá nenhum erro, pois a declaração é carregada antes da execução do código.

functionOne();
function functionOne() {
   // Some code
}
Nitin9791
fonte
11

Sobre o desempenho:

Novas versões V8introduziram várias otimizações ocultas e o mesmo aconteceu SpiderMonkey.

Não há quase nenhuma diferença agora entre expressão e declaração.
A expressão da função parece ser mais rápida agora.

Chrome 62.0.3202 Teste de cromo

FireFox 55 Teste do Firefox

Chrome Canary 63.0.3225 Teste de canário do Chrome


Anonymousexpressões de função parecem ter melhor desempenho em relação à Namedexpressão de função.


Firefox Chrome Canary ChromeFirefox named_anonymous Canário do Chrome named_anonymous Chrome named_anonymous

Panos Kal.
fonte
1
Sim, essa diferença é tão insignificante que, esperançosamente, os desenvolvedores se preocuparão com qual abordagem é mais sustentável para suas necessidades específicas e não com qual delas pode ser mais rápida (você obterá resultados diferentes do jsperf em cada tentativa, dependendo do que o navegador estiver fazendo - a maioria das tarefas javascript não precisa se preocupar com micro otimizações nesse nível).
squidbe
@squidbe Não há diferença. Veja aqui: jsperf.com/empty-tests-performance
Jack Giffin
10

Eles são bem parecidos com algumas pequenas diferenças, a primeira é uma variável atribuída a uma função anônima (Declaração de Função) e a segunda é a maneira normal de criar uma função em JavaScript (Declaração de Função Anônima), ambas com uso, contras e prós. :

1. Expressão de Função

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

Uma expressão de função define uma função como parte de uma sintaxe de expressão maior (geralmente uma atribuição de variável). As funções definidas por meio de Expressões de funções podem ser nomeadas ou anônimas. As expressões de função não devem começar com "function" (daí os parênteses em torno do exemplo de auto-invocação abaixo).

Atribua uma variável a uma função, significa que nenhum Hoisting, como sabemos que funções em JavaScript podem Hoist, significa que elas podem ser chamadas antes de serem declaradas, enquanto as variáveis ​​precisam ser declaradas antes de ter acesso a elas, portanto, neste caso, não podemos acessar a função antes de onde é declarada, também pode ser uma maneira de você escrever suas funções; para as funções que retornam outra função, esse tipo de declaração pode fazer sentido; também no ECMA6 e acima, você pode atribuir isso a uma função de seta que pode ser usado para chamar funções anônimas, também esta maneira de declarar é a melhor maneira de criar funções do Construtor em JavaScript.

2. Declaração de Função

function functionTwo() {
    // Some code
}

Uma declaração de função define uma variável de função nomeada sem exigir a atribuição de variáveis. As declarações de função ocorrem como construções independentes e não podem ser aninhadas dentro de blocos não funcionais. É útil pensar neles como irmãos de declarações variáveis. Assim como as declarações variáveis ​​devem começar com "var", as declarações de funções devem começar com "function".

Essa é a maneira normal de chamar uma função em JavaScript; essa função pode ser chamada antes mesmo de declará-la, pois em JavaScript todas as funções são Hoisted, mas se você tiver 'use strict', isso não será Hoist conforme o esperado, é uma boa maneira. chamar todas as funções normais que não são grandes em linhas e nem são uma função construtora.

Além disso, se você precisar de mais informações sobre como o içamento funciona em JavaScript, visite o link abaixo:

https://developer.mozilla.org/en-US/docs/Glossary/Hoisting

Alireza
fonte
1
...also this way of declaring is a better way to create Constructor functions in JavaScript, você pode elaborar, estou curioso!
Karl Morrison
Um dos motivos é que todas as funções internas do Construtor em JavaScript criadas como esta função Number () {[código nativo]} e você não devem ser confundidas com as internas, mas também é mais seguro fazer referência posteriormente neste caso. -se o código mais limpo, mas não usando elevação ...
Alireza
8

Esta é apenas duas maneiras possíveis de declarar funções e, na segunda maneira, você pode usar a função antes da declaração.

Tao
fonte
7

new Function()pode ser usado para passar o corpo da função em uma string. E, portanto, isso pode ser usado para criar funções dinâmicas. Também passando o script sem executar o script.

var func = new Function("x", "y", "return x*y;");
function secondFunction(){
   var result;
   result = func(10,20);
   console.log ( result );
}

secondFunction()
Super Nova
fonte
Embora isso seja bom e verdadeiro, como exatamente isso se relaciona com o questionário que está sendo solicitado?
Jack Giffin