¿Por qué entran en conflicto los nombres de mis funciones de JavaScript?

97

Escribí el siguiente script solo para ver qué sucede cuando una variable y una función que tiene una función asignada tienen sus nombres en conflicto:

var f = function() {
    console.log("Me original.");
}

function f() {
    console.log("Me duplicate.");
}

f();

El resultado que obtengo es "Yo original". ¿Por qué no se llamó a la otra función?

Además, si cambio mi asignación original a var f = new function() {, obtengo "Me original", seguido de un mensaje TypeError object is not a function. ¿Alguien puede explicarme?

ankush981
fuente
26
@ Dean.DePue - No hay confusión por parte de JavaScript. Las reglas para manejarlos son bastante claras (y Benjamin las explica en su respuesta).
Quentin
4
La curiosidad, sigue siendo la mejor forma de aprender un idioma. :-D
Cerbrus
2
Además, imagino que es bastante imposible que algo tan inmaterial como "JavaScript" se "sienta" confundido (o cualquier emoción, para el caso) ;-)
Cerbrus
2
¿Por qué la elevación debería invertir el orden en el segundo ejemplo?
Cerbrus
5
Pasos para crecer en el conocimiento de javascript: 1) Use 'use estricto' 2) Use siempre jslint o jshint 3) Busque las cosas de las que jslint o jshint se quejan 4) Enjuague y repita
steve-er-rino

Respuestas:

170

Las declaraciones de funciones se elevan (se mueven a la parte superior) en JavaScript. Si bien es incorrecto en términos de orden de análisis, el código que tiene es semánticamente el mismo que el siguiente, ya que las declaraciones de función se elevan:

function f() {
    console.log("Me duplicate.");
}
var f = function() {
    console.log("Me original.");
}


f();

Que a su vez, con la excepción del nombre de la función es el mismo que:

var f = function() {
    console.log("Me duplicate.");
}
var f = function() {
    console.log("Me original.");
}


f();

Lo que a su vez, debido a la elevación variable, es lo mismo que:

var f;
f = function() {
    console.log("Me duplicate.");
}
f = function() {
    console.log("Me original.");
}

f();

Lo que explica lo que está obteniendo, está anulando la función. De manera más general, varse permiten múltiples declaraciones en JavaScript, var x = 3; var x = 5es perfectamente legal. En el nuevo estándar ECMAScript 6, las letdeclaraciones lo prohíben.

Este artículo de @kangax hace un trabajo fantástico al desmitificar funciones en javascript

Benjamin Gruenbaum
fuente
2
Se puede realmente simplificar function f()al var f = function()tanto? ¿Son realmente los nombres de elevación y función la única diferencia?
djechlin
6
@djechlin en el contexto de esta pregunta, sí. Por lo general, es más sutil: consulte stackoverflow.com/questions/336859/… . Desde la perspectiva del compilador, son diferentes, pero desde la perspectiva del programador, estamos lo suficientemente cerca para afirmar eso. Es por eso que agregué ese largo "aunque es incorrecto en términos de orden de análisis, el código que tiene es semánticamente el mismo que" en lugar de decir "es el mismo que". Buen punto.
Benjamin Gruenbaum
1
@dotslash, por favor, no edite su pregunta original y no la cambie, eso se considera de mala educación aquí; además, mezclar varias preguntas en una también se considera de mala educación aquí. En su lugar, puede hacer una nueva pregunta o, si cree que es demasiado menor, pedir una aclaración en los comentarios (para eso son de todos modos). En el código anterior, ambas versiones de fse levantan, y la "Me Original"versión simplemente se levanta más tarde , cada una se mueve a la parte superior pero en el mismo orden. Solo me gustaría agregar que, en general, no debe nombrar varias funciones de la misma manera :)
Benjamin Gruenbaum
5
En modo estricto, no puede varel mismo nombre dos veces en el mismo ámbito.
Hoffmann
4
"Eso debería ser obvio" - quizás para usted , pero no fue obvio para mí en un momento, y no fue obvio para OP cuando lo preguntó, y nombrar, y más en general, cómo se administra el entorno léxico en JavaScript fue uno de las cosas más difíciles de comprender cuando aprendí JavaScript por primera vez. No me apresuraría a insultar a las personas que no lo entienden.
Benjamin Gruenbaum
10

Si no parece que nadie haya respondido su pregunta de seguimiento, la responderé aquí, aunque generalmente debe hacer preguntas de seguimiento como preguntas separadas.

Preguntaste por qué esto:

var f = new function() {
    console.log("Me original.");
}

function f() {
    console.log("Me duplicate.");
}

f();

imprime "Me original". y luego un error.

Lo que está sucediendo aquí es que newhace que la función se use como constructor. Entonces esto es equivalente a lo siguiente:

function myConstructor() {
    console.log("Me original.");
}
var f = new myConstructor();

function f() {
    console.log("Me duplicate.");
}

f();

Y gracias a la función izar que explicó Benjamin, lo anterior es esencialmente equivalente a esto:

var myConstructor = function() {
    console.log("Me original.");
};
var f = function() {
    console.log("Me duplicate.");
};

f = new myConstructor();

f();

Esta expresión:

var f = new function() {
    console.log("Me original.");
}

hace que se construya y asigne un nuevo objeto f, utilizando una función anónima como constructor. "Yo original". se imprime a medida que se ejecuta el constructor. Pero el objeto que se construye no es en sí mismo una función, por lo que cuando finalmente se ejecuta:

f();

obtiene un error, porque fno es una función.

JLRishe
fuente
¡Oh maravilloso! ¡Muchas gracias por tomarse la molestia de responderla! :) :)
ankush981
2

Perdóname si esta es la forma incorrecta de abordar la adición de un punto. No he estado mucho por aquí y agradecería una dirección constructiva y / o críticas.

La respuesta de Benjamin aborda la pregunta del OP de manera excelente, pero me gustaría agregar un ajuste que nos dará un recorrido completo por el izado y sus rarezas.

Si comenzamos el código original con una llamada a f, así:

f();

var f = function() {
   console.log("Me original.");
};

function f() {
   console.log("Me duplicate.");
}

f();

La salida será entonces:

Me duplicate.
Me original.

La razón es que las declaraciones vary functionse expresan de maneras ligeramente diferentes.

Porque varla declaración se mueve a la parte superior del alcance actual *, pero no se eleva ninguna asignación . En lo que respecta al valor de la var declarada, no está definido hasta que se alcanza la línea de asignación original.

Para las functiondeclaraciones , se incluyen tanto la declaración como la definición. Las expresiones de función , tal como se utilizan en la var f = function() {...construcción, no se elevan.

Entonces, después de izar, la ejecución es como si el código fuera:

var f; // declares var f, but does not assign it.

// name and define function f, shadowing the variable
function f() { 
  console.log("Me duplicate.");
}

// call the currently defined function f
f(); 

// assigns the result of a function expression to the var f,
// which shadows the hoisted function definition once past this point lexically
f = function() { 
  console.log("Me original."); 
}

// calls the function referenced by the var f
f();

* Todo el alcance de JavaScript es léxico, o de función, pero parece que confundir las cosas con usar la palabra f en ese punto.

Codelahoma
fuente