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

6876

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

El desarrollador anterior usó dos formas de declarar funciones y no puedo determinar 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 usar 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?

Richard Garside
fuente
199
permadi.com/tutorial/jsFunc/index.html es una muy buena página sobre las funciones de JavaScript
uzay95
68
Relacionado está este excelente artículo sobre Expresiones de funciones con nombre .
Phrogz
13
@CMS hace referencia a este artículo: kangax.github.com/nfe/#expr-vs-decl
Upperstage
107
Hay dos cosas que debe tener en cuenta: # 1 En JavaScript, se levantan las declaraciones. Significado que se var a = 1; var b = 2;convierte var a; var b; a = 1; b = 2. Entonces, cuando declara functionOne, se declara pero su valor no se establece de inmediato. Mientras que como functionTwo es solo una declaración, se coloca en la parte superior del alcance. # 2 functionDos le permite acceder a la propiedad de nombre y eso ayuda mucho al intentar depurar algo.
xavierm02
65
Ah y por cierto, la sintaxis correcta es con un ";" después de la asignación y sin después de la declaración. Ej . function f(){}Vs. var f = function(){};
xavierm02

Respuestas:

5046

La diferencia es que functionOnees una expresión de función y, por lo tanto, solo se define cuando se alcanza esa línea, mientras que functionTwoes una declaración de función y se define tan pronto como se ejecuta su función o script circundante (debido a la elevación ).

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!");
}

Históricamente, las declaraciones de funciones definidas dentro de los bloques se manejaban de manera inconsistente entre los navegadores. El modo estricto (introducido en ES5) resolvió esto mediante el alcance de las declaraciones de funciones en su bloque de inclusión.

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

Greg
fuente
633
@Greg: Por cierto, la diferencia no es solo que se analizan en diferentes momentos. Esencialmente, su functionOnees simplemente una variable que tiene asignada una función anónima, mientras functionTwoque en realidad es una función con nombre. Llama .toString()a ambos para ver la diferencia. Esto es significativo en algunos casos en los que desea obtener el nombre de una función mediante programación.
Jason Bunting
66
@Jason Bunting ... no estoy seguro de lo que está obteniendo aquí, .toString () parece devolver esencialmente el mismo valor (la definición de función) para ambos: cl.ly/2a2C2Y1r0J451o0q0B1B
Jon z
125
Los dos son diferentes. El primero es un function expressionel segundo es un function declaration. Puede leer más sobre el tema aquí: javascriptweblog.wordpress.com/2010/07/06/…
Michal Kuklis
128
@Greg La parte de su respuesta con respecto al tiempo de análisis versus el tiempo de ejecución no es correcta. En JavaScript, las declaraciones de funciones no se definen durante el tiempo de análisis, sino durante el tiempo de ejecución. El proceso es el siguiente: se analiza el código fuente -> se evalúa el programa JavaScript -> Se inicializa el contexto de ejecución global -> se realiza la creación de instancias de enlace de declaración. Durante este proceso, las declaraciones de funciones se instancian (consulte el paso 5 del Capítulo 10.5 ).
Šime Vidas
103
La terminología de este fenómeno se conoce como elevación.
Colin Pear
1944

Primero quiero corregir a Greg: también function abc(){}tiene un alcance: el nombre abcse define en el ámbito 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(){};

xyzse definirá como de costumbre, abcno está definido en todos los navegadores, pero 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 xyzy abcson 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 es compatible con Internet Explorer ). Básicamente cuando define una función como

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

Su nombre se asigna 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 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 arguments.callee, que todavía es relativamente largo y no es compatible 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 una returndeclaració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 función frente a la expresión de función es la verdadera razón por la que Greg demuestra una diferencia

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 manera 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 abcdefiní en ninguna parte de la cadena de ámbitos. 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.

Eugene Lazutkin
fuente
70
Me refiero a RoBorg pero no se lo encuentra en ninguna parte. Simple: RoBorg === Greg. Así es como se puede reescribir la historia en la era de internet. ;-)
Eugene Lazutkin el
10
var xyz = función abc () {}; console.log (xyz === abc); Todos los navegadores que he probado (Safari 4, Firefox 3.5.5, Opera 10.10) me dan "Variable indefinida: abc".
NVI
77
En general, creo que esta publicación hace un buen trabajo al explicar las diferencias y las ventajas de utilizar la declaración de función. Estaré de acuerdo en estar en desacuerdo en cuanto a los beneficios de utilizar las asignaciones de expresión de función a una variable, especialmente porque el "beneficio" parece ser una invocación de declarar una entidad global ... y todos saben que no debe saturar el espacio de nombres global , ¿derecho? ;-)
natlee75
84
Una gran razón para usar la función con nombre es porque los depuradores pueden usar el nombre para ayudarlo a dar sentido a su pila de llamadas o seguimiento de la pila. apesta cuando miras la pila de llamadas y ves "función anónima" con 10 niveles de profundidad ...
cabra
3
var abc = function(){}; console.log(abc.name);no produce ""más, sino en su "abc"lugar.
Qwerty
633

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

  • functionExpresión "anónima" (que a pesar del término, a veces crea funciones con nombres)

  • functionExpresió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 ( xen 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:

  1. Si en modo suelto no en un navegador web, se supone que el motor de JavaScript debe hacer una cosa
  2. Si está en modo suelto en un navegador web, se supone que el motor de JavaScript debe hacer otra cosa
  3. 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)

functionExpresió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 SetFunctionNameen la especificación  ; aparece en todas partes ).

Llamado functionExpresió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 ( wen 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.definePropertiesy 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 * 2cosa escondida en la map()llamada? Esa es una funcion.

Un par de cosas sobre las funciones de flecha:

  1. No tienen los suyos this. En su lugar, se cierran sobre el thisdel contexto en el que están definidas. (También cierran argumentsy, cuando corresponde,super ) Esto significa que el thisinterior de ellos es el mismo que el lugar thisdonde se crearon y no se puede cambiar.

  2. 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#mappasa 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) valueOfusando la sintaxis del método, podría usarlo super.valueOf()para obtener el valor Object.prototype.valueOfque 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.assigncomo 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 classsintaxis, 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.

TJ Crowder
fuente
3
entonces el nombre wsimplemente se ignora?
BiAiB
8
@PellePenna: los nombres de funciones son útiles para muchas cosas. Los dos grandes en mi opinión son la recursividad, y el nombre de la función que se muestra en las pilas de llamadas, rastreos de excepción, etc.
TJ Crowder
44
@ChaimEliyah: "Aceptar no significa que sea la mejor respuesta, solo significa que funcionó para la persona que preguntó". fuente
ScrapCode
66
@AR: Muy cierto. Sin embargo, de manera divertida, justo encima de eso dice "Las mejores respuestas aparecen primero para que siempre sean fáciles de encontrar". Dado que la respuesta aceptada aparece primero incluso en las respuestas con más votos, la gira podría ser algo contradictoria. ;-) También un poco impreciso, si determinamos "mejor" por votos (que no es confiable, es lo que tenemos), las respuestas "mejores" solo aparecen primero si está usando la pestaña "Votos" - de lo contrario, las respuestas que son primero son las activas o las más antiguas.
TJ Crowder
1
@TJCrowder: De acuerdo. 'organizado por fecha' a veces es molesto.
ScrapCode
145

Hablando sobre el contexto global, ambos, la vardeclaración y una FunctionDeclarational final crearán una propiedad no borrable en el objeto global, pero el valor de ambos puede sobrescribirse .

La sutil diferencia entre las dos formas es que cuando se ejecuta el proceso de instanciación variable (antes de la ejecución del código real) todos los identificadores declarados con varse inicializarán undefined, y los utilizados por los FunctionDeclaration's estarán disponibles 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 FunctionExpressiontiene lugar hasta el tiempo de ejecución.

Una propiedad global creada por a FunctionDeclarationse puede sobrescribir sin problemas, como un valor variable, por ejemplo:

 function test () {}
 test = null;

Otra diferencia obvia entre sus 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 la varpalabra clave.

Con una asignación, sin la vardeclaración, si el identificador referenciado no se encuentra en la cadena de alcance, se convertirá en una propiedad eliminable del objeto global.

Además, las tareas no declaradas arrojan un ReferenceErrorECMAScript 5 en modo estricto .

Una lectura obligada:

Nota : Esta respuesta se ha fusionado de otra pregunta , en la que la principal duda y la idea errónea del OP fue que los identificadores declarados con un FunctionDeclarationno podían sobrescribirse, lo que no es el caso.

CMS
fuente
¡No sabía que las funciones podrían sobrescribirse en JavaScript! Además, ese pedido de análisis es el gran punto de venta para mí. Supongo que necesito ver cómo creo funciones.
Xeoncross
2
+0 al artículo "Expresiones de funciones de nombres desmitificadas", ya que es 404ing. ¿Espejo posible ?: kangax.github.com/nfe
Mr_Chimp
@CMS Nice one. Tenga en cuenta que nunca vi el original, así que no sé si es un espejo o simplemente otro artículo con el mismo título.
Mr_Chimp
@Mr_Chimp Estoy bastante seguro de que es así, thewaybackmachine dice que obtuvo un 302 al momento del rastreo y que la redirección fue al enlace que proporcionó.
John
125

Los dos fragmentos de código que ha publicado allí, para casi todos los propósitos, 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 fooen tiempo de ejecución. En el segundo, la función se asigna a ese identificador foo, en tiempo de análisis.

Más información técnica

JavaScript tiene tres formas de definir funciones.

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

Una mejor explicación a 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:

El hoistedintérprete de JavaScript siempre mueve ( ) invisiblemente las declaraciones de función y las declaraciones de variable a la parte superior de su ámbito de contención. Los parámetros de función y los nombres definidos por el idioma ya están, obviamente, ahí. ben cherry

Esto significa que un código como este:

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

Observe que la parte de asignación de las declaraciones no se levantó. Solo se alza el nombre.

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

functionTwo();              ---------------      function functionTwo() {
                            | is actually |      };
function functionTwo() {    | interpreted |-->
}                           |    like     |      functionTwo();
                            ---------------
suhailvs
fuente
Hola suhail gracias por una información clara sobre el tema de la función. Ahora mi pregunta es ¿cuál será la primera declaración en la jerarquía de declaración, ya sea declaración de variable (functionOne) o declaración de función (functionTwo)?
Sharathi RB
91

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 JavaScript con un patrón como este:

(function(){
    var exports = {};

    function privateUtil() {
            ...
    }

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

    return exports;
})();

Con este patrón, todas sus funciones públicas usarán la asignación, mientras que sus funciones privadas usarán la 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).

Sean McMillan
fuente
44
yuiblog.com/blog/2007/06/12/module-pattern es la referencia primordial para el patrón del módulo, por lo que puedo decir. (Aunque ese artículo usa la var foo = function(){...}sintaxis incluso para variables privadas.
Sean McMillan
Esto no es del todo cierto en algunas versiones anteriores de IE, en realidad. ( function window.onload() {}fue una cosa.)
Ry-
77

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

Con

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

, esta definición de myfunctionanulará 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 myfunctionsolo cuando conditionse cumple.

Mbengue Assane
fuente
1
Este ejemplo es bueno y está cerca de la perfección, pero podría mejorarse. el mejor ejemplo sería definir var myFunc = null;fuera de un bucle o fuera de un bloque if / elseif / else. Entonces puede asignar condicionalmente diferentes funciones a la misma variable. En JS, es una convención mejor asignar un valor faltante a nulo, luego a indefinido. Por lo tanto, debe declarar myFunction como nulo primero y luego asignarlo condicionalmente.
Alexander Mills
62

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() {
  },
  ...
}

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

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

Robar
fuente
3
Parece que esta respuesta se fusionó con esta pregunta de otra pregunta, y la redacción podría parecer un poco ajena a esto. pregunta. ¿Consideraría editar la respuesta para que parezca más dirigida específicamente a esta pregunta? (para reiterar; esto no es tu culpa en absoluto ... solo un efecto secundario de una pregunta combinada). También puedes eliminarlo, y creo que mantendrás tu reputación. O puedes dejarlo; Como es viejo, puede que no haga una gran diferencia.
Andrew Barber
55

La elevación es la acción del intérprete de JavaScript de mover todas las declaraciones de variables y funciones a la parte superior del alcance actual.

Sin embargo, solo se levantan las declaraciones reales. dejando las tareas donde están.

  • Las variables / funciones declaradas dentro de la página son globales y pueden acceder a cualquier parte de esa página.
  • Las variables / funciones declaradas dentro de la función tienen alcance local. significa que están disponibles / accedidos dentro del cuerpo de la función (ámbito), no están disponibles fuera del cuerpo de la función.

Variable

Javascript se llama lenguaje de escritura libre. Lo que significa que las variables Javascript pueden contener valores de cualquier tipo de datos . Javascript se encarga automáticamente de cambiar el tipo de variable en función del valor / literal proporcionado durante el tiempo de ejecución.

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

Función

function Identifier_opt ( FormalParameterList_opt ) { 
      FunctionBody | sequence of statements

      « return;  Default undefined
      « return 'some data';
}
  • Las funciones declaradas dentro de la página se elevan a la parte superior de la página con acceso global.
  • Las funciones declaradas dentro del bloque de funciones se elevan a la parte superior del bloque.
  • El valor de retorno predeterminado de la función es ' undefined ', el valor predeterminado de la declaración de variables también 'undefined'

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

Declaración de funciones

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

Expresión de funciones

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

Función asignada a la variable Ejemplo:

(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();

Puede verificar la declaración de la función, la prueba de expresión en diferentes navegadores utilizando jsperf Test Runner


Clases de funciones del constructor ES5 : objetos de función creados con Function.prototype.bind

JavaScript trata las funciones como objetos de primera clase, por lo que al ser un objeto, puede asignar propiedades a una función.

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

ES6 introdujo la función de flecha : una expresión de función de flecha tiene una sintaxis más corta, son más adecuadas para funciones que no son de método y no pueden usarse como constructores.

ArrowFunction : ArrowParameters => ConciseBody.

const fn = (item) => { return item & 1 ? 'Odd' : 'Even'; };
console.log( fn(2) ); // Even
console.log( fn(3) ); // Odd
Yash
fuente
3
ahm, tu respuesta ... ¿no es ambigua? bien escrito, así que +1 para gastar y escribir demasiada información.
Danés
40

Estoy agregando mi propia respuesta solo porque todos los demás han cubierto completamente la parte de elevación.

Me he preguntado qué camino es mejor durante mucho tiempo, y gracias a http://jsperf.com ahora lo sé :)

ingrese la descripción de la imagen aquí

Las declaraciones de funciones son más rápidas, y eso es lo que realmente importa en el desarrollo web, ¿verdad? ;)

Leon Gaban
fuente
8
Yo diría que la mantenibilidad es el aspecto más importante de la mayoría del código. El rendimiento es importante, pero en la mayoría de los casos es probable que IO sea un cuello de botella más grande que la forma en que define sus funciones. Sin embargo, hay algunos problemas en los que necesita todo el rendimiento que puede obtener y esto es útil en esos casos. También es bueno tener una respuesta aquí que responda claramente una parte bien definida de la pregunta.
Richard Garside
3
Bueno, descubrí que era al revés con Firefox. jsperf.com/sandytest
Sandeep Nayak
Solo una actualización, dado que ahora he utilizado un estilo de programación funcional completo en JavaScript, nunca uso declaraciones, solo expresiones de función para poder encadenar y llamar a mis funciones por sus nombres de variables. Echa un vistazo a RamdaJS ...
Leon Gaban
1
@SandeepNayak Acabo de ejecutar su propia prueba en Firefox 50.0.0 / Windows 7 0.0.0, y en realidad es de la misma manera que la de Leon. Entonces, si su prueba es correcta, concluiría que las pruebas de jsperf no son indicativas, y todo depende de su navegador y / o versión del sistema operativo, o del estado particular de la máquina actual en ese momento en particular.
ocramot
33

Una declaración de función y una expresión de función asignada a una variable se comportan igual una vez que se establece el enlace.

Sin embargo, hay una diferencia en cómo y cuándo el objeto de función está realmente asociado con su variable. Esta diferencia se debe al mecanismo llamado elevación variable en JavaScript.

Básicamente, todas las declaraciones de funciones y declaraciones de variables se elevan a la parte superior de la función en la que se produce la declaración (es por eso que decimos que JavaScript tiene un alcance de función ).

  • Cuando se levanta una declaración de función, el cuerpo de la función "sigue", de modo que cuando se evalúa el cuerpo de la función, la variable se vinculará inmediatamente a un objeto de función.

  • Cuando se levanta una declaración de variable, la inicialización no sigue, sino que se "deja atrás". La variable se inicializa al undefinedcomienzo del cuerpo de la función y se le asignará un valor en su ubicación original en el código. (En realidad, se le asignará un valor en cada ubicación donde se produzca una declaración de una variable con el mismo nombre).

El orden de elevación también es importante: las declaraciones de funciones tienen prioridad sobre las declaraciones de variables con el mismo nombre, y la última declaración de funciones tiene prioridad sobre las declaraciones de funciones anteriores con el mismo nombre.

Algunos ejemplos...

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

La variable foose eleva a la parte superior de la función, se inicializa a undefined, !fooes decir true, foose asigna 10. El fooexterior debar alcance no juega ningún papel y está intacto.

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

Las declaraciones de funciones tienen prioridad sobre las declaraciones de variables, y la última declaración de funciones se "pega".

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

En este ejemplo, ase inicializa con el objeto de función resultante de evaluar la segunda declaración de función, y luego se le asigna 4.

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

Aquí la declaración de la función se eleva primero, declarando e inicializando la variable a. A continuación, se asigna esta variable 10. En otras palabras: la asignación no se asigna a la variable externa a.

eljenso
fuente
3
Tienes una forma un poco extraña de colocar los frenos de cierre. ¿Eres un codificador de Python? Parece que intentas hacer que Javascript se parezca a Python. Me temo que es confuso para otras personas. Si tuviera que mantener su código JavaScript, primero lo dejaría pasar por una impresora bonita automática.
finalmente
1
Excelente post. Una 'función autoejecutable' o 'expresión de función invocada inmediatamente' debería ser lo suficientemente fácil de ver y su preferencia de estilo no debería restar valor a su publicación, que es precisa y resume perfectamente 'elevación'. +1
Ricalsen
32

El primer ejemplo es una declaración de función:

function abc(){}

El segundo ejemplo es una expresión de función:

var abc = function() {};

La principal diferencia es cómo se izan (levantan y declaran). En el primer ejemplo, se levanta toda la declaración de función. En el segundo ejemplo, solo se iza la var 'abc', su valor (la función) será indefinido y la función misma permanecerá en la posición en la que se declara.

Para hacerlo mas simple:

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

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

Para estudiar más sobre este tema, le recomiendo este enlace.

sla55er
fuente
1
Su ejemplo es el mismo que la respuesta principal
GôTô
La razón principal para publicar esta respuesta fue proporcionar el enlace en la parte inferior. Esta fue la pieza que me faltaba para comprender completamente la pregunta anterior.
sla55er
1
Es genial que quisieras compartir el enlace. Pero los enlaces a información adicional, en SO, deberían ser solo un comentario sobre la pregunta o su respuesta favorita. Es muy poco óptimo saturar una página larga y complicada como esta con información repetida solo para agregar un enlace útil al final. No, no obtendrá puntos de representante por proporcionar el enlace, pero ayudará a la comunidad.
XML
31

En términos de costo de mantenimiento del código, las funciones con nombre son más preferibles:

  • Independiente del lugar donde se declaran (pero aún limitado por el alcance).
  • Más resistente a errores como la inicialización condicional (aún puede anular si lo desea).
  • El código se vuelve más legible al asignar funciones locales por separado de la funcionalidad del alcance. Por lo general, en el ámbito de aplicación, la funcionalidad va primero, seguida de declaraciones de funciones locales.
  • En un depurador, verá claramente el nombre de la función en la pila de llamadas en lugar de una función "anónima / evaluada".

Sospecho que se siguen más PROS para funciones con nombre. Y lo que aparece como una ventaja de las funciones con nombre es una desventaja para las anónimas.

Históricamente, las funciones anónimas surgieron de la incapacidad de JavaScript como lenguaje para enumerar miembros con funciones con nombre:

{
    member:function() { /* How do I make "this.member" a named function? */
    }
}
Sasha Firsov
fuente
2
Hay una prueba para confirmar: blog.firsov.net/2010/01/… Prueba de rendimiento JS - alcance y funciones con nombre - Análisis
Sasha Firsov
25

En términos de informática, 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á vinculada a un nombre, de ahí el nombre de la función anónima. En JavaScript es un objeto de primera clase declarado dinámicamente en tiempo de ejecución.

Para obtener más información sobre funciones anónimas y cálculo lambda, Wikipedia es un buen comienzo ( http://en.wikipedia.org/wiki/Anonymous_function ).

Kafka
fuente
A partir de ES2015 (seis años y medio después de que se publicó su respuesta), se nombran ambas funciones en la pregunta.
TJ Crowder
25

Utilizo el enfoque variable en mi código por una razón muy específica, cuya teoría se ha cubierto de manera abstracta anteriormente, pero un ejemplo podría ayudar a algunas personas como yo, con experiencia limitada en JavaScript.

Tengo un código que necesito ejecutar con 160 marcas diseñadas de forma independiente. La mayor parte del código está en archivos compartidos, pero las cosas específicas de la marca están en un archivo separado, uno para cada marca.

Algunas marcas requieren funciones específicas, y otras no. A veces tengo que agregar nuevas funciones para hacer nuevas cosas específicas de marca. Estoy feliz de cambiar el código compartido, pero no quiero tener que cambiar los 160 conjuntos de archivos de marca.

Al usar la sintaxis de la variable, puedo declarar la variable (esencialmente un puntero de función) en el código compartido y asignar una función de código auxiliar trivial o establecerla como nula.

Una o dos marcas que necesitan una implementación específica de la función pueden definir su versión de la función y asignarla a la variable si lo desean, y el resto no hace nada. Puedo probar una función nula antes de ejecutarla en el código compartido.

De los comentarios de las personas anteriores, deduzco que también es posible redefinir una función estática, pero creo que la solución variable es agradable y clara.

Herc
fuente
25

La respuesta de Greg es lo suficientemente buena, pero todavía me gustaría agregarle algo que aprendí hace un momento viendo Douglas Crockford's videos de .

Expresión de función:

var foo = function foo() {};

Declaración de función:

function foo() {};

La declaración de función es solo una abreviatura de vardeclaración con unfunction valor.

Entonces

function foo() {};

se expande a

var foo = function foo() {};

Que se expande aún más a:

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

Y ambos son izados en la parte superior del código.

Captura de pantalla del video

Rohan
fuente
77
Lo siento, pero esto es incorrecto: no sé lo que Crockford está tratando de decir en esa diapositiva. Tanto las declaraciones de función como las variables siempre se izan a la cima de su alcance. La diferencia es que las asignaciones de variables (ya sea que las esté asignando con una cadena, booleana o función) no se elevan a la parte superior, mientras que los cuerpos de función (mediante la declaración de función) sí lo están.
Thomas Heymann
Eche un vistazo a estos ejemplos de código: gist.github.com/cyberthom/36603fbc20de8e04fd09
Thomas Heymann
24

Hay cuatro comparaciones notables entre las dos declaraciones de funciones diferentes que se enumeran a continuación.

  1. Disponibilidad (alcance) de la función

Lo siguiente funciona porque function add()está limitado al bloque más cercano:

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

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

Lo siguiente no funciona porque se llama a la variable antes de asignar un valor de función a la variable add.

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

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

El código anterior es idéntico en funcionalidad al código siguiente. Tenga en cuenta que la asignación explícita add = undefinedes superflua porque simplemente hacer var add;es exactamente lo mismo 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;
}

Lo siguiente no funciona porque el var add=reemplaza al 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. (función) .name

El nombre de una función function thefuncname(){}es thefuncname cuando se declara de esta manera.

function foobar(a, b){}

console.log(foobar.name);

var a = function foobar(){};

console.log(a.name);

De lo contrario, si una función se declara como function(){}, la función .name es la primera variable utilizada para almacenar la función.

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

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

Si no hay variables establecidas para la función, entonces el nombre de las funciones es la cadena vacía ( "").

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

Por último, aunque la variable a la que se asigna la función establece inicialmente el nombre, las variables sucesivas establecidas en la función no cambian el nombre.

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

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

  1. Actuación

En Google V8 y Firemon's Spidermonkey puede haber una diferencia de compilación JIST de microsegundos, pero finalmente el resultado es exactamente el mismo. Para probar esto, examinemos la eficiencia de JSPerf en microbenchmarks comparando la velocidad de dos fragmentos de código en blanco. Las pruebas JSPerf se encuentran aquí . Y, las pruebas jsben.ch se encuentran aquí . Como puede ver, hay una diferencia notable cuando no debería haber ninguna. Si realmente eres un fanático del rendimiento como yo, entonces podría valer más la pena intentar reducir el número de variables y funciones en el alcance y especialmente eliminar el polimorfismo (como usar la misma variable para almacenar dos tipos diferentes).

  1. Mutabilidad Variable

Cuando utiliza la varpalabra clave para declarar una variable, puede reasignar un valor diferente a la variable de esta manera.

(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);
})();

Sin embargo, cuando usamos la declaración const, la referencia variable se vuelve inmutable. Esto significa que no podemos asignar un nuevo valor a la variable. Sin embargo, tenga en cuenta que esto no hace que el contenido de la variable sea inmutable: si lo hace const arr = [], aún puede hacerlo arr[10] = "example". Solo hacer algo como arr = "new value"o arr = []arrojaría un error como se ve a continuación.

(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, si declaramos la variable como function funcName(){}, entonces la inmutabilidad de la variable es la misma que declararla con 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);
})();

¿Cuál es el "bloque más cercano"

El "bloque más cercano" es la "función" más cercana (que incluye funciones asíncronas, funciones generadoras y funciones generadoras asíncronas). Sin embargo, curiosamente, a se function functionName() {}comporta como var functionName = function() {}cuando está en un bloque sin cierre para artículos fuera de dicho cierre. 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;
}

  • Función

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;
    }
})();

  • Declaración (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;
    }
}

  • Función de flecha con 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;
    }
})();

  • Función de flecha con 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
fuente
Esta merece ser la respuesta aceptada y más votada
Aaron John Sabu
18

@EugeneLazutkin da un ejemplo en el que nombra una función asignada para poder usarlashortcut() como referencia interna de sí misma. John Resig da otro ejemplo: copiar una función recursiva asignada a otro objeto en su Aprendizaje Javascript avanzado tutorial de aprendizaje. Si bien la asignación de funciones a propiedades no es estrictamente la pregunta aquí, recomiendo probar activamente el tutorial: ejecute el código haciendo clic en el botón en la esquina superior derecha y haga doble clic en el código para editarlo a su gusto.

Ejemplos del tutorial: llamadas recursivas en yell():

Las pruebas fallan cuando se elimina el objeto ninja original. (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?" ); 
}

Si nombra la función que se llamará recursivamente, las pruebas pasarán. (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
fuente
17

Otra diferencia que no se menciona en las otras respuestas es que si usa la función anónima

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

y usar eso como constructor como en

var one = new functionOne();

entonces one.constructor.nameno se definirá. Function.nameno es estándar, pero es compatible con Firefox, Chrome, otros navegadores derivados de Webkit e IE 9+.

Con

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

Es posible recuperar el nombre del constructor como una cadena con two.constructor.name.

Ingo Kegel
fuente
El nombre en el primer caso no se definirá porque es una función anónima asignada a una variable. Creo que la palabra anónimo se inventó para cosas que no tienen su nombre definido :)
Om Shankar
En este ejemplo, el dos = nuevo se convierte en una función global porque no hay var
Waqas Tahir
16

La primera (función doSomething (x)) debe ser parte de una notación de objeto.

El segundo ( var doSomething = function(x){ alert(x);}) es simplemente crear una función anónima y asignarla a una variable,doSomething . Entonces doSomething () llamará a la función.

Es posible que desee saber qué es una declaración de función y una expresión de función .

Una declaración de función define una variable de función nombrada sin requerir asignación de variable. Las declaraciones de función se producen como construcciones independientes y no se pueden anidar dentro de bloques que no son de función.

function foo() {
    return 3;
}

ECMA 5 (13.0) define la sintaxis como
Identificador de función (FormalParameterList opt ) {FunctionBody}

En la condición anterior, el nombre de la función es visible dentro de su alcance y el alcance de su padre (de lo contrario sería inalcanzable).

Y en una expresión de función

Una expresión de función define una función como parte de una sintaxis de expresión más grande (típicamente una asignación variable). Las funciones definidas mediante expresiones de funciones pueden ser nombradas o anónimas. Las expresiones de función no deben comenzar con "función".

// 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!");
})();

ECMA 5 (13.0) define la sintaxis como
identificador de función opt (FormalParameterList opt ) {FunctionBody}

NullPoiиteя
fuente
16

Estoy enumerando las diferencias a continuación:

  1. Se puede colocar una declaración de función en cualquier parte del código. Incluso si se invoca antes de que la definición aparezca en el código, se ejecuta a medida que la declaración de función se confirma en la memoria o de una manera que se levanta, antes de que cualquier otro código en la página comience la ejecución.

    Echa un vistazo a la función a continuación:

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

    Esto se debe a que, durante la ejecución, se ve así:

    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

    Una expresión de función, si no se define antes de llamarla, dará como resultado un error. Además, aquí la definición de la función en sí misma no se mueve a la parte superior ni se guarda en la memoria, como en las declaraciones de funciones. Pero la variable a la que asignamos la función se eleva y se le asigna indefinida .

    Misma función usando expresiones de función:

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

    Esto es porque durante la ejecución, se ve así:

    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. No es seguro escribir declaraciones de funciones en bloques que no son de función, como si fuera porque no serán accesibles.

    if (test) {
        function x() { doSomething(); }
    }
  3. Es posible que la expresión de función con nombre como la que se muestra a continuación no funcione en los navegadores Internet Explorer anteriores a la versión 9.

    var today = function today() {return new Date()}
varna
fuente
1
@Arjun ¿Cuál es el problema si se hizo una pregunta años antes? Una respuesta no solo beneficia al OP sino que potencialmente a todos los usuarios de SO, sin importar cuándo se formuló la pregunta. ¿Y qué hay de malo en responder preguntas que ya tienen una respuesta aceptada?
SantiBailors
1
@Arjun tienes que entender que responder viejas preguntas no está mal. Si fuera así, SO habría tenido tal barrera. Imagine que hay un cambio en la API (aunque no en el contexto de esta pregunta) y alguien lo detecta y proporciona una respuesta con la nueva API, ¿no debería permitirse eso? Hasta y a menos que la respuesta no tenga sentido y no pertenezca aquí, sería rechazada y eliminada automáticamente. ¡No necesitas molestarte!
Sudhansu Choudhary
15

Si usara esas funciones para crear objetos, obtendría:

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
fuente
Parece que no puedo reproducir esto. console.log(objectOne.__proto__);imprime "functionOne {}" en mi consola. ¿Alguna idea de por qué este puede ser el caso?
Mike
Parece que no puedo reproducirlo también.
daremkd
1
Esta es una capacidad de su depurador (para mostrar la "clase" del objeto registrado), y la mayoría de las personas pueden derivar un nombre incluso para expresiones de función anónimas en estos días. Por cierto, debe dejar en claro que no hay diferencia funcional entre las dos instancias.
Bergi
12

A la luz del argumento "las funciones nombradas aparecen en los rastros de la pila", los motores JavaScript modernos son realmente capaces de representar funciones anónimas.

Al escribir estas líneas, V8, SpiderMonkey, Chakra y Nitro siempre se refieren a las funciones nombradas por sus nombres. Casi siempre se refieren a una función anónima por su identificador si tiene una.

SpiderMonkey puede descubrir el nombre de una función anónima devuelta por otra función. El resto no puede.

Si realmente quisieras que tu iterador y las devoluciones de llamada exitosas aparecieran en la traza, también podrías nombrarlas ...

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

Pero en su mayor parte no vale la pena estresarse.

Arnés ( violín )

'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

Mono araña

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
fuente
12

En JavaScript hay dos formas de crear funciones:

  1. Declaración de función:

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

    Esto es muy básico, se explica por sí mismo, se usa en muchos lenguajes y es estándar en toda la familia de lenguajes C. Declaramos que una función la definió y la ejecutó al llamarla.

    Lo que debe saber es que las funciones son en realidad objetos en JavaScript; internamente hemos creado un objeto para la función anterior y le hemos dado un nombre llamado fn o la referencia al objeto se almacena en fn. Las funciones son objetos en JavaScript; una instancia de función es en realidad una instancia de objeto.

  2. Expresión de función:

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

    JavaScript tiene funciones de primera clase, es decir, crear una función y asignarla a una variable tal como crea una cadena o número y la asigna a una variable. Aquí, la variable fn se asigna a una función. La razón de este concepto es que las funciones son objetos en JavaScript; fn apunta a la instancia del objeto de la función anterior. Inicializamos una función y la asignamos a una variable. No está ejecutando la función y asignando el resultado.

Referencia: sintaxis de declaración de función de JavaScript: var fn = function () {} vs function fn () {}

Anoop Rai
fuente
1
¿Qué pasa con la tercera opción var fn = function fn() {...}?
chharvey
Hola Chharvey, no estoy seguro de tu pregunta, supongo que estás hablando de la expresión de función que ya he mencionado. Sin embargo, si todavía hay algo de confusión, simplemente sea más elaborado.
Anoop Rai el
Sí, estaba preguntando sobre una expresión de función con nombre . es similar a su opción # 2, excepto que la función tiene un identificador. generalmente este identificador es el mismo que la variable a la que se le asigna, pero ese no es siempre el caso.
chharvey
1
Sí La expresión de la función con nombre es similar a mi opción # 2. Bueno, tener un identificador no es obligatorio ya que no se usa. Siempre que ejecute la expresión de función, usará la variable que contiene el objeto de función. El identificador no sirve para nada.
Anoop Rai el
11

Ambas son formas diferentes de definir una función. La diferencia es cómo el navegador los interpreta y los carga en un contexto de ejecución.

El primer caso es de expresiones de función que se cargan solo cuando el intérprete alcanza esa línea de código. Entonces, si lo hace de la siguiente manera, obtendrá un error de que functionOne no es una función .

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

La razón es que en la primera línea no se asigna ningún valor a functionOne y, por lo tanto, no está definido. Estamos tratando de llamarlo como una función y, por lo tanto, estamos obteniendo un error.

En la segunda línea estamos asignando la referencia de una función anónima a functionOne.

El segundo caso es de declaraciones de funciones que se cargan antes de ejecutar cualquier código. Entonces, si le gusta lo siguiente, no obtendrá ningún error cuando la declaración se cargue antes de la ejecución del código.

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

Sobre el rendimiento:

Las nuevas versiones de V8introdujeron varias optimizaciones bajo el capó y así lo hicieron SpiderMonkey.

Casi no hay diferencia ahora entre expresión y declaración.
La expresión de la función parece ser más rápida ahora.

Chrome 62.0.3202 Prueba de cromo

FireFox 55 Prueba de Firefox

Chrome Canary 63.0.3225 Prueba de canario de cromo


AnonymousLas expresiones de función parecen tener un mejor rendimiento frente a la Namedexpresión de función.


Firefox Chrome Canary ChromeFirefox llamado_anónimo Chrome canary named_anonymous Chrome named_anonymous

Panos Kal.
fuente
1
Sí, esta diferencia es tan insignificante que, con suerte, los desarrolladores se preocuparán por qué enfoque es más fácil de mantener para sus necesidades específicas en lugar de cuál podría ser más rápido (obtendrá diferentes resultados de jsperf en cada intento dependiendo de lo que esté haciendo el navegador) la mayoría de las tareas de JavaScript no necesitan preocuparse por micro optimizaciones en este grado).
squidbe
@squidbe No hay diferencia. Mire aquí: jsperf.com/empty-tests-performance
Jack Giffin
10

Son bastante similares con algunas pequeñas diferencias, la primera es una variable que se asigna a una función anónima (Declaración de función) y la segunda es la forma normal de crear una función en JavaScript (Declaración de función anónima), ambas tienen uso, desventajas y ventajas. :

1. Expresión de funciones

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

Una expresión de función define una función como parte de una sintaxis de expresión más grande (típicamente una asignación variable). Las funciones definidas a través de las expresiones de funciones pueden ser nombradas o anónimas. Las expresiones de función no deben comenzar con "función" (de ahí los paréntesis alrededor del ejemplo de auto invocación a continuación).

Asignar una variable a una función, significa que no hay elevación, ya que sabemos que las funciones en JavaScript pueden elevar, significa que pueden llamarse antes de ser declaradas, mientras que las variables deben declararse antes de acceder a ellas, por lo que en este caso no podemos acceda a la función antes de que se declare, también podría ser una forma de escribir sus funciones, para las funciones que devuelven otra función, este tipo de declaración podría tener sentido, también en ECMA6 y arriba puede asignar esto a una función de flecha que se puede usar para llamar a funciones anónimas, también esta forma de declarar es una mejor manera de crear funciones de Constructor en JavaScript.

2. Declaración de funciones

function functionTwo() {
    // Some code
}

Una declaración de función define una variable de función nombrada sin requerir asignación de variable. Las declaraciones de función se producen como construcciones independientes y no se pueden anidar dentro de bloques que no son de función. Es útil pensar en ellos como hermanos de declaraciones variables. Así como las declaraciones de variables deben comenzar con "var", las declaraciones de funciones deben comenzar con "function".

Esta es la forma normal de llamar a una función en JavaScript, esta función se puede llamar incluso antes de declararla, ya que en JavaScript todas las funciones se alzan, pero si tiene 'uso estricto', esto no se alzará como se esperaba, es una buena manera para llamar a todas las funciones normales que no son grandes en líneas y tampoco son una función constructora.

Además, si necesita más información sobre cómo funciona la elevación en JavaScript, visite el siguiente enlace:

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

Alireza
fuente
1
...also this way of declaring is a better way to create Constructor functions in JavaScript, por favor explique, ¡tengo curiosidad!
Karl Morrison
Una razón es porque todas las funciones de Constructor incorporadas en JavaScript creadas como esta función Número () {[código nativo]} y no debe confundirse con las incorporadas, también es más seguro hacer referencia más adelante en este caso y usted termina código más ordenado pero sin usar el izado ...
Alireza
8

Estas son solo dos formas posibles de declarar funciones, y en la segunda forma, puede usar la función antes de la declaración.

Tao
fuente
7

new Function()se puede usar para pasar el cuerpo de la función en una cadena. Y, por lo tanto, esto puede usarse para crear funciones dinámicas. También pasa el guión sin ejecutar el guión.

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

secondFunction()
SuperNova
fuente
Si bien esto es bueno y cierto, ¿cómo se relaciona esto solo con la pregunta que se hace?
Jack Giffin