ES6 Tranceur vs Desempenho Javascript Padrão

ES6 traz alguns recursos há muito desejados pelos engenheiros de Javascript há algum tempo. Traceur é uma ferramenta de código aberto para compilar ES6 em javascript padrão e oferece uma proposta atraente – ser capaz de escrever seu javascript em ES6 e convertê-lo para ES5 antes de enviá-lo. Quatro pontos importantes a serem avaliados ao adotar a tecnologia são:

  1. Funciona?
  2. É robusto?
  3. É confiável?
  4. Qual é seu desempenho em relação ao que está lá fora e ao que você está usando atualmente.

Estive pensando em escrever mais coisas pessoais no ES6, mas queria avaliar o desempenho em relação ao Javascript padrão. Este post específico dará uma breve olhada no # 4 quando submetido a um pequeno teste de estresse no Firefox. Os testes serão em relação ao ES6 pré-compilado, e não interpretados em tempo de execução.

O código

Javascript padrão:

function Factory(operation){

for(var n in operation) this[n] = operation[n];

this.units = [];
this.boxes = [];

};

Factory.prototype = {

startProduction
: function(){

var _colors = ['black', 'white', 'silver', 'blue', 'red'],
_prices
= [299.99, 299.99, 299.99, 349.99, 349.99],
_key
= 0;

for(var i = 0; i < 100; i++){

_key
= i%5;

this.make({

color
: _colors[_key],
price
: _prices[_key]

});

}

return this.package();

},

make
: function(build){

this.units.push(new this.item({

brand
: this.brand,
type
: this.type,
color
: build.color,
price
: build.price,
tax
: this.tax

}));

},

package: function(){

var _box = [];

for(var i = 1, l = this.units.length + 1; i < l; i++){

if(!(i%10)){

this.boxes.push(_box);
_box
=[];

}

_box
.push(this.units[i]);

};

return function(){

return this.boxes;

}.bind(this);

},

};

function Item(requirements){

for(var n in requirements) this[n] = requirements[n];

this.salesPrice = this.calculatePrice();

};

Item.prototype = {

calculatePrice
: function(){

return this.price * (1 + this.tax);

}

};

var factory = new Factory({

brand
: 'Sony',
type
: 'Tablet',
tax
: .11,
item
: Item

});

for(var i = 0; i < 500; i++){

var output = factory.startProduction();
output
();

}

ES6 pré-compilado

class FactoryBase {

startProduction
(){

var [_colors, _prices, _key] = [['black', 'white', 'silver', 'blue', 'red'], [299.99, 299.99, 299.99, 349.99, 349.99], 0];

for(let i = 0; i < 100; i++){

_key
= i%5;

this.make({

color
: _colors[_key],
price
: _prices[_key]

});

}

return this.package();

}

make
(build){

this.units.push(new this.item({

brand
: this.brand,
type
: this.type,
color
: build.color,
price
: build.price,
tax
: this.tax

}));

}

'package'(){

var _box = [];

for(let i = 1, l = this.units.length + 1; i < l; i++){

if(!(i%10)){

this.boxes.push(_box);
_box
=[];

}

_box
.push(this.units[i]);

};

return () => {

return this.boxes;

}

}

};

class Factory extends FactoryBase{

constructor(operation){

for(let n in operation) this[n] = operation[n];

this.units = [];
this.boxes = [];

}

}

class ItemBase {

calculatePrice
(){

return this.price * (1 + this.tax);

}

}

class Item extends ItemBase {

constructor(requirements){

for(var n in requirements) this[n] = requirements[n];

this.salesPrice = this.calculatePrice();

}

}

var factory = new Factory({

brand
: 'Sony',
type
: 'Tablet',
tax
: .11,
item
: Item

});

for(var i = 0; i < 500; i++){

var output = factory.startProduction();
output
();

}

O que esses dois scripts estão fazendo é gerar uma matriz de 10 matrizes, cada uma com 10 instâncias do Item 500 vezes. O objetivo é medir a instanciação em relação às classes ES6, o efeito do uso de vínculos com escopo de bloco, atribuição desestruturada e funções de seta. Então, como eles se saíram?

Os resultados

Cenário

À primeira vista, sua impressão inicial provavelmente será a de que você nunca está usando o Traceur. Neste teste inicial, ele foi executado 550% mais lento do que o Javascript padrão. Então, o que dá? Bem, acontece que o uso de “let” exige que o Traceur implemente algum código sério para reforçar o escopo. Esse loop for em startProduction () é convertido em:

try {
throw undefined;
} catch ($i) {
$i
= 0;
for (; $i < 100; $i++) {
try {
throw undefined;
} catch (i) {
i
= $i;
try {
_key
= i % 5;
this.make({
color
: _colors[$traceurRuntime.toProperty(_key)],
price
: _prices[$traceurRuntime.toProperty(_key)]
});
} finally {
$i
= i;
}
}
}
}
}

Provavelmente é por isso que as ligações de bloco não são habilitadas por padrão e requerem um sinalizador extra durante a compilação. Vamos executar o teste novamente, mas trocar “let” por “var”:

Cenário

Os resultados parecem muito melhores desta vez, apenas duas vezes mais lentos. Os outros recursos são compilados de uma forma que não levanta minha sobrancelha de que está afetando o desempenho.

Conclusão

Olhando para os resultados desses testes rápidos, minha opinião é que o Traceur não é adequado para aplicativos como jogos ou qualquer coisa altamente computacional, mas deve funcionar bem para implementações de IU e scripts padrão. A principal vantagem, a meu ver, é que quando chegar a hora inevitável de mover tudo para a ES6, você já estará na metade do caminho para a linha de chegada. Apenas certifique-se de evitar ligações de bloco, é bom declarar “let”, mas definitivamente não vale a pena até que seja implementado nativamente.