Domanda

Mi sono chiesto, esiste una differenza di prestazioni tra l'utilizzo di funzioni denominate e funzioni anonime in Javascript?

for (var i = 0; i < 1000; ++i) {
    myObjects[i].onMyEvent = function() {
        // do something
    };
}

contro

function myEventHandler() {
    // do something
}

for (var i = 0; i < 1000; ++i) {
    myObjects[i].onMyEvent = myEventHandler;
}

Il primo è più ordinato poiché non ingombra il codice con funzioni usate raramente, ma è importante che tu dichiari nuovamente quella funzione più volte?

È stato utile?

Soluzione

Il problema di prestazioni qui è il costo della creazione di un nuovo oggetto funzione ad ogni iterazione del ciclo e non il fatto che utilizzi una funzione anonima:

for (var i = 0; i < 1000; ++i) {    
    myObjects[i].onMyEvent = function() {
        // do something    
    };
}

Stai creando un migliaio di oggetti funzione distinti anche se hanno lo stesso corpo di codice e nessun legame con l'ambito lessicale (chiusura).Il seguente sembra invece più veloce perché assegna semplicemente il file Stesso riferimento alla funzione agli elementi dell'array durante il ciclo:

function myEventHandler() {
    // do something
}

for (var i = 0; i < 1000; ++i) {
    myObjects[i].onMyEvent = myEventHandler;
}

Se dovessi creare la funzione anonima prima di entrare nel ciclo, quindi assegnarle solo i riferimenti agli elementi dell'array mentre sei all'interno del ciclo, scoprirai che non vi è alcuna differenza di prestazioni o semantica rispetto alla versione della funzione denominata:

var handler = function() {
    // do something    
};
for (var i = 0; i < 1000; ++i) {    
    myObjects[i].onMyEvent = handler;
}

In breve, non vi è alcun costo prestazionale osservabile nell'utilizzo di funzioni anonime rispetto a quelle denominate.

Per inciso, dall'alto potrebbe sembrare che non vi sia alcuna differenza tra:

function myEventHandler() { /* ... */ }

E:

var myEventHandler = function() { /* ... */ }

Il primo è a dichiarazione di funzione mentre quest'ultimo è un'assegnazione di variabile a una funzione anonima.Sebbene possano sembrare avere lo stesso effetto, JavaScript li tratta in modo leggermente diverso.Per capire la differenza, consiglio di leggere “Ambiguità nella dichiarazione della funzione JavaScript”.

Il tempo di esecuzione effettivo per qualsiasi approccio sarà in gran parte dettato dall'implementazione del compilatore e del runtime da parte del browser.Per un confronto completo delle prestazioni dei browser moderni, visitare il sito JS Perf

Altri suggerimenti

Ecco il mio codice di prova:

var dummyVar;
function test1() {
    for (var i = 0; i < 1000000; ++i) {
        dummyVar = myFunc;
    }
}

function test2() {
    for (var i = 0; i < 1000000; ++i) {
        dummyVar = function() {
            var x = 0;
            x++;
        };
    }
}

function myFunc() {
    var x = 0;
    x++;
}

document.onclick = function() {
    var start = new Date();
    test1();
    var mid = new Date();
    test2();
    var end = new Date();
    alert ("Test 1: " + (mid - start) + "\n Test 2: " + (end - mid));
}

I risultati:
Prova 1:142ms Test 2:1983ms

Sembra che il motore JS non riconosca che si tratta della stessa funzione in Test2 e la compila ogni volta.

Come principio generale di progettazione, dovresti evitare di implementare lo stesso codice più volte.Invece dovresti estrarre il codice comune in una funzione ed eseguire quella funzione (generale, ben testata, facile da modificare) da più posti.

Se (a differenza di quanto deduci dalla tua domanda) stai dichiarando la funzione interna una volta e utilizzando quel codice una volta (e non hai nient'altro identico nel tuo programma), allora una funzione anonima probabilmente (questa è un'ipotesi gente) viene trattata allo stesso modo dal compilatore come una normale funzione con nome.

È una funzionalità molto utile in casi specifici, ma non dovrebbe essere utilizzata in molte situazioni.

Non mi aspetto molta differenza, ma se ce n'è una probabilmente varierà in base al motore di scripting o al browser.

Se ritieni che il codice sia più facile da capire, le prestazioni non sono un problema a meno che non ti aspetti di chiamare la funzione milioni di volte.

Gli oggetti anonimi sono più veloci degli oggetti con nome.Ma chiamare più funzioni è più costoso e ad un livello tale da eclissare qualsiasi risparmio che potresti ottenere utilizzando funzioni anonime.Ogni funzione chiamata si aggiunge allo stack di chiamate, introducendo una quantità piccola ma non banale di sovraccarico.

Ma a meno che tu non stia scrivendo routine di crittografia/decrittografia o qualcosa di simile sensibile alle prestazioni, come molti altri hanno notato è sempre meglio ottimizzare per un codice elegante e di facile lettura rispetto a un codice veloce.

Supponendo che tu stia scrivendo un codice ben architettato, i problemi di velocità dovrebbero essere responsabilità di coloro che scrivono gli interpreti/compilatori.

Dove possiamo avere un impatto sulle prestazioni è nell'operazione di dichiarazione delle funzioni.Ecco un punto di riferimento per dichiarare le funzioni all'interno del contesto di un'altra funzione o all'esterno:

http://jsperf.com/function-context-benchmark

In Chrome l'operazione è più veloce se dichiariamo la funzione all'esterno, ma in Firefox è il contrario.

In un altro esempio vediamo che se la funzione interna non è una funzione pura, avrà una mancanza di prestazioni anche in Firefox:http://jsperf.com/function-context-benchmark-3

Ciò che renderà sicuramente il tuo loop più veloce su una varietà di browser, in particolare i browser IE, è il loop come segue:

for (var i = 0, iLength = imgs.length; i < iLength; i++)
{
   // do something
}

Hai inserito un 1000 arbitrario nella condizione del ciclo, ma ottieni la mia deriva se volessi esaminare tutti gli elementi dell'array.

un riferimento sarà quasi sempre più lento dell'oggetto a cui si riferisce.Pensala in questo modo: supponiamo che tu voglia stampare il risultato della somma di 1 + 1.Il che ha più senso:

alert(1 + 1);

O

a = 1;
b = 1;
alert(a + b);

Mi rendo conto che è un modo davvero semplicistico di vedere la cosa, ma è illustrativo, giusto?Utilizza un riferimento solo se verrà utilizzato più volte, ad esempio quale di questi esempi ha più senso:

$(a.button1).click(function(){alert('you clicked ' + this);});
$(a.button2).click(function(){alert('you clicked ' + this);});

O

function buttonClickHandler(){alert('you clicked ' + this);}
$(a.button1).click(buttonClickHandler);
$(a.button2).click(buttonClickHandler);

Il secondo è un esercizio migliore, anche se ha più righe.Speriamo che tutto questo sia utile.(e la sintassi jquery non ha scoraggiato nessuno)

@nickf

(vorrei avere il rappresentante per commentare, ma ho appena trovato questo sito)

Il punto è che qui c'è confusione tra funzioni denominate/anonime e il caso d'uso di esecuzione + compilazione in un'iterazione.Come ho illustrato, la differenza tra anon+named è di per sé trascurabile: sto dicendo che è il caso d'uso ad essere difettoso.

Mi sembra ovvio, ma in caso contrario penso che il miglior consiglio sia "non fare cose stupide" (di cui il costante spostamento di blocchi + creazione di oggetti di questo caso d'uso è uno) e se non sei sicuro, prova!

SÌ!Le funzioni anonime sono più veloci delle funzioni normali.Forse se la velocità è della massima importanza...più importante del riutilizzo del codice, considera l'utilizzo di funzioni anonime.

C'è un ottimo articolo sull'ottimizzazione di JavaScript e delle funzioni anonime qui:

http://dev.opera.com/articles/view/efficient-javascript/?page=2

@nickf

Questo è un test piuttosto fazioso però, stai confrontando l'esecuzione e compilazione tempo lì che ovviamente costerà il metodo 1 (compila N volte, a seconda del motore JS) con il metodo 2 (compila una volta).Non riesco a immaginare uno sviluppatore JS che supererebbe il periodo di prova scrivendo codice in questo modo.

Un approccio molto più realistico è l'assegnazione anonima, come in effetti stai utilizzando per il tuo documento. Il metodo onclick è più simile al seguente, che in effetti favorisce leggermente il metodo anon.

Utilizzando un framework di test simile al tuo:


function test(m)
{
    for (var i = 0; i < 1000000; ++i) 
    {
        m();
    }
}

function named() {var x = 0; x++;}

var test1 = named;

var test2 = function() {var x = 0; x++;}

document.onclick = function() {
    var start = new Date();
    test(test1);
    var mid = new Date();
    test(test2);
    var end = new Date();
    alert ("Test 1: " + (mid - start) + "ms\n Test 2: " + (end - mid) + "ms");
}

Come sottolineato nei commenti alla risposta di @nickf:La risposta a

Creare una funzione una volta è più veloce che crearla un milione di volte

è semplicemente sì.Ma come mostra la sua prestazione JS, non è più lento di un fattore un milione, dimostrando che in realtà diventa più veloce nel tempo.

La domanda più interessante per me è:

Come funziona una ripetizione creare + eseguire confrontare per creare una volta + ripetuto correre.

Se una funzione esegue un calcolo complesso, il tempo necessario per creare l'oggetto funzione è molto probabilmente trascurabile.Ma per quanto riguarda il sovraccarico? creare nei casi in cui correre è veloce?Ad esempio:

// Variant 1: create once
function adder(a, b) {
  return a + b;
}
for (var i = 0; i < 100000; ++i) {
  var x = adder(412, 123);
}

// Variant 2: repeated creation via function statement
for (var i = 0; i < 100000; ++i) {
  function adder(a, b) {
    return a + b;
  }
  var x = adder(412, 123);
}

// Variant 3: repeated creation via function expression
for (var i = 0; i < 100000; ++i) {
  var x = (function(a, b) { return a + b; })(412, 123);
}

Questo JS Perf mostra che creare la funzione solo una volta è più veloce del previsto.Tuttavia, anche con un'operazione molto rapida come una semplice aggiunta, il sovraccarico derivante dalla creazione ripetuta della funzione è solo di pochi punti percentuali.

La differenza probabilmente diventa significativa solo nei casi in cui la creazione dell'oggetto funzione è complessa, pur mantenendo un tempo di esecuzione trascurabile, ad esempio se l'intero corpo della funzione è racchiuso in un if (unlikelyCondition) { ... }.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top