Skip to content

Instantly share code, notes, and snippets.

@fredck
Created May 11, 2020 11:34
Show Gist options
  • Save fredck/f89ecaad2ba99d40c6b274efbf6369f0 to your computer and use it in GitHub Desktop.
Save fredck/f89ecaad2ba99d40c6b274efbf6369f0 to your computer and use it in GitHub Desktop.
Test for different strategies for dumping an in-memory model representation
// Case 1:
// - format: native DOM (documnet fragment with a clone of body contents)
// - output: outerHTML call
{
const doc = new DocumentFragment();
doc.append( document.createElement( 'root' ) );
Array.from( document.body.children ).forEach( node => doc.firstElementChild.append( node.cloneNode( true ) ) );
// innerHTML gives the very similar time, so let's go with outerHTML to have a more complete structure.
console.time( 'outerHTML time' );
const len = doc.firstElementChild.outerHTML.length;
console.timeEnd( 'outerHTML time' );
console.log( 'outerHTML length: ' + len );
}
// Case 2:
// - format: native JS object
// - output: JSON.stringify( object )
{
const root = { type: 'root' };
appendChildren( document.body, root );
function appendChildren( source, target ) {
if ( !source.children.length ) {
return;
}
target.children = Array.from( source.childNodes ).map( node => {
const child = { type: node.nodeType };
if ( node.nodeType === Node.ELEMENT_NODE ) {
child.name = node.nodeName;
if ( node.attributes.length ) {
child.attribs = Array.from( node.attributes ).reduce( ( attribs, att ) => {
attribs[ att.name ] = att.value;
return attribs;
}, {} );
}
appendChildren( node, child );
} else {
child.text = node.textContent
}
return child;
} );
}
// innerHTML gives the very similar time, so let's go with outerHTML to have a more complete structure.
console.time( 'JSON time' );
const len = JSON.stringify( root ).length;
console.timeEnd( 'JSON time' );
console.log( 'JSON length: ' + len );
}
// Case 3:
// - format: "almost string" array tree
// - output: array.flat( Infinity ).join( '' )
{
const rootChildren = Array.from( document.body.childNodes ).map( child => getDefinition( child ) );
const root = [ '<root>', ...rootChildren, '</root>' ];
console.time( 'XML Array time' );
const len = root.flat( Infinity ).join( '' ).length;
console.timeEnd( 'XML Array time' );
console.log( 'XML Array length: ' + len );
function getDefinitionStart( node ) {
let type;
const attribs = [];
if ( node.nodeType !== Node.ELEMENT_NODE ) {
type = 'text';
} else {
type = `element`;
attribs.push( ' name="', escapeAttrib( node.nodeName ), '"' );
if ( node.attributes.length ) {
const attribsObj = Array.from( node.attributes ).reduce( ( attribs, att ) => {
attribs[ att.name ] = att.value;
return attribs;
}, {} );
attribs.push( ' attribs="', escapeAttrib( JSON.stringify( attribsObj ) ), '"' );
}
}
return `<${ type }${ attribs.join( '' ) }>`;
}
function getDefinition( node ) {
let type, children;
if ( node.nodeType !== Node.ELEMENT_NODE ) {
type = 'text';
children = [ escapeText( node.textContent ) ];
} else {
type = `element`;
children = node.childNodes.length ?
Array.from( node.childNodes ).map( child => getDefinition( child ) ) :
[];
}
const definition = [
getDefinitionStart( node ),
...children,
`</${ type }>`
];
definition.type = type;
return definition;
}
function escapeText( value ) {
return value
.replace( /&/g, '&amp;' )
.replace( /</g, '&lt;' );
}
function escapeAttrib( value ) {
return value
.replace( /&/g, '&amp;' )
.replace( /</g, '&lt;' )
.replace( /"/g, '&quot;' );
}
}
@fredck
Copy link
Author

fredck commented May 11, 2020

Just paste this in the console and execute. It'll output timer results for the different strategies.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment