it-swarm.it

L'uso di funzioni anonime influisce sulle prestazioni?

Mi sono chiesto, c'è una differenza di prestazioni tra l'utilizzo di funzioni con nome e funzioni anonime in Javascript?

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

vs

function myEventHandler() {
    // do something
}

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

Il primo è più ordinato perché non ingombra il codice con le funzioni usate raramente, ma è importante che tu lo dichiari più volte?

80
nickf

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

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

Stai creando un migliaio di oggetti funzione distinti pur avendo lo stesso corpo di codice e nessun legame all'ambito lessicale ( closure ). Quanto segue sembra più veloce, d'altra parte, perché assegna semplicemente il riferimento same function agli elementi dell'array in tutto 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 assegna solo riferimenti ad esso negli elementi dell'array mentre ci si trova all'interno del ciclo, scoprirai che non ci sono prestazioni o differenza semantica di qualsiasi tipo rispetto alla versione della funzione nominata:

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

In breve, non vi è alcun costo di prestazione osservabile per l'utilizzo di funzioni anonime con un nome.

Per inciso, può sembrare dall'alto che non vi è alcuna differenza tra:

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

e:

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

Il primo è a dichiarazione di funzione mentre il secondo è un'assegnazione variabile a una funzione anonima. Sebbene possano sembrare che abbiano lo stesso effetto, JavaScript li tratta in modo leggermente diverso. Per capire la differenza, ti consiglio di leggere " JavaScript function declaration ambiguity ".

L'effettivo tempo di esecuzione 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 del browser moderno, visita il sito JS Perf

81
Atif Aziz

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:
Test 1: 142 ms Test 2: 1983ms

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

21
nickf

Come principio generale di progettazione, è necessario evitare di impiantare lo stesso codice più volte. Invece dovresti sollevare il codice comune in una funzione ed eseguire quella (generale, ben collaudata, facile da modificare) da più punti.

Se (a differenza di ciò che deduci dalla tua domanda) stai dichiarando la funzione interna una sola volta e usando quel codice una volta (e non hai nient'altro identico nel tuo programma) allora una funzione anonima probabilmente (che è una gente indovina) viene trattata allo stesso modo dal compilatore come una normale funzione denominata.

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

2
Tom Leys

Dove possiamo avere un impatto sulle prestazioni è nel funzionamento delle funzioni di dichiarazione. Ecco un punto di riferimento per dichiarare funzioni all'interno del contesto di un'altra funzione o al di fuori:

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

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

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

1
Pablo Estornut

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

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

1
Joe Skora

Gli oggetti anonimi sono più veloci degli oggetti con nome. Ma chiamare più funzioni è più costoso e ad un livello che eclissa qualsiasi risparmio derivante dall'uso di funzioni anonime. Ogni funzione chiamata aggiunge allo stack delle chiamate, che introduce una piccola ma non banale quantità di overhead.

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

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

1
pcorcoran

@nickf

Questo è un test piuttosto laborioso, ma stai confrontando l'esecuzione e la compilazione tempo là che ovviamente andrà a costare 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 passerebbe il loro codice di scrittura in prova in questo modo.

Un approccio molto più realistico è l'assegnazione anonima, come in effetti si sta utilizzando per il proprio documento. Il metodo di lettura è 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");
}
0
annakata

@nickf

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

Il mio punto è che qui c'è confusione tra funzioni anonime/anonime e il caso d'uso dell'esecuzione + compila in una iterazione. Come ho illustrato, la differenza tra anon + named è trascurabile di per sé - sto dicendo che è il caso d'uso che è difettoso.

Mi sembra ovvio, ma se non penso che il consiglio migliore sia "non fare cose stupide" (di cui il blocco costante + la creazione dell'oggetto di questo caso d'uso è uno) e se non sei sicuro, prova!

0
annakata

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

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

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

0

un riferimento è quasi sempre più lento della cosa a cui si riferisce. Pensaci in questo modo - diciamo che vuoi stampare il risultato dell'aggiunta di 1 + 1. Che ha più senso:

alert(1 + 1);

o

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

Mi rendo conto che è un modo davvero semplicistico di guardarlo, 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 è una pratica migliore, anche se ha più linee. Speriamo che tutto questo sia utile. (e la sintassi jquery non ha gettato nessuno)

0
matt lohkamp

Come indicato nei commenti a @nickf answer: La risposta a 

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

è semplicemente sì Tuttavia, come mostra il suo perf per JS, non è più lento di un fattore di un milione, dimostrando che in realtà diventa più veloce nel tempo.

La domanda più interessante per me è:

Come fa un ripetuto create + run compare per creare una volta + ripetuto run .

Se una funzione esegue un calcolo complesso, il tempo per creare l'oggetto funzione è molto probabilmente trascurabile. Ma per quanto riguarda l'overhead di create nei casi in cui run è veloce? Per 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 la creazione della funzione solo una volta è più veloce come previsto. Tuttavia, anche con un'operazione molto rapida come una semplice aggiunta, l'overhead di creare la funzione ripetutamente è solo una piccola percentuale.

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

0
bluenote10

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

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

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

0
Sarhanis