Nota do tradutor: sou a primeira vez que traduzi uma língua estrangeira e minhas palavras são inevitavelmente um pouco obscuras, mas tentei o meu melhor para expressar a intenção original do autor e não tinha muito polimento. Críticas e correção são bem -vindas. Além disso, este artigo é longo e possui uma grande quantidade de informações, o que pode ser difícil de digerir. Por favor, deixe uma mensagem para discutir detalhes. Este artigo se concentra principalmente na otimização de desempenho do V8, e parte do conteúdo não é aplicável a todos os motores JS. Finalmente, indique a fonte ao reimprimir :)
==========================================================================
Muitos motores JavaScript, como o mecanismo V8 do Google (usado pelo Chrome e Node), são projetados especificamente para grandes aplicativos JavaScript que requerem execução rápida. Se você é um desenvolvedor e está preocupado com o uso da memória e o desempenho da página, deve entender como o mecanismo JavaScript no seu navegador funciona. Seja V8, Spidermonkey (Firefox) Carakan (Opera), Chakra (IE) ou outros motores, isso pode ajudá -lo a otimizar melhor seu aplicativo . Isso não significa que você deve otimizar especificamente para um determinado navegador ou motor e nunca faça isso.
No entanto, você deve se fazer algumas perguntas:
Um site de carregamento rápido é como um carro esportivo rápido que requer peças especialmente personalizadas. Fonte da imagem: dhybridcars.
Existem algumas armadilhas comuns ao escrever código de alto desempenho e, neste artigo, mostraremos algumas maneiras comprovadas e melhores de escrever código.
Se você não tem um profundo entendimento dos motores JS, não há problema em desenvolver um grande aplicativo da Web, assim como uma pessoa que pode dirigir só viu o capô, mas não o motor dentro do capô. Dado que o Chrome é a primeira escolha do meu navegador, vamos falar sobre seu mecanismo JavaScript. V8 é composto pelas seguintes partes principais:
A coleta de lixo é uma forma de gerenciamento de memória , que é na verdade um conceito de colecionador, tentando reciclar a memória ocupada por objetos que não são mais usados. Em uma linguagem de coleta de lixo como JavaScript, objetos que ainda estão sendo referenciados no aplicativo não serão limpos.
A eliminação manual de referências de objetos não é necessária na maioria dos casos. Tudo funcionará bem, simplesmente colocando as variáveis onde são necessárias (idealmente, o mais escopo localmente possível, ou seja, a função que são usadas em vez da camada externa da função).
O coletor de lixo tenta reciclar a memória. Fonte da imagem: Valtteri Mäki.
Em JavaScript, é impossível forçar a coleta de lixo. Você não deve fazer isso porque o processo de coleta de lixo é controlado pelo tempo de execução e sabe qual é o melhor momento para limpar.
Existem muitas discussões sobre a reciclagem de memória JavaScript na Internet sobre a palavra -chave Excluir. Embora possa ser usado para excluir atributos (chaves) em objetos (mapa), alguns desenvolvedores acreditam que ele pode ser usado para forçar "desreferências". Recomenda -se evitar o uso de exclusão sempre que possível. No exemplo abaixo delete ox 的弊大于利,因为它改变了o的隐藏类,并使它成为一个"慢对象"。
var o = {x: 1}; excluir boi; // ox verdadeiro; // indefinidoVocê encontrará facilmente a remoção de referência nas bibliotecas JS populares - isso é linguisticamente proposital. Deve -se notar aqui que evite modificar a estrutura do objeto "quente" em tempo de execução. O mecanismo JavaScript pode detectar esses objetos "quentes" e tentar otimizá -los. Se a estrutura do objeto não mudar significativamente durante o ciclo de vida, o motor será mais fácil para otimizar o objeto, e a operação de exclusão realmente desencadeará essa maior mudança estrutural, que não é propícia à otimização do motor.
Também há mal -entendidos sobre como o NULL funciona. Definir uma referência de objeto para Null não torna o objeto "vazio", apenas define sua referência para vazio. Usar ox = null é melhor do que usar excluir, mas pode não ser necessário.
var o = {x: 1}; o = nulo; o; // nullo.x // typeErrorSe essa referência for a última referência ao objeto atual, o objeto será coletado de lixo. Se essa referência não for a última referência ao objeto atual, o objeto estará acessível e não será coletado com lixo.
Deve -se notar também que as variáveis globais não são limpas pelo coletor de lixo durante o ciclo de vida da página. Não importa quanto tempo a página esteja aberta, as variáveis no escopo do objeto global sempre existirão quando o JavaScript for executado.
var myGlobalNamespace = {};Os objetos globais só serão limpos ao atualizar a página, navegar para outra página, fechar a guia ou sair do navegador. As variáveis no escopo da função serão limpas quando estiverem fora de escopo, ou seja, quando a função é excitada, não há referências e essas variáveis serão limpas.
Para que o coletor de lixo colete o maior número possível de objetos, não segure objetos que não sejam mais usados . Aqui estão algumas coisas para lembrar:
Em seguida, vamos falar sobre funções. Como já dissemos, os trabalhos de coleta de lixo reciclando blocos de memória (objetos) que não são mais acessíveis. Para ilustrar isso melhor, aqui estão alguns exemplos.
function foo () {var bar = new lregaBject (); bar.somecall ();}Quando o Foo retornar, o objeto apontado pela barra será automaticamente reciclado pelo coletor de lixo porque não possui referências existentes.
Comparar:
function foo () {var bar = new lregaBject (); bar.somecall (); barra de retorno;} // em algum lugar de outra pessoa b = foo ();Agora, temos uma referência apontando para o objeto de barra, para que o ciclo de vida do objeto de barra continue da chamada para Foo até que o chamador especifique outra variável B (ou B está fora de escopo).
Quando você vir uma função, retorne uma função interna, que sairá do acesso ao escopo, mesmo após a execução da função externa. Este é um fechamento básico - uma expressão de variáveis que podem ser definidas em um contexto específico. Por exemplo:
função soma (x) {função sumit (y) {return x + y; }; retornar sumit;} // uSagevar suma = soma (4); var sumb = suma (3); console.log (sumb); // retorna 7O objeto de função (SUMIT) gerado no contexto da chamada da soma não pode ser reciclado. É referenciado pela variável global (SUMA) e pode ser chamado através da SUMA (n).
Vamos dar uma olhada em outro exemplo, onde podemos acessar a variável maior?
var a = function () {var maior = nova matriz (1000000) .Join ('x'); return function () {return maioresr; };} ();Sim, podemos acessar o maior a (), para que não seja reciclado. E quanto ao seguinte?
var a = function () {var smallstr = 'x'; var maior = nova matriz (1000000) .Join ('x'); Retornar função (n) {return smallstr; };} ();Não podemos mais acessar o maior, ele já é um candidato a coleção de lixo. [Nota do tradutor: porque o maior não tem mais referências externas]
Um dos piores lugares para vazar a memória está no loop, ou no setTimeout ()/setInterval (), mas isso é bastante comum. Pense nos seguintes exemplos:
var myobj = {CallMemayBe: function () {var myref = this; var val = setTimeout (function () {console.log ('O tempo está acabando!'); myRef.callMemaybe ();}, 1000); }}; Se executarmos myobj.callmemaybe (); Para iniciar o cronômetro, podemos ver que as impressões do console "Time está acabando!" a cada segundo. Se myObj = null,定时器依旧处于激活状态。为了能够持续执行,闭包将myObj传递给setTimeout,这样myObj是无法被回收的。相反,它引用到myObj的因为它捕获了myRef。这跟我们为了保持引用将闭包传给其他的函数是一样的。
Também vale lembrar que as referências nas chamadas Settimeout/SetInterval (como funções) precisarão ser executadas e concluídas antes que elas possam ser coletadas de lixo.
Nunca otimize o código até realmente precisar. Agora, muitas vezes você pode ver alguns benchmarks que mostram que N é mais otimizado no V8 do que M, mas se você o testar no código ou aplicativo do módulo, descobrirá que essas otimizações são realmente muito menores do que você espera.
É melhor fazer nada do que fazer muito. Fonte da imagem: Tim Sheerman-Chase.
Por exemplo, queremos criar esse módulo:
Existem vários fatores diferentes nesse problema, embora também seja fácil de resolver. Como armazenamos dados, como desenhamos eficientemente as tabelas e as anexamos ao DOM, e como melhor lidamos com os eventos da tabela?
A abordagem inicial (ingênua) para enfrentar esses problemas é usar objetos para armazenar dados e colocá -los em uma matriz, usar o jQuery para atravessar os dados para desenhar uma tabela e anexá -los ao DOM e, finalmente, usar a ligação do evento ao comportamento de cliques que esperamos.
Nota: não é isso que você deve fazer
var modulea = function () {return {data: DataArrayObject, init: function () {this.addtable (); this.Addevents (); }, addTable: function () {for (var i = 0; i <linhas; i ++) {$ tr = $ ('<tr> </tr>'); for (var j = 0; j <this.data.length; j ++) {$ tr.append ('<td>' + this.data [j] ['id'] + '</td>'); } $ tr.appendto ($ tbody); }}, addEvents: function () {$ ('tabela td'). }};} ();Esse código conclui de maneira simples e eficaz a tarefa.
Mas, nesse caso, os dados que atravessamos são apenas um ID de propriedade numérica que deveria ter sido simplesmente armazenado na matriz. Curiosamente, é melhor usar os métodos DocumentFragment e DOM local diretamente do que gerar tabelas usando jQuery (dessa maneira) e, é claro, o proxy do evento tem maior desempenho do que a ligação a cada TD sozinho.
Observe que, embora o jQuery use o DocumentFragment internamente, em nosso exemplo, o código chama anexar dentro de um loop e essas chamadas envolvem algum outro pouco conhecimento; portanto, o efeito de otimização aqui não é muito bom. Espero que isso não seja um ponto de dor, mas não se esqueça de fazer uma referência para garantir que seu código esteja bem.
Para o nosso exemplo, as práticas acima trazem melhorias de desempenho (desejadas). A proxy de eventos é uma melhoria para a ligação simples, e o documental opcional também ajuda.
var moduled = function () {return {data: DataArray, init: function () {this.addtable (); this.Addevents (); }, addTable: function () {var td, tr; var fragment = document.createCumentFragment (); var fragment2 = document.createCumentFragment (); for (var i = 0; i <linhas; i ++) {tr = document.createElement ('tr'); for (var j = 0; j <this.data.length; j ++) {td = document.createElement ('td'); td.appendChild (document.createTextNode (this.data [j])); frag2.appendchild (td); } tr.appendChild (frag2); frag.appendchild (tr); } tbody.appendChild (frag); }, addEvents: function () {$ ('tabela'). }};} ();Vamos dar uma olhada em outras maneiras de melhorar o desempenho. Você pode ter lido que o uso do modo de protótipo é melhor que o modo de módulo, ou ouviu dizer que o uso do JS Model Frameworks tem um desempenho melhor. Às vezes, isso é verdade, mas eles são usados para tornar o código mais legível. A propósito, há pré -compilação! Vamos ver como ele se sai na prática?
moduleg = function () {}; moduleg.prototype.data = dataArray; moduleg.prototype.init = function () {this.addtable (); this.addevents ();}; moduleg.prototype.addtable = function () {var modelo = _.template ($ ('#modelo'). text ()); var html = modelo ({'dados': this.data}); $ tbody.append (html);}; moduleg.prototype.addevents = function () {$ ('tabela').Acontece que as melhorias de desempenho trazidas nessa situação são insignificantes. A escolha de modelos e protótipos não oferece nada mais. Ou seja, o desempenho não é a razão pela qual os desenvolvedores os usam, e a legibilidade, modelo de herança e manutenção trazida ao código são os motivos reais.
Problemas mais complexos incluem desenhar fotos com eficiência em tela e manipular dados de pixels com ou sem matrizes de tipos.
Antes de usar alguns métodos para o seu próprio aplicativo, aprenda mais sobre o benchmarking dessas soluções. Talvez alguém ainda se lembre da filmagem e das extensões subsequentes do modelo JS. Você precisa descobrir que o benchmarking não existe em aplicativos virtuais que você não pode ver, mas deve testar as otimizações trazidas pelo seu código real.
Os pontos de otimização de cada motor V8 são introduzidos em detalhes fora do escopo deste artigo. Obviamente, há muitas dicas que vale a pena mencionar aqui. Lembre -se dessas dicas e você pode reduzir o código com baixo desempenho.
função add (x, y) {return x+y;} add (1, 2); add ('a', 'b'); add (my_custom_object, indefinido);Para mais conteúdo, consulte o compartilhamento de Daniel Clifford no Google I/S. Quebrando o limite de velocidade JavaScript com V8. Otimizar para V8 - Uma série também vale muito a pena ler.
Existe apenas uma diferença principal entre objetos e matrizes em JavaScript, ou seja, a propriedade de matrizes de comprimento mágico das matrizes. Se você mesmo manter essa propriedade, objetos e matrizes no V8 são tão rápidos quanto os de matrizes.
A clonagem de objetos é um problema comum para os desenvolvedores de aplicativos. Embora vários benchmarks possam provar que o V8 lida bem a esse problema, tenha cuidado. Copiar grandes coisas geralmente é mais lento - não faça isso. O loop for..in in js é especialmente ruim porque possui uma especificação demoníaca e pode nunca ser mais rápido do que qualquer objeto em qualquer mecanismo.
Quando você tem certeza de copiar objetos no caminho crítico do código de desempenho, use uma matriz ou uma função "Copiar Construtor" para copiar explicitamente cada propriedade. Esta é provavelmente a maneira mais rápida:
função clone (original) {this.foo = original.foo; this.bar = original.bar;} var cópia = novo clone (original);As funções de cache ao usar o modo de módulo podem levar a melhorias de desempenho. Veja o exemplo abaixo, porque ele sempre cria uma nova cópia da função do membro, as alterações que você vê podem ser mais lentas.
Observe também que o uso desse método é obviamente melhor, não apenas dependendo do modo de protótipo (confirmado pelo teste JSPERF).
Melhorias de desempenho ao usar o modo de módulo ou modo de protótipo
Este é um teste de comparação de desempenho do modo de protótipo e modo de módulo:
// padrão de protótipo klass1 = function () {} klass1.prototype.foo = function () {Log ('foo'); } Klass1.prototype.bar = function () {log ('bar'); } // Module Pattern klass2 = function () {var foo = function () {log ('foo'); }, bar = function () {log ('bar'); }; return {foo: foo, bar: bar}} // padrão de módulo com funções em cache var foofunction = function () {log ('foo'); }; varfunção var = function () {log ('bar'); }; Klass3 = function () {return {foo: foofunction, bar: barfunction}} // testes de iteração // prototypal var i = 1000, objs = []; while (i--) {var o = novo klass1 () objs.push (novo klass1 ()); O.bar; O.FOO; } // Padrão do módulo var i = 1000, objs = []; while (i--) {var o = novo klass1 () objs.push (novo klass1 ()); O.bar; O.FOO; } // Padrão do módulo var i = 1000, objs = []; while (i--) {var o = klass2 () objs.push (klass2 ()); O.bar; O.FOO; } // padrão de módulo com funções em cache var i = 1000, objs = []; while (i--) {var o = klass3 () objs.push (klass3 ()); O.bar; O.FOO; } // Veja o teste para obter detalhes completosEm seguida, vamos falar sobre as técnicas relacionadas às matrizes. Em geral, não exclua os elementos da matriz , o que fará a transição da matriz para uma representação interna mais lenta. Quando o índice se tornar escasso, o V8 transforma o elemento em um padrão de dicionário mais lento.
Os literais da matriz são muito úteis e podem sugerir o tamanho e o tipo da matriz VM. Geralmente é usado em matrizes com tamanhos pequenos.
// Aqui V8 pode ver que você deseja uma matriz de 4 elementos contendo números: var a = [1, 2, 3, 4]; // Não faça isso: a = []; // aqui v8 não sabe nada sobre o arrayfor (var i = 1; i <= 4; i ++) {a.push (i);}Não é de forma alguma uma boa idéia armazenar dados de tipos mistos (como números, strings, indefinidos, verdadeiros/falsos) em uma matriz. Por exemplo, var arr = [1, "1", indefinido, verdadeiro, "verdadeiro"]
Teste de desempenho de inferência de tipo
Como vimos, as matrizes de números inteiros são os mais rápidos.
Quando você usa matrizes esparsas, tenha cuidado para acessar elementos será muito mais lento que as matrizes completas. Como o V8 não alocará um espaço inteiro a uma matriz que usa apenas parte do espaço. Em vez disso, é gerenciado em um dicionário, economizando tanto o espaço, mas levando tempo para acessar.
Teste de matrizes esparsas e matrizes completas
Não pré-alocem matrizes grandes (como elementos maiores que 64k), seu tamanho máximo, mas devem ser alocadas dinamicamente. Antes de testar nosso desempenho neste artigo, lembre -se de que isso se aplica apenas a alguns motores JavaScript.
Literais vazios e matrizes pré-alocadas são testadas em diferentes navegadores
O Nitro (Safari) é mais benéfico para matrizes pré-alocadas. Em outros motores (V8, Spidermonkey), a pré-alocação não é eficiente.
Teste de matriz pré -alocada
// vazio Arrayvar arr = []; para (var i = 0; i <1000000; i ++) {arr [i] = i;} // Arrayvar pré-alocado ARR = nova matriz (1000000); para (var i = 0; i <1000000; i ++) {arr [i] = i;}No mundo dos aplicativos da web, a velocidade é tudo. Nenhum usuário deseja usar um aplicativo de tabela que leva segundos para calcular o número total de uma coluna ou resumir informações. Essa é uma razão importante pela qual você deseja espremer todo desempenho em seu código.
Fonte da imagem: por Olof Forsberg.
Compreender e melhorar o desempenho do seu aplicativo é muito útil, mas também é difícil. Recomendamos as seguintes etapas para resolver pontos de dor de desempenho:
Algumas das ferramentas e técnicas recomendadas abaixo podem ajudá -lo.
Existem muitas maneiras de executar uma referência para os trechos de código JavaScript para testar seu desempenho - a suposição geral é que a referência compara simplesmente dois registros de data e hora. Esse padrão é apontado pela equipe JSPERF e é usado no conjunto de benchmarks Sunspider e Kraken:
var totaltime, start = nova data, iterações = 1000; while (iterações--) {// snippet de código vai aqui} // TotalTime → o número de milhões de segundos tomados // para executar o snippet de código 1000 timestotaltime = nova data-start;Aqui, o código a ser testado é colocado em um loop e executa um número definido de vezes (por exemplo, 6 vezes). Depois disso, a data de início é subtraída a partir da data de término e o tempo necessário para executar a operação no loop é derivado.
No entanto, esse benchmarking faz coisas que são muito simples, especialmente se você deseja executar benchmarks em vários navegadores e ambientes. O próprio coletor de lixo tem um certo impacto nos resultados. Mesmo se você usar uma solução como janela.performance, essas deficiências devem ser levadas em consideração.
Independentemente de você executar apenas a parte de referência do código, escreva uma suíte de teste ou codifique a biblioteca de benchmark, os benchmarks JavaScript são realmente mais do que você pensa. Para benchmarks de guia mais detalhados, recomendo que você leia os benchmarks JavaScript fornecidos por Mathias Bynens e John-David Dalton.
As ferramentas de desenvolvedor do Chrome têm um bom suporte para o JavaScript Analytics. Você pode usar esse recurso para detectar quais funções ocupam a maior parte do tempo para que você possa otimizá -las. Isso é importante, mesmo pequenas mudanças no código podem ter um impacto significativo no desempenho geral.
Painel de análise de ferramentas de desenvolvedor de Chrome
O processo de análise começa a obter a linha de base do desempenho do código e o manifesta na forma de uma linha do tempo. Isso nos dirá quanto tempo o código levará para ser executado. A guia Perfis nos fornece uma perspectiva melhor sobre o que está acontecendo no aplicativo. Os arquivos de análise da CPU JavaScript mostram quanto tempo de CPU é usado em nosso código, os arquivos de análise de seletor de CSS mostram quanto tempo é gasto em seletores de processamento e os instantâneos de heap mostram quanta memória está sendo usada em nossos objetos.
Com essas ferramentas, podemos separar, ajustar e reanalitar para medir se nossas otimizações de desempenho funcional ou operacional são realmente eficazes.
A guia Perfil exibe informações de desempenho do código.
Uma boa introdução à análise, leia o perfil JavaScript de Zack Grossbart com as ferramentas de desenvolvedor do Chrome.
DICA: Idealmente, se você deseja garantir que sua análise não seja afetada por nenhum aplicativo ou extensão instalada, você pode usar o sinalizador --user-data-dir <empty_directory> para iniciar o Chrome. Na maioria dos casos, esse teste de otimização de métodos deve ser suficiente, mas também leva mais tempo para você. É isso que o logotipo V8 pode ajudar.
Dentro do Google, as ferramentas de desenvolvedor do Chrome são amplamente utilizadas por equipes como o Gmail para ajudar a detectar e solucionar problemas de memória.
Estatísticas de memória em ferramentas de desenvolvedor de cromo
Conta a memória do uso de memória privada, o tamanho da pilha de JavaScript, o número de nós DOM, limpeza de armazenamento, balcões de escuta de eventos e coletores de lixo com os quais nossa equipe está preocupada. LEITURA recomendada "3 instantâneos" da Loreena Lee. O ponto -chave dessa técnica é registrar algum comportamento em seu aplicativo, forçar a coleta de lixo, verificar se o número de nós DOM foi restaurado na linha de base esperada e, em seguida, analise os instantâneos dos três montes para determinar se há um vazamento de memória.
O gerenciamento de memória de aplicativos de página única (como AngularJS, Backbone, Ember) é muito importante, eles quase nunca atualizam a página. Isso significa que os vazamentos de memória podem ser bastante óbvios. Os aplicativos de página única nos terminais móveis estão cheios de armadilhas porque o dispositivo possui memória limitada e executa aplicativos como clientes de email ou redes sociais por um longo tempo. Quanto maior a habilidade, mais pesada é a responsabilidade.
Existem muitas maneiras de resolver esse problema. No backbone, certifique -se de usar o Dispon () para lidar com vistas e referências antigas (atualmente disponíveis no backbone (Edge). Essa função é adicionada recentemente, removendo o manipulador adicionado ao objeto "evento" da visualização, e o ouvinte de que o mais limpo e o que se lembra de que o Remonseutr -When, o que se lembra de que o Remonsewling, o que se lembra de que o Remonsewling, o que se lembra de que o mais, o que se lembra do que o mais, o que se lembra do que o mais, o que se lembra do que o mais, o que se lembra de que o mais, o que se lembra de que o mais, o que se lembra de que o mais, o que se lembra de que o mais, o que se lembra do que o mais, o que se lembra do que o mais, o que se lembra do que o mais, o que se lembra do que o mais, o que se lembra do que o mais, o que se lembra de que o mais, o que se lembra do terceiro parâmetro (contexto de chamada). até o ouvinte para evitar vazamentos de memória quando eles detectarem que os elementos são removidos.
Alguns conselhos sábios de Derick Bailey:
Em vez de entender como os eventos e referências funcionam, siga as regras padrão para gerenciar a memória em JavaScript. Se você deseja carregar dados em uma coleção de backbone cheia de objetos de usuário, deseja limpar a coleção para que não ocorra mais memória, todas as referências à coleção e referências aos objetos da coleção são necessários. Depois que a referência usada é clara, o recurso é reciclado. Esta é as regras padrão de coleta de lixo JavaScript.
No artigo, Derick cobre muitas falhas de memória comum ao usar o backbone.js e como resolver esses problemas.
O tutorial sobre vazamentos de memória de depuração no nó de Felix Geisendörfer também vale a pena ler, especialmente quando faz parte de uma pilha de spa mais ampla.
Quando o navegador renderiza os elementos em um documento, eles precisam ser recalculados e suas posições e geometria, que chamamos de refluxo. O Reflow bloqueia as operações dos usuários no navegador, por isso é muito útil entender que melhorar o tempo do refluxo é aprimorado.
Gráfico de tempo de reflexão
Você deve desencadear refluxo ou redesenhado em lotes, mas use esses métodos com moderação. Também é importante tentar não lidar com DOM. Você pode usar o DocumentFragment, um objeto de documento leve. Você pode usá -lo como uma maneira de extrair parte da árvore de documentos ou criar um novo documento "fragmento". Em vez de adicionar constantemente os nós DOM, é melhor executar operações de inserção DOM apenas uma vez depois de usar o fragmento de documentos para evitar o refluxo excessivo.
Por exemplo, escrevemos uma função para adicionar 20 divs a um elemento. Se você simplesmente anexar uma div ao elemento a cada vez, isso acionará 20 reflexos.
função adddivs (elemento) {var div; for (var i = 0; i <20; i ++) {div = document.createElement ('div'); div.innerhtml = 'Heya!'; element.appendChild (div); }}Para resolver esse problema, podemos usar o DocumentFragment, podemos adicionar um novo Div a ele por vez. A adição de documentários ao DOM após a conclusão acionará apenas um reflexão uma vez.
função adddivs (elemento) {var div; // cria um novo documento vazio. var fragment = document.createCumentFragment (); for (var i = 0; i <20; i ++) {div = document.createElement ('a'); div.innerhtml = 'Heya!'; fragment.appendchild (div); } Element.appendChild (fragmento);}Consulte Torne a Web mais rápida, o otimização da memória JavaScript e a localização de vazamentos de memória.
Para ajudar a descobrir vazamentos de memória JavaScript, os desenvolvedores do Google (Marja Hölttä e Jochen Eisinger) desenvolveram uma ferramenta que funciona em conjunto com as ferramentas de desenvolvedor do Chrome para recuperar instantâneos da pilha e detectar quais objetos estão causando o vazamento de memória.
Uma ferramenta de detecção de vazamento de memória JavaScript
Há um artigo completo sobre como usar essa ferramenta. É recomendável que você vá para a página do projeto de detector de vazamento de memória para si mesmo.
Se você quiser saber por que essas ferramentas não foram integradas em nossas ferramentas de desenvolvimento, há dois motivos. Foi originalmente projetado para nos ajudar a capturar alguns cenários de memória específicos na biblioteca de fechamento, o que é mais adequado como uma ferramenta externa.
O Chrome suporta passar alguns sinalizadores diretamente para o V8 para obter resultados de saída de otimização do motor mais detalhados. Por exemplo, isso pode rastrear a otimização do V8:
"/Applications/Google Chrome/Google Chrome" --js-flags = "-Trace-Opt-Trace-deopt"
Os usuários do Windows podem executar o Chrome.exe js-flags = "Trace-Opt Trace-deopt"
Ao desenvolver um aplicativo, o logotipo V8 abaixo pode ser usado.
O script de processamento do V8 usa * (Asterisk) para identificar funções e usos otimizados ~ (WAVY) para representar funções não otimizadas.
Se você estiver interessado em aprender mais sobre o logotipo do V8 e como funciona o interior do V8, é altamente recomendável ler o excelente post de Vyacheslav Egorov sobre os internos do V8.
O tempo de alta precisão (HRT) é uma interface de tempo de alta precisão no nível submillissegundos que não oferece impacto nos ajustes do tempo e do usuário. Pode ser considerado um método de medição mais preciso do que a nova data e data.now (). Isso nos ajuda muito a escrever benchmarks.
O tempo de alta precisão (HRT) fornece precisão atual de tempo de sub-milissegundos
Atualmente, a HRT é usada no Chrome (versão estável) em window.performance.webkitnow (), mas o prefixo é descartado no Chrome Canary, o que torna possível chamar a Window.performance.now (). Paul Irish postou mais sobre HRT em HTML5Rocks.
Agora que conhecemos o tempo preciso atual, existe uma API que pode medir com precisão o desempenho da página? Well, now there is a Navigation Timing API that provides an easy way to get accurate and detailed time measurement records when web pages are loaded and presented to users. You can use window.performance.timing in console to get time information:
显示在控制台中的时间信息
我们可以从上面的数据获取很多有用的信息,例如网络延时为responseEnd fetchStart,页面加载时间为loadEventEnd responseEnd,处理导航和页面加载的时间为loadEventEnd navigationStart。
正如你所看到的,perfomance.memory的属性也能显示JavaScript的内存数据使用情况,如总的堆大小。
更多Navigation Timing API的细节,阅读Sam Dutton的Measuring Page Load Speed With Navigation Timing。
Chrome中的about:tracing提供了浏览器的性能视图,记录了Chrome的所有线程、tab页和进程。
About:Tracing提供了浏览器的性能视图
这个工具的真正用处是允许你捕获Chrome的运行数据,这样你就可以适当地调整JavaScript执行,或优化资源加载。
Lilli Thompson有一篇写给游戏开发者的使用about:tracing分析WebGL游戏的文章,同时也适合JavaScript的开发者。
在Chrome的导航栏里可以输入about:memory,同样十分实用,可以获得每个tab页的内存使用情况,对定位内存泄漏很有帮助。
我们看到, JavaScript的世界中有很多隐藏的陷阱,且并没有提升性能的银弹。只有把一些优化方案综合使用到(现实世界)测试环境,才能获得最大的性能收益。即便如此,了解引擎是如何解释和优化代码,可以帮助你调整应用程序。
测量,理解,修复。不断重复这个过程。
图片来源: Sally Hunter
谨记关注优化,但为了便利可以舍弃一些很小的优化。例如,有些开发者选择.forEach和Object.keys代替for和for..in循环,尽管这会更慢但使用更方便。要保证清醒的头脑,知道什么优化是需要的,什么优化是不需要的。
同时注意,虽然JavaScript引擎越来越快,但下一个真正的瓶颈是DOM。回流和重绘的减少也是重要的,所以必要时再去动DOM。还有就是要关注网络,HTTP请求是珍贵的,特别是移动终端上,因此要使用HTTP的缓存去减少资源的加载。
Remembering these points can ensure that you have obtained most of the information in this article. Espero que seja útil para você!
原文:http://coding.smashingmagazine.com/2012/11/05/writing-fast-memory-efficient-javascript/
作者:Addy Osmani