jsPerf.app is an online JavaScript performance benchmark test runner & jsperf.com mirror. It is a complete rewrite in homage to the once excellent jsperf.com now with hopefully a more modern & maintainable codebase.
jsperf.com URLs are mirrored at the same path, e.g:
https://jsperf.com/negative-modulo/2
Can be accessed at:
https://jsperf.app/negative-modulo/2
class Template2 {
findvariables = /{(?<variable>[^block:|\/block:].+)}/gmd;
findblocks = /(?<block>[^\S\r\n]*?{block:(?<tagname>.+)}(?:\s+?)(?<content>[\s\S]*?)(?:\s+?){\/block:\k<tagname>})/gmd;
structure = [];
constructor(string) {
this.load(string);
}
load(string) {
this.structure = this.flatten(this.getblocks(string));
}
getvariables(string, structure=[]) {
let matches = [...string.matchAll(this.findvariables)];
let index = 0;
if (matches.length == 0) {
return string;
}
for (const match of matches) {
const variable = match.groups.variable;
const indices = match.indices.groups.variable;
const startindex = indices[0] - 1; // remember the curly braces on either side of the variable
const endindex = indices[1] + 1;
// get standalone string
let standalonestring = string.slice(index, startindex);
// standalone string cannot have any variables in it, since it's already excluded from the findvariables regex matches
structure.push(standalonestring);
structure.push([[variable]]);
index = endindex;
}
if (index < string.length) {
structure.push(string.slice(index));
}
return structure;
}
getblocks(string, structure=[]) {
let matches = [...string.matchAll(this.findblocks)];
let index = 0;
if (matches.length == 0) {
return this.getvariables(string);
}
if (matches.length) {
for (const match of matches) {
const groups = match.groups;
const indices = match.indices.groups;
const startindex = indices.block[0];
const endindex = indices.block[1];
// check standalone string for variable substitutions
let standalonestring = this.getvariables(string.slice(index, startindex));
structure.push(standalonestring);
structure.push({block: [groups.tagname, this.getblocks(groups.content)]});
index = endindex;
}
if (index < string.length) {
structure.push(this.getvariables(string.slice(index)));
}
} else {
return this.getvariables(string.slice)
}
return structure;
}
getFunction2(string) {
return new Function('d', `for (let p in d) { eval('var ' + p + ' = ' + JSON.stringify(d[p]) + ';' )}; ` + 'return `${(' + string + ' ?? ("{" + "' + string + '" + "}"))}`');
}
getFunctionBlock(string) {
return new Function('d', 'return `${(d?.' + string + ' == true)}`');
}
flatten(structure=[]) {
const structure_length = structure.length;
let output = [];
for (let i=0; i < structure_length; i++) {
const item = structure[i];
if (item && typeof item === "string") {
output.push(0, item);
continue;
} else if (item?.length == 1 && item[0]?.length == 1) {
output.push(1, this.getFunction2(item[0][0]));
} else if (item.block) {
const blockcontent = this.flatten(item.block[1]);
const len = 3 + blockcontent.length;
output.push(2, this.getFunctionBlock(item.block[0]), len);
output = output.concat(blockcontent);
}
}
return output;
}
_render(options={}) {
let output = '';
const len = this.structure.length;
const flat = this.structure;
let i=0;
while (i < len)
{
const item = flat[i];
const content = flat[i + 1];
switch (item) {
case 0:
output += content;
i += 2;
break;
case 1:
output += content(options); //print variable name if not found in options
i+=2;
break;
case 2:
i += content(options) ? 3 : flat[i + 2];
break;
}
}
return output;
}
render(options={}) {
return this._render.bind(this)(options);
}
}
class Template {
findvariables = /{(?<variable>[^block:|\/block:].+)}/gmd;
findblocks = /(?<block>[^\S\r\n]*?{block:(?<tagname>.+)}(?:\s+?)(?<content>[\s\S]*?)(?:\s+?){\/block:\k<tagname>})/gmd;
structure = [];
constructor(string) {
this.load(string);
}
load(string) {
this.structure = this.flatten(this.getblocks(string));
}
getvariables(string, structure=[]) {
let matches = [...string.matchAll(this.findvariables)];
let index = 0;
if (matches.length == 0) {
return string;
}
for (const match of matches) {
const variable = match.groups.variable;
const indices = match.indices.groups.variable;
const startindex = indices[0] - 1; // remember the curly braces on either side of the variable
const endindex = indices[1] + 1;
// get standalone string
let standalonestring = string.slice(index, startindex);
// standalone string cannot have any variables in it, since it's already excluded from the findvariables regex matches
structure.push(standalonestring);
structure.push([[variable]]);
index = endindex;
}
if (index < string.length) {
structure.push(string.slice(index));
}
return structure;
}
getblocks(string, structure=[]) {
let matches = [...string.matchAll(this.findblocks)];
let index = 0;
if (matches.length == 0) {
return this.getvariables(string);
}
if (matches.length) {
for (const match of matches) {
const groups = match.groups;
const indices = match.indices.groups;
const startindex = indices.block[0];
const endindex = indices.block[1];
// check standalone string for variable substitutions
let standalonestring = this.getvariables(string.slice(index, startindex));
structure.push(standalonestring);
structure.push({block: [groups.tagname, this.getblocks(groups.content)]});
index = endindex;
}
if (index < string.length) {
structure.push(this.getvariables(string.slice(index)));
}
} else {
return this.getvariables(string.slice)
}
return structure;
}
getFunction2(string) {
return new Function('d', 'return `${(d?.' + string + ' ?? ("{" + string + "}"))}`');
}
getFunctionBlock(string) {
return new Function('d', 'return `${(d?.' + string + ' == true)}`');
}
flatten(structure=[]) {
const structure_length = structure.length;
let output = [];
for (let i=0; i < structure_length; i++) {
const item = structure[i];
if (item && typeof item === "string") {
output.push(0, item);
continue;
} else if (item?.length == 1 && item[0]?.length == 1) {
output.push(1, item[0][0]);
} else if (item.block) {
const blockcontent = this.flatten(item.block[1]);
const len = 3 + blockcontent.length;
output.push(2, item.block[0], len);
output = output.concat(blockcontent);
}
}
return output;
}
_render(options={}) {
let output = '';
const len = this.structure.length;
const flat = this.structure;
let i=0;
while (i < len)
{
const item = flat[i];
const string = flat[i + 1];
switch (item) {
case 0:
output += string;
i += 2;
break;
case 1:
output += options?.[string] ?? '{' + string + '}'; //print variable name if not found in options
i+=2;
break;
case 2:
i += options?.[string] ? 3 : flat[i + 2];
break;
}
}
return output;
}
render(options={}) {
return this._render.bind(this)(options);
}
}
const string = `
{block:ShowAvatar}
<div class="portrait {AvatarShape}">
<img src="{PortraitURL_128}" />
</div>
<div class="name">{Title}</div>
{/block:ShowAvatar}`;
const template = new Template(string);
const template2 = new Template2(string);
Ready to run.
Test | Ops/sec | |
---|---|---|
old render |
| ready |
new render |
| ready |
You can edit these tests or add more tests to this page by appending /edit to the URL.