I'comecei recentemente a manter outra pessoa's código JavaScript. I'm corrigindo bugs, adicionando funcionalidades e também tentando arrumar o código e torná-lo mais consistente.
O programador anterior usa duas formas de declarar funções e eu posso't funcionar se há ou não uma razão por detrás disso.
As duas formas são:
var functionOne = function() {
// Some code
};
function functionTwo() {
// Some code
}
Quais são as razões para utilizar estes dois métodos diferentes e quais são os prós e os contras de cada um deles? Há alguma coisa que possa ser feita com um método que possa'não pode ser feita com o outro?
A diferença é que functionOne
é uma expressão de função e por isso só é definida quando essa linha é alcançada, enquanto que functionTwo
é uma declaração de função e é definida assim que a sua função ou guião circundante é executado (devido a içamento).
Por exemplo, uma expressão funcional:
// 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!");
}
Isto também significa que pode't definir condicionalmente funções usando declarações de funções:
if (test) {
// Error or misbehavior
function functionThree() { doSomething(); }
}
O acima referido define realmente funçãoTrês
independentemente do teste
's valor — a menos que utilização estrita
esteja em vigor, caso em que simplesmente levanta um erro.
Primeiro quero corrigir Greg: function abc(){}
is scoped too — o nome abc
é definido no âmbito onde esta definição é encontrada. Exemplo:
function xyz(){
function abc(){};
// abc is defined here...
}
// ...but not here
Em segundo lugar, é possível combinar ambos os estilos:
var xyz = function abc(){};
O xyz
vai ser definido como habitualmente, o abc
é indefinido em todos os navegadores excepto Internet Explorer — não confie em que seja definido. Mas será definido dentro do seu corpo:
var xyz = function abc(){
// xyz is visible here
// abc is visible here
}
// xyz is visible here
// abc is undefined here
Se pretender funções de pseudónimo em todos os browsers, utilize este tipo de declaração:
function abc(){};
var xyz = abc;
Neste caso, tanto o xyz
como o abc
são pseudónimos do mesmo objecto:
console.log(xyz === abc); // prints "true"
Uma razão convincente para usar o estilo combinado é o "name" atributo de objectos de função (não suportado por Internet Explorer). Basicamente, quando se define uma função como
function abc(){};
console.log(abc.name); // prints "abc"
o seu nome é automaticamente atribuído. Mas quando o define como
var abc = function(){};
console.log(abc.name); // prints ""
o seu nome está vazio — criámos uma função anónima e atribuímo-la a alguma variável.
Outra boa razão para usar o estilo combinado é usar um nome curto interno para se referir a si próprio, ao mesmo tempo que fornece um nome longo não conflituoso para utilizadores 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 ele'será demasiado pesado (e mais lento).
(Outra forma de se referir a si próprio é utilizar argumentos.callee
, que ainda é relativamente longo, e não suportado no modo estrito).
No fundo, o JavaScript trata as duas afirmações de forma diferente. Esta é uma declaração de função:
function abc(){}
O abc
aqui é definido em toda a parte no âmbito actual:
// 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, eleva-se através de uma declaração de "retorno":
// We can call it here
abc(); // Works
return;
function abc(){}
Esta é uma expressão funcional:
var xyz = function(){};
O xyz
aqui é definido a partir do ponto de afectação:
// 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 versus expressão de função é a verdadeira razão pela qual existe uma diferença demonstrada por Greg.
Facto engraçado:
var xyz = function abc(){};
console.log(xyz.name); // Prints "abc"
Pessoalmente, prefiro o " expressão da função" declaração porque desta forma posso controlar a visibilidade. Quando defino a função como
var abc = function(){};
Eu sei que defini a função localmente. Quando defino a função como
abc = function(){};
Sei que o defini globalmente desde que não'não definisse abc
em qualquer parte da cadeia de âmbitos. Este estilo de definição é resiliente mesmo quando utilizado dentro de eval()
. Enquanto a definição
function abc(){};
depende do contexto e pode deixá-lo adivinhar onde está realmente definido, especialmente no caso de eval()
— a resposta é: Depende do navegador.
Em termos de informática, falamos de funções anónimas e funções nomeadas. Penso que a diferença mais importante é que uma função anónima não está ligada a um nome, daí o nome de função anónima. Em JavaScript é um objecto de primeira classe declarado dinamicamente em tempo de execução.
Para mais informações sobre funções anónimas e cálculo lambda, a Wikipédia é um bom começo (http://en.wikipedia.org/wiki/Anonymous_function).