Recientemente he empezado a mantener el código JavaScript de otra persona. Estoy corrigiendo errores, añadiendo características y también tratando de ordenar el código y hacerlo más consistente.
El desarrollador anterior utiliza dos formas de declarar las funciones y no puedo averiguar si hay una razón detrás de esto o no.
Las dos formas son:
var functionOne = function() {
// Some code
};
..;
function functionTwo() {
// Some code
}
¿Cuáles son las razones para utilizar estos dos métodos diferentes y cuáles son los pros y los contras de cada uno? ¿Hay algo que se pueda hacer con un método que no se pueda hacer con el otro?
La diferencia es que funciónUna
es una expresión de función y, por tanto, sólo se define cuando se llega a esa línea, mientras que funciónDos
es una declaración de función y se define tan pronto como se ejecuta la función o el script que la rodea (debido al hoisting).
Por ejemplo, una expresión de función:
// TypeError: functionOne is not a function
functionOne();
var functionOne = function() {
console.log("Hello!");
};
Y, una declaración de función:
// Outputs: "Hello!"
functionTwo();
function functionTwo() {
console.log("Hello!");
}
Esto también significa que no puedes definir funciones condicionalmente usando declaraciones de función:
if (test) {
// Error or misbehavior
function functionThree() { doSomething(); }
}
Lo anterior define realmente "funciónTres" independientemente del valor de "test"; a menos que "use strict" esté en efecto, en cuyo caso simplemente genera un error.
Primero quiero corregir a Greg: función abc(){}
tiene un alcance demasiado — el nombre abc
está definido en el alcance donde se encuentra esta definición. Ejemplo:
function xyz(){
function abc(){};
// abc is defined here...
}
// ...but not here
En segundo lugar, es posible combinar ambos estilos:
var xyz = function abc(){};
xyz
se va a definir como siempre, abc
no está definido en todos los navegadores menos en Internet Explorer — no confíe en que esté definido. Pero se definirá dentro de su cuerpo:
var xyz = function abc(){
// xyz is visible here
// abc is visible here
}
// xyz is visible here
// abc is undefined here
Si quiere poner un alias a las funciones en todos los navegadores, utilice este tipo de declaración:
function abc(){};
var xyz = abc;
En este caso, tanto xyz
como abc
son alias del mismo objeto:
console.log(xyz === abc); // prints "true"
Una razón de peso para utilizar el estilo combinado es el atributo "name" de los objetos de función (no soportado por Internet Explorer). Básicamente, cuando se define una función como
function abc(){};
console.log(abc.name); // prints "abc"
su nombre se asigna automáticamente. Pero cuando se define como
var abc = function(){};
console.log(abc.name); // prints ""
su nombre está vacío — creamos una función anónima y la asignamos a alguna variable.
Otra buena razón para utilizar el estilo combinado es utilizar un nombre interno corto para referirse a sí mismo, mientras que proporciona un nombre largo no conflictivo para los usuarios 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);
// ...
}
En el ejemplo anterior podemos hacer lo mismo con un nombre externo, pero será demasiado difícil de manejar (y más lento).
(Otra forma de referirse a sí mismo es usar argumentos.callee
, que sigue siendo relativamente largo, y no es soportado en el modo estricto.)
En el fondo, JavaScript trata ambas declaraciones de forma diferente. Esta es una declaración de función:
function abc(){}
abc
aquí se define en todas partes en el ámbito actual:
// We can call it here
abc(); // Works
// Yet, it is defined down there.
function abc(){}
// We can call it again
abc(); // Works
Además, se eleva a través de una declaración return
:
// We can call it here
abc(); // Works
return;
function abc(){}
Esta es una expresión de función:
var xyz = function(){};
xyz
aquí se define desde el punto de asignación:
// We can't call it here
xyz(); // UNDEFINED!!!
// Now it is defined
xyz = function(){}
// We can call it here
xyz(); // works
La declaración de la función frente a la expresión de la función es la verdadera razón por la que hay una diferencia demostrada por Greg.
Dato curioso:
var xyz = function abc(){};
console.log(xyz.name); // Prints "abc"
Personalmente, prefiero la declaración de "expresión de función" porque así puedo controlar la visibilidad. Cuando defino la función como
var abc = function(){};
Sé que he definido la función localmente. Cuando defino la función como
abc = function(){};
Sé que la definí globalmente siempre que no definí abc
en ninguna parte de la cadena de ámbitos. Este estilo de definición es resistente incluso cuando se utiliza dentro de eval()
. Mientras que la definición
function abc(){};
depende del contexto y puede dejarte adivinando dónde se define realmente, especialmente en el caso de eval()
— la respuesta es: depende del navegador.
En términos informáticos, hablamos de funciones anónimas y funciones con nombre. Creo que la diferencia más importante es que una función anónima no está ligada a un nombre, de ahí el nombre de función anónima. En JavaScript es un objeto de primera clase declarado dinámicamente en tiempo de ejecución.
Para más información sobre las funciones anónimas y el cálculo lambda, Wikipedia es un buen comienzo (http://en.wikipedia.org/wiki/Anonymous_function).