Ordine delle funzioni JavaScript: perché è importante?


106

Domanda originale:

JSHint si lamenta quando il mio JavaScript chiama una funzione che è definita più in basso nella pagina rispetto alla chiamata ad essa. Tuttavia, la mia pagina è per un gioco e nessuna funzione viene chiamata fino a quando l'intera cosa non è stata scaricata. Allora perché le funzioni di ordine appaiono nel mio codice importante?

EDIT: Penso di aver trovato la risposta.

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

Gemo dentro. Sembra che debba passare UN ALTRO giorno a riordinare seimila righe di codice. La curva di apprendimento con javascript non è affatto ripida, ma è moooolto lunga.


+1 per l'eccellente riferimento nell'aggiornamento. E spero che questo ti convinca che non hai davvero bisogno di riordinare il tuo codice. :)
awm

Risposte:


294

tl; dr Se non chiami nulla finché non viene caricato tutto, dovresti stare bene.


Modifica: per una panoramica che copre anche alcune dichiarazioni ES6 ( let, const): https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Scope_Cheatsheet

Questo strano comportamento dipende da

  1. Come definire le funzioni e
  2. Quando li chiami.

Ecco alcuni esempi.

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

Ciò è dovuto a qualcosa chiamato sollevamento !

Esistono due modi per definire le funzioni: dichiarazione di funzione ed espressione di funzione . La differenza è fastidiosa e minuscola, quindi diciamo solo questa cosa leggermente sbagliata: se la scrivi come function name() {}, è una dichiarazione , e quando la scrivi come var name = function() {}(o una funzione anonima assegnata a un ritorno, cose del genere), è un'espressione di funzione .

Per prima cosa, diamo un'occhiata a come vengono gestite le variabili:

var foo = 42;

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

Ora, come vengono gestite le dichiarazioni di funzione :

var foo = 42;
function bar() {}

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

Le vardichiarazioni "gettano" la creazione di fooin alto, ma non le assegnano ancora il valore. La dichiarazione della funzione viene successiva nella riga e alla fine viene assegnato un valore foo.

E questo?

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

Solo la dichiarazione di fooviene spostata all'inizio. L'assegnazione avviene solo dopo che barè stata effettuata la chiamata a , dove si trovava prima che avvenisse tutto il sollevamento.

E infine, per concisione:

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

Ora, che dire delle espressioni di funzione ?

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

Proprio come le variabili regolari, prima fooviene dichiarato nel punto più alto dell'ambito, quindi gli viene assegnato un valore.

Vediamo perché il secondo esempio genera un errore.

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

Come abbiamo visto prima, fooviene sollevata solo la creazione di , l'assegnazione arriva dove appariva nel codice "originale" (non sollevato). Quando barviene chiamato, è prima che gli foovenga assegnato un valore, quindi foo === undefined. Ora nel corpo della funzione di bar, è come se lo stessi facendo undefined(), il che genera un errore.


Scusa se ho scoperto questo, ma sono stati sollevati sovraccarichi come Array.prototype.someMethod = function () {}? Mi sembra di ricevere errori se questi tipi di cose sono alla fine del mio script.
Edge

Come ti assicuri che tutto venga caricato ? Esiste una pratica comune?
zwcloud

6

Il motivo principale è probabilmente che JSLint fa solo un passaggio sul file in modo che non sa che si definisce tale funzione.

Se hai usato la sintassi dell'istruzione functions

function foo(){ ... }

In realtà non c'è alcuna differenza nel punto in cui si dichiara la funzione (si comporta sempre come se la dichiarazione fosse all'inizio).

D'altra parte, se la tua funzione è stata impostata come una variabile normale

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

Devi garantire che non lo chiamerai prima dell'inizializzazione (questo può effettivamente essere una fonte di bug).


Poiché riordinare tonnellate di codice è complicato e può essere una fonte di bug in sé, ti suggerirei di cercare una soluzione alternativa. Sono abbastanza sicuro che puoi dire a JSLint il nome delle variabili globali in anticipo in modo che non si lamenti di cose non dichiarate.

Metti un commento all'inizio del file

/*globals foo1 foo2 foo3*/

Oppure puoi usare una casella di testo lì per quello. (Penso anche che tu possa passare questo negli argomenti alla funzione jslint interna se puoi immischiarti con esso.)


Grazie. Quindi la riga / * globals * / funzionerà? Bene, qualsiasi cosa per far piacere a JsHint. Sono ancora nuovo in JavaScript e ottengo pause inspiegabili quando aggiorno una pagina, ma nessun bug segnalato. Quindi ho pensato che la soluzione fosse giocare secondo tutte le regole e poi vedere se succede ancora.
Chris Tolworthy,

4

Ci sono troppe persone che spingono regole arbitrarie su come dovrebbe essere scritto JavaScript. La maggior parte delle regole sono assolutamente spazzatura.

La funzione di sollevamento è una funzionalità di JavaScript perché è una buona idea.

Quando si ha una funzione interna che spesso è l'utilità delle funzioni interne, aggiungerla all'inizio della funzione esterna è uno stile accettabile di scrittura del codice, ma ha lo svantaggio di dover leggere i dettagli per arrivare a cosa la funzione esterna fa.

Dovresti attenersi a un principio in tutta la base del codice o mettere le funzioni private per prime o per ultime nel modulo o nella funzione. JSHint è utile per rafforzare la coerenza, ma dovresti ASSOLUTAMENTE regolare il .jshintrc in base alle tue esigenze, NON adattare il tuo codice sorgente a concetti di codifica stravaganti di altre persone.

Uno stile di codifica che potresti vedere in natura dovresti evitare perché non ti offre vantaggi e solo possibili problemi di refactoring:

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

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

Questa è esattamente la funzione di sollevamento da evitare. Basta imparare la lingua e sfruttare i suoi punti di forza.


2

Viene sollevata solo la dichiarazione di funzione, non l'espressione di funzione (assegnazione).

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.