Orden de las funciones de JavaScript: ¿por qué es importante?

106

Pregunta original:

JSHint se queja cuando mi JavaScript llama a una función que se define más abajo en la página que la llamada a ella. Sin embargo, mi página es para un juego y no se llama a ninguna función hasta que se haya descargado todo. Entonces, ¿por qué son importantes las funciones de orden que aparecen en mi código?

EDITAR: Creo que he encontrado la respuesta.

http://www.adequatelygood.com/2010/2/JavaScript-Scoping-and-Hoisting

Estoy gimiendo por dentro. Parece que necesito pasar OTRO día reordenando seis mil líneas de código. La curva de aprendizaje con javascript no es empinada en absoluto, pero es muuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu

Chris Tolworthy
fuente
+1 por la excelente referencia en la actualización. Y espero que eso te convenza de que realmente no necesitas reordenar tu código. :)
awm

Respuestas:

294

tl; dr Si no está llamando a nada hasta que todo se cargue, debería estar bien.


Editar: Para obtener una descripción general que también cubre algunas declaraciones de ES6 ( let, const): https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Scope_Cheatsheet

Este comportamiento extraño depende de

  1. Cómo se definen las funciones y
  2. Cuando los llames.

He aquí algunos ejemplos.

bar(); //This won't throw an error
function bar() {}

foo(); //This will throw an error
var foo = function() {}
bar();
function bar() {
    foo(); //This will throw an error
}
var foo = function() {}
bar();
function bar() {
    foo(); //This _won't_ throw an error
}
function foo() {}
function bar() {
    foo(); //no error
}
var foo = function() {}
bar();

¡Esto se debe a algo llamado izar !

Hay dos formas de definir funciones: declaración de función y expresión de función . La diferencia es molesta y diminuta, así que digamos algo ligeramente incorrecto: si lo escribes como function name() {}, es una declaración , y cuando lo escribes como var name = function() {}(o una función anónima asignada a una devolución, cosas así), es una expresión de función .

Primero, veamos cómo se manejan las variables:

var foo = 42;

//the interpreter turns it into this:
var foo;
foo = 42;

Ahora, cómo se manejan las declaraciones de funciones :

var foo = 42;
function bar() {}

//turns into
var foo; //Insanity! It's now at the top
function bar() {}
foo = 42;

Las vardeclaraciones "arrojan" la creación de fooal principio, pero aún no le asignan el valor. La declaración de la función viene a continuación en la línea y finalmente se le asigna un valor foo.

¿Y que hay de esto?

bar();
var foo = 42;
function bar() {}
//=>
var foo;
function bar() {}
bar();
foo = 42;

Solo la declaración de foose mueve a la parte superior. La asignación viene solo después de que barse realiza la llamada a , donde estaba antes de que ocurriera todo el izado.

Y finalmente, por concisión:

bar();
function bar() {}
//turns to
function bar() {}
bar();

Ahora, ¿qué pasa con las expresiones de función ?

var foo = function() {}
foo();
//=>
var foo;
foo = function() {}
foo();

Al igual que las variables regulares, primero foose declara en el punto más alto del alcance, luego se le asigna un valor.

Veamos por qué el segundo ejemplo arroja un error.

bar();
function bar() {
    foo();
}
var foo = function() {}
//=>
var foo;
function bar() {
    foo();
}
bar();
foo = function() {}

Como hemos visto antes, sólo foose levanta la creación de , la asignación viene donde apareció en el código "original" (no-alzado). Cuando barse llama, es antes de que foose le asigne un valor, entonces foo === undefined. Ahora, en el cuerpo de función de bar, es como si lo estuviera haciendo undefined(), lo que arroja un error.

Zirak
fuente
Lamento desenterrar esto, pero ¿hay sobrecargas como Array.prototype.someMethod = function () {} izadas? Parece que recibo errores si este tipo de cosas están al final de mi script.
Edge
¿Cómo se asegura de que todo se cargue ? ¿Existe alguna práctica común?
zwcloud
6

La razón principal es, probablemente, que JSLint hace una sola pasada en el archivo por lo que no sabe que va a definir una función de este tipo.

Si utilizó la sintaxis de instrucciones de funciones

function foo(){ ... }

En realidad, no hay ninguna diferencia en el lugar donde declara la función (siempre se comporta como si la declaración estuviera al principio).

Por otro lado, si su función se configuró como una variable regular

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

Debe garantizar que no lo llamará antes de la inicialización (esto puede ser una fuente de errores).


Dado que reordenar toneladas de código es complicado y puede ser una fuente de errores en sí mismo, le sugiero que busque una solución. Estoy bastante seguro de que puede decirle a JSLint el nombre de las variables globales de antemano para que no se queje de cosas no declaradas.

Ponga un comentario al principio del archivo.

/*globals foo1 foo2 foo3*/

O puede usar un cuadro de texto allí para eso. (También creo que puede pasar esto en los argumentos a la función jslint interna si puede entrometerse con ella).

hugomg
fuente
Gracias. Entonces, ¿funcionará la línea / * globals * /? Bien, cualquier cosa para gustarle a JsHint. Todavía soy nuevo en JavaScript y obtengo pausas inexplicables cuando actualizo una página, pero no se informa de errores. Así que pensé que la solución era seguir todas las reglas y luego ver si todavía sucede.
Chris Tolworthy
4

Hay demasiadas personas que imponen reglas arbitrarias sobre cómo se debe escribir JavaScript. La mayoría de las reglas son una absoluta tontería.

La función de elevación es una característica de JavaScript porque es una buena idea.

Cuando tiene una función interna que a menudo es la utilidad de las funciones internas, agregarla al comienzo de la función externa es un estilo aceptable para escribir código, pero tiene el inconveniente de que debe leer los detalles para llegar a lo que la función externa lo hace.

Debe ceñirse a un principio a lo largo de su base de código, ya sea poner las funciones privadas primero o al final en su módulo o función. JSHint es bueno para hacer cumplir la coherencia, pero debe ABSOLUTAMENTE ajustar el .jshintrc para satisfacer sus necesidades, NO ajustar su código fuente a los conceptos de codificación extravagantes de otras personas.

Un estilo de codificación que puede ver en la naturaleza debe evitarlo porque no le brinda ventajas y solo puede causar problemas de refactorización:

function bigProcess() {
    var step1,step2;
    step1();
    step2();

    step1 = function() {...};
    step2 = function() {...};
}

Esta es exactamente la función que debe evitarse la elevación. Simplemente aprenda el idioma y aproveche sus puntos fuertes.

Henrik Vendelbo
fuente
2

Sólo la declaración de función se eleva, no la expresión de función (asignación).

Abhay Srivastav
fuente