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:
- Funciona?
- É robusto?
- É confiável?
- 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
À 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”:
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.