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


Recientemente comencé a mantener el código de JavaScript de otra persona. Estoy arreglando errores, agregando funciones y también tratando de ordenar el código y hacerlo más consistente.

El desarrollador anterior usa dos formas de declarar funciones y no puedo resolver si hay una razón detrás 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 puede hacer con el otro?


6052
2017-12-03 11:31


origen


Respuestas:


La diferencia es que functionOne es una expresión de función y solo se define cuando se alcanza esa línea, mientras que functionTwo es una declaración de función y se define tan pronto como se ejecuta su función o script circundante (debido a elevando)

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 puede definir funciones condicionalmente usando declaraciones de funciones:

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

Lo anterior realmente define functionThree sin tener en consideración testvalor de s a menos que use strict está en efecto, en cuyo caso simplemente genera un error.


4489
2017-12-03 11:37



Primero quiero corregir a Greg: function abc(){} tiene un alcance también: el nombre abc se define 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 de costumbre, abc no está definido en todos los navegadores, sino en Internet Explorer: no confíe en que se defina. 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 desea alias funciones en todos los navegadores, use este tipo de declaración:

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

En este caso, ambos xyz y abc son alias del mismo objeto:

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

Una razón convincente para usar el estilo combinado es el atributo "nombre" de los objetos de función (no admitido por Internet Explorer) Básicamente cuando defines una función como

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

su nombre es asignado automáticamente. Pero cuando lo defines 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 usar el estilo combinado es usar un nombre interno corto para referirse a sí mismo, al tiempo que proporciona un nombre largo y 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í misma es usar arguments.callee, que todavía es relativamente largo y no se admite en el modo estricto).

En el fondo, JavaScript trata ambas declaraciones de manera diferente. Esta es una declaración de función:

function abc(){}

abc aquí se define en todas partes en el alcance 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 izó a través de un return declaración:

// 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 cual hay una diferencia demostrada por Greg.

Hecho de la diversión:

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

Personalmente, prefiero la declaración de "expresión de función" porque de esta forma puedo controlar la visibilidad. Cuando defino la función como

var abc = function(){};

Sé que definí la función localmente. Cuando defino la función como

abc = function(){};

Sé que lo definí globalmente, siempre que no definí abc en cualquier lugar de la cadena de telescopios. Este estilo de definición es resistente incluso cuando se usa en el interior eval(). Mientras que la definición

function abc(){};

depende del contexto y puede dejarlo adivinando dónde está realmente definido, especialmente en el caso de eval() - la respuesta es: depende del navegador.


1804
2017-12-03 17:43



Aquí está el resumen de los formularios estándar que crean funciones: (Originalmente escrito para otra pregunta, pero adaptado después de ser trasladado a la pregunta canónica).

Condiciones:

La lista rápida:

  • Declaración de funciones

  • "Anónimo" function Expresión (que a pesar del término, a veces crea funciones con nombres)

  • Llamado function Expresión

  • Inicializador de función de acceso (ES5 +)

  • Expresión de la función de flecha (ES2015 +) (que, al igual que las expresiones de función anónima, no implican un nombre explícito, y aún así pueden crear funciones con nombres)

  • Declaración de método en el inicializador de objetos (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).

Una declaración de función se procesa cuando la ejecución ingresa al contexto en el que aparece, antes de se ejecuta cualquier código paso a paso. La función que crea recibe un nombre propio (x en el ejemplo anterior), y ese nombre se pone en el ámbito en el que aparece la declaración.

Debido a que se procesa antes que 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 debería hacer un motor de JavaScript si coloca una declaración de función dentro de una estructura de control como try, if, switch, while, etc., así:

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

Y dado que se procesan antes de se ejecuta el código paso a paso, es complicado saber qué hacer cuando están en una estructura de control.

Aunque hacer esto no fue especificado hasta ES2015, era 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:

  1. Si está en modo suelto no en un navegador web, se supone que el motor JavaScript debe hacer una cosa
  2. Si está en modo suelto en un navegador web, se supone que el motor JavaScript debe hacer algo más
  3. Si en estricto modo (navegador o no), el motor de JavaScript se supone que debe hacer otra cosa

Las reglas para los modos sueltos son complicadas, pero en estricto modo, las declaraciones de función en bloques son fáciles: son locales al bloque (tienen alcance del bloque, que también es nuevo en ES2015), y se elevan hasta la parte superior del bloque. Asi que:

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

"Anónimo" function Expresión

La segunda forma común se llama 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 crea no tiene nombre (es anónima). En ES2015, a la función se le asigna un nombre si es posible al inferirlo 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 propiedades. (Para detalles sobre cuando esto sucede y las reglas, busque SetFunctionName en el la especificación- aparece por todas partes el lugar.)

Llamado function Expresión

La tercera forma es una expresión de función nombrada ("NFE"):

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

La función que esto crea tiene un nombre propio (w en este caso). Como 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 es no agregado al alcance en el que aparece la expresión; el nombre es en el alcance dentro de la función en sí:

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

Tenga en cuenta que las NFE frecuentemente han sido una fuente de errores para las implementaciones de JavaScript. IE8 y 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 versiones posteriores, 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).

Inicializador de función de acceso (ES5 +)

A veces las funciones pueden colarse en gran medida inadvertidas; ese es el caso con 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 usé ()! Eso es porque es un función de acceso para una propiedad. Obtenemos y establecemos la propiedad de la manera normal, pero detrás de las escenas, se llama a la función.

También puede crear funciones de acceso con Object.defineProperty, Object.defineProperties, y el segundo argumento menos conocido para Object.create.

Expresión de la función de flecha (ES2015 +)

ES2015 nos trae el 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

Mira eso n => n * 2 Lo que se esconde en el map() ¿llamada? Esa es una función.

Un par de cosas sobre las funciones de flecha:

  1. Ellos no tienen su propio this. En cambio, ellos cerrar sobre el this del contexto donde están definidos. (También cierran sobre arguments y, cuando sea relevante, super.) Esto significa que el this dentro de ellos es lo mismo que el this donde fueron creados, y no pueden ser cambiados.

  2. Como habrás notado con lo anterior, no usas la palabra clave function; en su lugar, usas =>.

los n => n * 2 el 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

(Recuerda eso Array#map pasa la entrada como el primer argumento y el índice como el 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 se usa un explícito return)

Si está haciendo algo más que una sola expresión, use {} y un 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 una función de flecha con un cuerpo de expresión o cuerpo conciso. (También un conciso función de flecha.) El que tiene { ... } definir el cuerpo es una función de flecha con un cuerpo de la función. (También un verboso función de flecha.)

Declaración de método en el inicializador de objetos (ES2015 +)

ES2015 permite una forma más corta de declarar una propiedad que hace referencia a una función; se parece a esto:

var o = {
    foo() {
    }
};

el equivalente en ES5 y anterior sería:

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

Declaraciones de constructor y método en class (ES2015 +)

ES2015 nos trae 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 arriba: una para el constructor, que recibe el nombre Persony uno para getFullName, que es una función asignada a Person.prototype.


544
2018-03-04 13:35



Hablando sobre el contexto global, ambos, el var declaración y una FunctionDeclaration al final creará un no borrable propiedad en el objeto global, pero el valor de ambos puede sobrescribirse.

La sutil diferencia entre las dos formas es que cuando el Instanciación de variables proceso ejecuta (antes de la ejecución del código real) todos los identificadores declarados con var será inicializado con undefinedy los que usa el FunctionDeclarationEstará disponible desde ese momento, por ejemplo:

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

La asignación de la bar  FunctionExpression tiene lugar hasta el tiempo de ejecución.

Una propiedad global creada por un FunctionDeclaration puede sobrescribirse sin ningún problema, al igual que un valor variable, por ejemplo:

 function test () {}
 test = null;

Otra diferencia obvia entre los dos ejemplos es que la primera función no tiene un nombre, pero la segunda lo tiene, lo que puede ser realmente útil al depurar (es decir, inspeccionar una pila de llamadas).

Acerca de su primer ejemplo editado (foo = function() { alert('hello!'); };), es una tarea no declarada, le recomiendo que use siempre var palabra clave.

Con una tarea, sin el var declaración, si el identificador de referencia no se encuentra en la cadena de alcance, se convertirá en un borrable propiedad del objeto global.

Además, las asignaciones no declaradas arrojan un ReferenceError en ECMAScript 5 bajo Modo estricto.

Una lectura obligada:

Nota: Esta respuesta se ha fusionado de otra pregunta, en el que la mayor duda y error de OP era que los identificadores declarados con FunctionDeclaration, no se pudo sobrescribir, que no es el caso.


133
2017-08-08 19:32



Los dos fragmentos de código que ha publicado allí, para casi todos los fines, se comportarán de la misma manera.

Sin embargo, la diferencia en el comportamiento es que con la primera variante (var functionOne = function() {}), esa función solo se puede llamar después de ese punto en el código.

Con la segunda variante (function functionTwo()), la función está disponible para el código que se ejecuta arriba donde se declara la función.

Esto se debe a que con la primera variante, la función se asigna a la variable foo en tiempo de ejecución. En el segundo, la función se asigna a ese identificador, foo, en el tiempo de análisis.

Más información técnica

JavaScript tiene tres formas de definir funciones.

  1. Tu primer fragmento muestra un expresión de función. Esto implica usar el operador "función" para crear una función: el resultado de ese operador se puede almacenar en cualquier variable o propiedad del objeto. La expresión de la función es poderosa de esa manera. La expresión de función a menudo se llama una "función anónima", porque no tiene que tener un nombre,
  2. Tu segundo ejemplo es un declaración de función. Esto usa el declaración "función" para crear una función La función está disponible en el momento de análisis y se puede llamar a cualquier parte de ese alcance. Todavía puede almacenarlo en una propiedad de variable o objeto más adelante.
  3. La tercera forma de definir una función es la "Function ()" constructor, que no se muestra en tu publicación original. No se recomienda usar esto ya que funciona de la misma manera que eval(), que tiene sus problemas

111
2018-04-20 04:54



Una mejor explicación para La respuesta de Greg

functionTwo();
function functionTwo() {
}

¿Por qué no hay error? Siempre nos enseñaron que las expresiones se ejecutan de arriba a abajo (??)

Porque:

Las declaraciones de funciones y las declaraciones de variables siempre se mueven (hoisted) invisiblemente a la parte superior de su ámbito de contenido por el intérprete de JavaScript. Los parámetros de función y los nombres definidos en el lenguaje obviamente ya están allí. Ben Cherry

Esto significa que código como este:

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

Tenga en cuenta que la parte de asignación de las declaraciones no fueron izadas. Solo el nombre es izado.

Pero en el caso de las declaraciones de funciones, todo el cuerpo de la función también se izará:

functionTwo();              ---------------      function functionTwo() {
                            | is actually |      };
function functionTwo() {    | interpreted |-->
}                           |    like     |      functionTwo();
                            ---------------

91
2017-08-09 02:45



Otros comentaristas ya han cubierto la diferencia semántica de las dos variantes anteriores. Quería notar una diferencia estilística: solo la variación de "asignación" puede establecer una propiedad de otro objeto.

A menudo construyo módulos de JavaScript con un patrón como este:

(function(){
    var exports = {};

    function privateUtil() {
            ...
    }

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

    return exports;
})();

Con este patrón, sus funciones públicas usarán asignación, mientras que sus funciones privadas usarán declaración.

(Tenga en cuenta también que la asignación debe requerir un punto y coma después de la declaración, mientras que la declaración lo prohíbe).


83
2018-03-03 19:19



Una ilustración de cuándo preferir el primer método al segundo es cuando necesita evitar anular las definiciones previas de una función.

Con

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

, esta definición de myfunction anulará cualquier definición anterior, ya que se realizará en tiempo de análisis.

Mientras

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

hace el trabajo correcto de definir myfunction sólo cuando condition se cumple.


68
2018-03-29 13:26



Una razón importante es agregar una y solo una variable como la "Raíz" de su espacio de nombres ...

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

}

o

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

Hay muchas técnicas para el espaciado de nombres. Se ha vuelto más importante con la gran cantidad de módulos de JavaScript disponibles.

Ver también ¿Cómo declaro un espacio de nombres en JavaScript?


55
2017-08-08 19:44