it-swarm-pt.tech

O uso de funções anônimas afeta o desempenho?

Eu tenho me perguntado, existe uma diferença de desempenho entre o uso de funções nomeadas e funções anônimas em 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;
}

O primeiro é mais ordenado, pois não atrapalha seu código com funções raramente usadas, mas importa que você esteja declarando essa função várias vezes?

80
nickf

O problema de desempenho aqui é o custo de criar um novo objeto de função em cada iteração do loop e não o fato de você usar uma função anônima:

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

Você está criando mil objetos de função distintos, embora eles tenham o mesmo corpo de código e nenhuma ligação ao escopo léxico ( encerramento ). O seguinte parece mais rápido, por outro lado, porque simplesmente atribui a referência same aos elementos da matriz em todo o loop:

function myEventHandler() {
    // do something
}

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

Se você criasse a função anônima antes de entrar no loop, então apenas atribua referências a ela aos elementos da matriz enquanto estiver dentro do loop, você descobrirá que não há desempenho ou diferença semântica em comparação com a versão da função nomeada:

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

Em suma, não há custo de desempenho observável para o uso de funções anônimas sobre nome.

Como um aparte, pode parecer de cima que não há diferença entre:

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

e:

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

O primeiro é uma declaração de funçãoenquanto o segundo é uma atribuição de variável a uma função anônima. Embora eles possam parecer ter o mesmo efeito, o JavaScript os trata de maneira ligeiramente diferente. Para entender a diferença, recomendo ler “ JavaScript function declaration ambiguity ”.

O tempo real de execução de qualquer abordagem será amplamente determinado pela implementação do compilador e do tempo de execução do navegador. Para uma comparação completa do desempenho moderno do navegador, visite o site do JS Perf

81
Atif Aziz

Aqui está o meu código de teste:

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

Os resultados:
Teste 1: 142 ms Teste 2: 1983 ms

Parece que o mecanismo JS não reconhece que é a mesma função no Test2 e o compila a cada vez.

21
nickf

Como princípio geral de design, você deve evitar implantar o mesmo código várias vezes. Em vez disso, você deve levantar o código comum para uma função e executar essa função (geral, bem testada, fácil de modificar) de vários lugares.

Se (ao contrário do que você inferir da sua pergunta) você está declarando a função interna uma vez e usando esse código uma vez (e não tem nada mais idêntico em seu programa) então uma função anônima provavelmente (isso é um palpite) é tratada mesma maneira pelo compilador como uma função nomeada normal.

É um recurso muito útil em instâncias específicas, mas não deve ser usado em muitas situações.

2
Tom Leys

Onde podemos ter um impacto no desempenho está na operação de declarar funções. Aqui está uma referência de declarar funções dentro do contexto de outra função ou fora dela:

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

No Chrome, a operação é mais rápida se declararmos a função do lado de fora, mas no Firefox é o oposto.

Em outro exemplo, vemos que, se a função interna não é uma função pura, ela terá uma falta de desempenho também no Firefox: http://jsperf.com/function-context-benchmark-3

1
Pablo Estornut

Eu não esperaria muita diferença, mas se houver um, provavelmente irá variar de mecanismo de script ou navegador. 

Se você achar que o código é mais fácil, o desempenho não é um problema, a menos que você espere chamar a função milhões de vezes.

1
Joe Skora

Objetos anônimos são mais rápidos que objetos nomeados. Mas chamar mais funções é mais caro e, em certa medida, ofusca qualquer economia que você possa obter usando funções anônimas. Cada função chamada adiciona à pilha de chamadas, o que introduz uma pequena, mas não trivial, sobrecarga.

Mas, a menos que você esteja escrevendo rotinas de criptografia/descriptografia ou algo similar ao desempenho, como muitos outros notaram, é sempre melhor otimizar para obter um código elegante e fácil de ler em vez de código rápido.

Assumindo que você está escrevendo um código bem arquitetado, os problemas de velocidade devem ser responsabilidade daqueles que escrevem os interpretadores/compiladores.

1
pcorcoran

@nickf

Isso é um teste bastante tolo, você está comparando a execução e compilação tempo lá que obviamente vai custar o método 1 (compila N vezes, dependendo do mecanismo JS) com o método 2 (compila uma vez). Eu não posso imaginar um desenvolvedor JS que passaria seu código de gravação de prova de tal maneira.

Uma abordagem muito mais realista é a atribuição anônima, como na verdade você está usando para o seu documento. O método onclick é mais parecido com o seguinte, que de fato favorece levemente o método anon.

Usando um framework de teste similar ao seu:


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

(gostaria de ter o representante apenas para comentar, mas eu acabei de encontrar este site)

Meu ponto é que há confusão aqui entre funções nomeadas/anônimas e o caso de uso de execução + compilação em uma iteração. Como ilustrei, a diferença entre anon + named é insignificante em si - estou dizendo que é o caso de uso que é defeituoso.

Parece óbvio para mim, mas se não, eu acho que o melhor conselho é "não faça coisas estúpidas" (das quais o bloqueio constante + criação de objetos deste caso de uso é um) e se você não tem certeza, teste!

0
annakata

SIM! Funções anônimas são mais rápidas que funções normais. Talvez, se a velocidade for da maior importância ... mais importante que a reutilização de código, considere o uso de funções anônimas.

Existe um artigo muito bom sobre como otimizar o javascript e as funções anônimas aqui:

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

0
Christopher Tokar

uma referência quase sempre será mais lenta do que a que está se referindo. Pense nisso dessa maneira - digamos que você queira imprimir o resultado da adição de 1 + 1. O que faz mais sentido:

alert(1 + 1);

ou

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

Eu percebo que é uma maneira simplista de olhar para isso, mas é ilustrativo, certo? Use uma referência somente se ela for usada várias vezes - por exemplo, qual desses exemplos faz mais sentido:

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

ou

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

O segundo é uma prática melhor, mesmo que tenha mais linhas. Espero que tudo isso seja útil. (e a sintaxe jquery não jogou ninguém fora)

0
matt lohkamp

Como apontado nos comentários para @nickf answer: A resposta para 

Está criando uma função uma vez mais rápido do que criá-lo um milhão de vezes

é simplesmente sim. Mas como mostra seu JS perf, não é mais lento em um fator de um milhão, mostrando que ele realmente fica mais rápido com o tempo.

A questão mais interessante para mim é:

Como uma repetição criar + executar comparar para criar uma vez + repetida executar .

Se uma função executa um cálculo complexo, o tempo para criar o objeto de função é provavelmente desprezível. Mas e o overhead de create nos casos em que run é rápido? Por exemplo:

// 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);
}

Este JS Perf mostra que criar a função apenas uma vez é mais rápido conforme o esperado. No entanto, mesmo com uma operação muito rápida, como um simples acréscimo, a sobrecarga de criar a função repetidamente é de apenas alguns por cento.

A diferença provavelmente só se torna significativa nos casos em que a criação do objeto de função é complexa, mantendo um tempo de execução insignificante, por exemplo, se todo o corpo da função for empacotado em um if (unlikelyCondition) { ... }.

0
bluenote10

O que definitivamente tornará seu loop mais rápido em vários navegadores, especialmente IE navegadores, está em loop da seguinte forma:

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

Você colocou um 1000 arbitrário na condição de loop, mas você me entende se quiser passar por todos os itens da matriz.

0
Sarhanis