Is there a way cleanly use hidden classes in javascript when you dont know what the properties will be? Is there a way cleanly use hidden classes in javascript when you dont know what the properties will be? json json

Is there a way cleanly use hidden classes in javascript when you dont know what the properties will be?


You mean something like this right?

function toHiddenObjectArray(attrNames, attrValues){    var numAttrNames = attrNames.length,        numValues = attrValues.length;    function Data( values ) {        for(var v = 0; v < numAttrNames; v++) {            this[attrNames[v]] = values[v];        }    }    var ret=[];    for( var i=0; i<numValues ; i+=numAttrNames ) {        ret.push( new Data( attrValues.slice(i,i+numAttrNames) ) );    }    return ret;}

You can check our the fiddle here: http://jsfiddle.net/B2Bfs/ (With some comparison code). It should use the same "Hidden Class" (i.e. Data). Not sure how much quicker it is though!

But, if you really want to make your code none blocking, why not load the page, then request the data via AJAX, then run all you code when you get a response.


I can get some of this boost using "eval" to create objects, but this feels ugly

There's a less ugly way using the Function constructor. Also, further optimisations can be done by immediately assigning the values to the properties, instead of initialising them with null and then again iterating through the attrs array like the adHoc does it. You'd just pass each of the rows you get in the response (array? string? byte-whatever?) as a parameter to the factory.

Also I've moved the creation of the factory function out of the create function, so that only one function will be instantiated (and optimized after enough calls to it).
A decent amount of the time in your test loop is spent on the getTotal, so I've optimised this in a similar manner. Not using getTotalAdHoc in testing the optimised solution drastically reduces the measured time (you can test with getTotalOptimum as well).

var factory = new Function("arr", "return{"+attrs.map(function(n, i){    return n+":arr["+i+"]";}).join(",")+"};");var getSum = new Function("o","return "+attrs.map(function(n, i){    return "o."+n;}).join("+")+";");

(updated jsfiddle)

I haven't yet tried moving the complete loop into the generated code, which could avoid a few function calls, but I don't think this is necessary.


For some reason I just recalled this question... and I just came up with a solution that is way dirtier than using eval but which causes a huge speed boost. The downside of it is that code will be similarly little maintainable as when using eval.

The basic idea is: When receiving the attribute names, generate the function code to parse the following data in JavaScript and add it in a <script> tag to the <head>.

Yeah, isn't that dirty? :-)

If performance is so critical for you, it will definitely help you... here's a modified version of your microbenchmak that proves it: http://jsfiddle.net/N6CrK/17/


Some remarks on the code...

The two functions createWithGeneratedFunction and getTotalWithGeneratedFunction are simply wrapper functions that can be used by productive code. All they do is make sure that the <script> with the generated functions is set up and then call it.

function createWithGeneratedFunction(numValues){    makeSureScriptsAreSetUp()    return createWithGeneratedFunctionAdded(numValues);}function getTotalWithGeneratedFunction(objs){    makeSureScriptsAreSetUp()    return getTotalWithGeneratedFunctionAdded(objs);}

The actual workhorse is the makeSureScriptsAreSetUp with the functions it creates. I'll go through it line by line:

function makeSureScriptsAreSetUp() {    if(scriptIsSetUp)        return;

If the required <script> tag was already set up this function will directly return since there is nothing to do for it anymore.

    var head = document.getElementsByTagName('head')[0];    var script = document.createElement('script');    var theFunctions = "";

This prepares the creation of the required functions. The theFunctions variable will be filled with the code that is going to be put into the <script> tag content.

    theFunctions =         "function createWithGeneratedFunctionAdded(numValues) {" +        "    var ret = [];" +        "    var value = 0;" +        "    for(var i = numValues; i-- > 0;) {" +        "        ret.push({";    for(var attr in attrs) {        theFunctions +=        "            " + attrs[attr] + ": value++,";    }    theFunctions +=        "        });" +        "    }" +        "    return ret;" +        "}" +        "";

This completes the code for the parsing function. Obviously it just "parses" the numbers 0 to numValues in this microbenchmark. But replacing value++ with something like TheObjectThatTheClientSentMe.values[value++] should bring you very close to what you outlined in your question. (Obviously it would make quite a lot of sense to rename value to index then.)

    theFunctions +=        "function getTotalWithGeneratedFunctionAdded(objs) {" +        "    var ret = 0;" +        "    for(var i = objs.length; i-- > 0;) {" +        "        var obj = objs[i];" +        "        ret += 0";    for(var attr in attrs) {        theFunctions +=        "            + obj." + attrs[attr];    }    theFunctions +=        "        ;" +        "    }" +        "    return ret;" +        "}";

This completes the code for the processing function. Since you seem to require several processing functions, especially this code could become somewhat ugly to write and maintain.

    script.text = theFunctions;    head.appendChild(script);    scriptIsSetUp = true;}

In the very end we simply set the <script> tag content to the code we just created. By then adding that tag to the <head>, Chrome's hidden class magic will occur and will make the code VERY fast.


Concerning extensibility: If you have to query different attribute/value sets from the server on the same page, you might want to give each parsing/processing method set unique names. For example, if you first receive attrs = ["foo","bar"] and next attrs = ["foo","bar","baz"] you could concat the underscore-joined attribute name array to the generated function names.

For example, instead of using createWithGeneratedFunctionAdded you could use createWithGeneratedFunctionAdded_foo_bar for the first attribute/value set and createWithGeneratedFunctionAdded_foo_bar_baz for the second attribute/value set. An attr parameter could then be added to the wrapper functions that will be used to generate the correct code line for an eval (yes, here the evil eval would return) to trigger the correct generated function. Obviously, the attr parameter would also be required for the makeSureScriptsAreSetUp function.