Aquí está el resumen de los formularios estándar que crean funciones: (Originalmente escrito para otra pregunta, pero adaptado después de pasar a la pregunta canónica).
Condiciones:
La lista rápida:
Declaración de funciones
function
Expresión "anónima" (que a pesar del término, a veces crea funciones con nombres)
function
Expresión nombrada
Accesorio Inicializador de funciones (ES5 +)
Expresión de función de flecha (ES2015 +) (que, como las expresiones de función anónimas, no implican un nombre explícito y, sin embargo, puede crear funciones con nombres)
Declaración de método en Object Initializer (ES2015 +)
Declaraciones de constructor y método en class
(ES2015 +)
Declaración de funciones
La primera forma es una declaración de función , que se ve así:
function x() {
console.log('x');
}
Una declaración de función es una declaración ; No es una declaración o expresión. Como tal, no lo sigues con un ;
(aunque hacerlo es inofensivo).
Se procesa una declaración de función cuando la ejecución entra en el contexto en el que aparece, antes de que se ejecute un código paso a paso. La función que crea recibe un nombre propio ( x
en el ejemplo anterior), y ese nombre se coloca en el ámbito en el que aparece la declaración.
Como se procesa antes de cualquier código paso a paso en el mismo contexto, puede hacer cosas como esta:
x(); // Works even though it's above the declaration
function x() {
console.log('x');
}
Hasta ES2015, la especificación no cubría lo que es un motor de JavaScript debe hacer si se pone una declaración de la función dentro de una estructura de control como try
, if
, switch
, while
, etc, así:
if (someCondition) {
function foo() { // <===== HERE THERE
} // <===== BE DRAGONS
}
Y como son procesados antes de ejecutar el código paso a paso, es complicado saber qué hacer cuando están en una estructura de control.
Aunque hacer esto no se especificó hasta ES2015, fue un extensión permitida para admitir declaraciones de funciones en bloques. Desafortunadamente (e inevitablemente), diferentes motores hicieron cosas diferentes.
A partir de ES2015, la especificación dice qué hacer. De hecho, da tres cosas separadas que hacer:
- Si en modo suelto no en un navegador web, se supone que el motor de JavaScript debe hacer una cosa
- Si está en modo suelto en un navegador web, se supone que el motor de JavaScript debe hacer otra cosa
- Si está en modo estricto (navegador o no), se supone que el motor de JavaScript debe hacer otra cosa
Las reglas para los modos sueltos son complicadas, pero en modo estricto , las declaraciones de funciones en bloques son fáciles: son locales para el bloque (tienen alcance de bloque , que también es nuevo en ES2015), y se elevan a la parte superior de la cuadra. Entonces:
"use strict";
if (someCondition) {
foo(); // Works just fine
function foo() {
}
}
console.log(typeof foo); // "undefined" (`foo` is not in scope here
// because it's not in the same block)
function
Expresión "anónima"
La segunda forma común se denomina expresión de función anónima :
var y = function () {
console.log('y');
};
Como todas las expresiones, se evalúa cuando se alcanza en la ejecución paso a paso del código.
En ES5, la función que esto crea no tiene nombre (es anónima). En ES2015, a la función se le asigna un nombre si es posible deduciéndolo del contexto. En el ejemplo anterior, el nombre sería y
. Algo similar se hace cuando la función es el valor de un inicializador de propiedad. (Para obtener detalles sobre cuándo sucede esto y las reglas, busque SetFunctionName
en la especificación ; aparece en todas partes ).
Llamado function
Expresión
La tercera forma es una expresión de función con nombre ("NFE"):
var z = function w() {
console.log('zw')
};
La función que esto crea tiene un nombre propio ( w
en este caso). Al igual que todas las expresiones, esto se evalúa cuando se alcanza en la ejecución paso a paso del código. El nombre de la función no se agrega al ámbito en el que aparece la expresión; el nombre está dentro del alcance de la función en sí:
var z = function w() {
console.log(typeof w); // "function"
};
console.log(typeof w); // "undefined"
Tenga en cuenta que los NFE han sido con frecuencia una fuente de errores para las implementaciones de JavaScript. IE8 y versiones anteriores, por ejemplo, manejan NFE completamente incorrectamente , creando dos funciones diferentes en dos momentos diferentes. Las primeras versiones de Safari también tenían problemas. La buena noticia es que las versiones actuales de los navegadores (IE9 y superiores, Safari actual) ya no tienen esos problemas. (Pero a partir de este escrito, lamentablemente, IE8 sigue siendo de uso generalizado, por lo que el uso de NFE con código para la web en general sigue siendo problemático).
Accesorio Inicializador de funciones (ES5 +)
Algunas veces las funciones pueden colarse en gran medida sin ser notadas; Ese es el caso de las funciones de acceso . Aquí hay un ejemplo:
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"
Tenga en cuenta que cuando usé la función, ¡no la usé ()
! Eso es porque es una función de acceso para una propiedad. Obtenemos y configuramos la propiedad de la manera normal, pero detrás de escena, se llama a la función.
También puede crear funciones de descriptor de acceso con Object.defineProperty
, Object.defineProperties
y la menos conocida segundo argumento Object.create
.
Expresión de función de flecha (ES2015 +)
ES2015 nos trae la función de flecha . Aquí hay un ejemplo:
var a = [1, 2, 3];
var b = a.map(n => n * 2);
console.log(b.join(", ")); // 2, 4, 6
¿Ves esa n => n * 2
cosa escondida en la map()
llamada? Esa es una funcion.
Un par de cosas sobre las funciones de flecha:
No tienen los suyos this
. En su lugar, se cierran sobre el this
del contexto en el que están definidas. (También cierran arguments
y, cuando corresponde,super
) Esto significa que el this
interior de ellos es el mismo que el lugar this
donde se crearon y no se puede cambiar.
Como habrás notado con lo anterior, no utilizas la palabra clave function
; en su lugar, usas=>
.
los n => n * 2
ejemplo anterior es una forma de ellos. Si tiene múltiples argumentos para pasar la función, usa parens:
var a = [1, 2, 3];
var b = a.map((n, i) => n * i);
console.log(b.join(", ")); // 0, 2, 6
(Recuerde que Array#map
pasa la entrada como primer argumento y el índice como segundo).
En ambos casos, el cuerpo de la función es solo una expresión; El valor de retorno de la función será automáticamente el resultado de esa expresión (no utiliza un explícito return
).
Si está haciendo más que una sola expresión, use {}
y explícito return
(si necesita devolver un valor), como es 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));
La versión sin { ... }
se llama función de flecha con un cuerpo de expresión o cuerpo conciso . (También: una función de flecha concisa .) La que { ... }
define el cuerpo es una función de flecha con un cuerpo de función . (También: una función de flecha detallada .)
Declaración de método en Object Initializer (ES2015 +)
ES2015 permite una forma más corta de declarar una propiedad que hace referencia a una función llamada definición de método ; se parece a esto:
var o = {
foo() {
}
};
el casi equivalente en ES5 y versiones anteriores sería:
var o = {
foo: function foo() {
}
};
La diferencia (aparte de la verbosidad) es que un método puede usar super
, pero una función no. Entonces, por ejemplo, si tuviera un objeto que definiera (digamos) valueOf
usando la sintaxis del método, podría usarlo super.valueOf()
para obtener el valor Object.prototype.valueOf
que habría devuelto (antes de presumiblemente hacer algo más con él), mientras que la versión ES5 tendría que hacerlo en su Object.prototype.valueOf.call(this)
lugar.
Eso también significa que el método tiene una referencia al objeto en el que se definió, por lo que si ese objeto es temporal (por ejemplo, lo está pasando Object.assign
como uno de los objetos de origen), la sintaxis del método podría significar que el objeto se conserva en la memoria cuando de lo contrario podría haberse recolectado basura (si el motor de JavaScript no detecta esa situación y la maneja si ninguno de los métodos utiliza super
).
Declaraciones de constructor y método en class
(ES2015 +)
ES2015 nos trae la class
sintaxis, incluidos los constructores y métodos declarados:
class Person {
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
getFullName() {
return this.firstName + " " + this.lastName;
}
}
Hay dos declaraciones de funciones anteriores: una para el constructor, que obtiene el nombre Person
, y otra para getFullName
, que es una función asignada Person.prototype
.