quarta-feira, 17 de junho de 2009

Performance de JS - Direto no BIT

Este post é baseado em algumas definições apresentadas nos Slides de Nicholas C. Zakas, Principal Front End Engineer do Yahoo! e um dos desenvolvedores do YUI. Vou apresentar superficialmente o que está na apresentação, mas sugiro que vocês a vejam slide por slide, já que são 90 páginas e todas com observações bem importantes.

A URL da apresentação é : http://www.slideshare.net/nzakas/speed-up-your-javascript

Por que o Javascript é tão lento?
O Javascript torna-se lento devido à forma que a implementação é realizada. No meu primeiro post no blog eu mencionei as vantagens quanto ao bom e mau uso de frameworks Javascript, mais especificamente da JQuery.


No quesito de performance, alguns tópicos devem ser analisados:
- Gerenciamento de Escopo
- Acesso à dados
- Loops (Repetições)
- DOM (Document Object Model)


Darei importância aos 3 últimos itens, apresentando uma breve descrição do primeiro (o que não o torna menos importante).

Gerenciamento de Escopo

Brevemente falando, todas as vezes que uma função é executada pelo Javascript uma série de encadeamentos são disparados, e executados como processos em paralelo.
Algumas instruções destes procedimentos são executadas de forma desnecessária, já que o algoritmo não consegue interpretar o que virá pela frente.
Para evitarmos que este encadeamento seja percorrido à cada instrução presente em nossa função, uma das mais importantes colocações é quanto ao armazenamento de variaveis, que sugere a declaração de elementos globais como variaveis pertencentes ao escopo interno da função.

Ex.:
function exemplo1(){
    // A declaração do objeto DOCUMENT no escopo da função, evita que o interpretador fique "entrando" e "saindo" do escopo da função para buscar valores
    var doc = document;
    var divs = doc.getElementsByTagName("divs");
    var images = doc.getElementsByTagName("img");
    return true;
}


Outro detalhe, NUNCA esqueça do VAR antes de declarar uma váriavel, caso contrário ela será tratada como GLOBAL e o fluxo de encadeamento é quebrado, prejudicando a navegação pelo escopo definido.

Acesso à dados
Este item é bem importante, seguindo a linha de raciocínio especificado no item anterior, temos as seguintes colocações:
- Acessar dados literais ou de variáveis locais é algo rápido.
- Acessar dados de uma propriedade (atributo) de um objeto ou array é muito mais custoso (variando de browser para browser).



- Acessar objeto.name é mais rápido do que objeto.name.name;
- Quanto mais profundo o nível da propriedade, mais custoso é o acesso.

Property Notation
O acesso à objetos em Javascript pode ser realizada de duas formas:
- Com ponto : objeto.nome
- Com colchetes (como um array associativo) : objeto["nome"]

A diferença entre as duas está apenas na sintaxe, com excessão do Safari, que processa mais rápido a primeira opção.

Recomendações:
- Armaneze niveis de acesso de objetos em variáveis locais, dentro do escopo da função ou método, especificamente quando estes são acessados de forma repetitiva.
- Minimize o nível de atributos/propriedades em objetos, lembre-se: quanto mais complexa a estrutura, mais lento é o processamento.

Ex.:
function exemplo2(arrData){
    for(var a = 0; a < arrData.length; a++){
        alert(arrData[a]);
    }
}


O exemplo acima realiza o loop em um array (bem óbvio), entretando a CADA volta do Loop, o array é novamente acessado, e medido. Ou seja, você está realizando a contagem de todas posições ocupadas à cada volta do Loop. :S

Agora observe o exemplo abaixo:
Ex.:
function exemplo3(arrData){
    var arrSize = arrData.length;
    for(var a = 0; a < arrSize; a++){
        alert(arrData[a]);
    }
}


ÓTIMO, E O QUAL O GANHO DISSO?
Vamos então aos números:
- Firefox ganho de 5%;
- Safari ganho de 10%;
- Internet Explorer ganho de 33%;

Loops / Repetições:
Neste processo devemos ter cuidado à quantidade de instruções por interação e principalmente termos uma condição de parada eficaz.

Ex.:
var len = 5;
for(var i = 0; i < len; i++){
 // faça alguma coisa
}

No exemplo acima temos duas instruções repetitivas:
i < len;
e
i++


Podemos simplificar esse processo da seguinte forma:
for(var i = len; i--){
  // faça alguma coisa , agora mais rapido!
}


QUAL O GANHO COM ISSO?
Somente 50%. :)

DOM (Document Object Model)
Objetos de uma HTMLCollection (document.images, document.forms, getElementsByTagName, getElementsByClassName, etc).

Coleções em HTML DOM assumem-se como "vivas" pois são automaticamente atualizadas cada vez que a estrutura do documento sofrer modificações.

Vejamos o exemplo:
Ex.:
var divs = document.getElementsByTagName("div");
for(a = 0; a < divs.length; a++){
    var div = document.createElement("div");
    document.body.appendChild(div);
}


No exemplo acima acabamos de criar um loop infinito! Isso por que a cada interação a variavel "divs" é acessada, e o DOM é reparseado para sua contagem, e já que estamos inserindo um elemento novo a cada interação, não teremos um fim nunca.

Entretanto se seguirmos a seguinte linha:

Ex.:
var divs = document.getElementsByTagName("div");
for(a = 0, len = divs.length; a < len; a++){
    // Faça alguma coisa
}


Atribuimos o tamanho do elemento à uma nova variável, a qual pertence apenas ao escopo do FOR, e que não será reparseada em casos de modificações no documento.

Obs.: Da mesma maneira que sugerimos o armazenamento de variaveis GLOBAIS como LOCAIS no gerenciamento de escopo podemos aplicar esta regra também para o DOM.

Reaproveitamento
Este tópico também recebe destaque no que se refere à modificações de layout de um elemento. Como de costume fazemos isso acessando o elemento através do atributo "style" e por fim a propriedade desejada.
Ex.:
document.getElementById("myDiv").style.display = "block";
document.getElementById("myDiv").style.border = "1px solid #FF0000";

No exemplo acima estamos obrigando o interpretador acessar toda a àrvore dde propriedade do elemento "myDiv" duas vezes, o ideal é evitarmos mudar propriedades de elementos desta forma, realizando esta configuração através de classes especificas de CSS.

Ex.:
document.getElementById("myDiv").className = "estilo1";

e no CSS teremos a configuração desejada:

.estilo1{
    display: block;
    border: 1px solid #FF0000
}


Nenhum comentário:

Postar um comentário