O JavaScript surgiu em 1996 no Netscape Navigator, num contexto onde otimização não era algo muito considerado pelos desenvolvedores, embora as páginas levassem bastante tempo para carregar.
Seu surgimento veio com
o propósito de melhorar a experiência do usuário, realizando tarefas mais simples diretamente na página ao invés de utilizar o servidor para isso (imagina você preencher um formulário, esperar de 30s a 60s pra enviá-lo após submeter, e depois receber uma validação de que um campo não foi preenchido?! pois é!) Nessa época o JS era utilizado meramente para enviar mensagens.
Com a chegada do IE4 no mercado e o
dynamic HTML (capacidade de alterar elementos das páginas sem precisar recarregá-las), a quantidade de JS nas páginas só aumentou. Em seguida, veio a introdução do DOM (Document Object Model), que nada mais é do que uma abordagem unificada do DHTML que foi adotada pelo IE5, Netscape 6 e Opera, o que logo foi acompanhado pela padronização do JavaScript no ECMA-262 (3ª edição).
À medida em que estes progressos iam acontecendo, sistemas web mais complexos iam surgindo e aumentava a quantidade de código JS nas suas páginas. O problema é que a engine do JS ainda era a mesma de 1996, onde as páginas ainda não eram tão recheadas de JS. Browsers como o IE6 (se é que podemos chamá-lo de browser rs), que surgiu em 2001 se tornaram mais lentos com o passar dos anos pois as páginas que iam surgindo utilizavam cada vez mais códigos javascript, e como
a engine era muito antiga, não estava preparada. Em suma, ela
trabalhava de forma a procurar um número fixo de objetos na memória para determinar quando deveria coletar o lixo. Nos primórdios do JS, os desenvolvedores não atingiam este limite, mas com o aumento de código JS nas páginas, a quantidade de objetos aumentou e aplicações mais complexas foram sendo desenvolvidas, então este limite passou a ser atingido com frequência, exigindo cada vez mais dos browsers.
Os desenvolvedores web evoluíram, mas as
engines estavam paradas no tempo.
Embora os diferentes browsers da época tivessem lógicas diferentes (e até um pouco mais eficientes) para lidar com o problema da coleta de lixo,
a maioria deles utilizava um interpretador JavaScript para executar o código. Como se sabe, a interpretação de código exige a tradução entre o código e e as instruções do computador que devem ser executadas, o que é um processo bem mais lento, se comparado com a compilação.
Compiladores já funcionam diferente. Eles determinam a partir de uma análise léxica, o que o código está tentando fazer e então otimiza-o produzindo um código mais rápido para a execução da tarefa.
Em 2008 o Google lançou o Chrome, o primeiro navegador com uma
engine otimizadora de JavaScript, o
V8, o qual faz a compilação 'just-in-time' do JS e é capaz de produzir código de máquina a partir de JS e executá-lo, resultando na sua execução de forma bem mais performática. Seguindo essa idéia novas engines de JS vieram surgindo e com o passar dos anos evoluindo, como o Nitro do safari, o traceMonkey, JaegerMonkey SpiderMonkey, etc..
Mesmo com estes avanços no tempo de execução no core do JavaScript, ainda há situações que as
engines modernas não conseguem resolver, como operações que afetam a aparência das páginas, atrasos provocados pela latência da rede, etc. Assim, o desenvolvedor JavaScript precisa buscar conhecimento para entender varios aspectos diferentes, como latência, bloqueio e downloads em paralelo, interação com o DOM, etc, preocupando-se sempre com a melhor forma de escrever seu código, para obter um melhor desempenho na execução de seu código, melhorando por consequência a experiencia do usuário ao navegar nas páginas.