Canvas arrays needs to fire events when they are modified so others can sync with them and expose extra functions to simplify
their use: add, remove, replace. Canvas arrays are Array-like objects, all the standard array functions are exposed and you can use them as you will use normal array:
// Plain iterationvar figures = group.figures();for( var n = 0; n < figures.length; n++ ) { var f = figures[n]; // ...}// Lo-dash iteration_.forEach(group.figures(),function(f){ // ... });
Only the a[n] = o
write interface needs to be avoided. Instead you need to use replaceAt
or at
.
// Wrong!figures[2] = text; // Good...figures.replaceAt( 2, text );
The problem with using figures[2] = o
, will just modify the 2
property of figures and nobody will know that this change has happened. For example, when you use replaceAt, there is code that deletes the old figure view and adds a new one (all this sync code is handled behind the scene). There is also another problem, if you set figures[6] in a 4 element array, the length should change but it will still be 6.
There are running standards to try to identify array like objects so it is easier to code utility libraries that are supposed to work with them. Underscore.js takes the approach of defining that if object.length is a positive number, then the object is array like. You will see checks in their code base like: obj.length === +obj.length
.
Lo-dash and Undercore.js and other functional libraries do not modify the arrays you pass to them, they create new arrays with the results (_.map
, _.filter
, etc). Even things like _.shuffle
. An array-like object then works fine on them, you can filter the arguments object for example, or a jQuery array.
Rationale
In Javascript we can not create a class that derives from an Array object, with different compromises. We played with that ideas but we couldn’t impose the compromises they propose to Canvas (the closest one).
jQuery actually uses something similar to this, at least in the final structure of their arrays.
var buttons = $('.button');
buttons is an Array Like object (like the ‘arguments’ function var). It has a length integer property, and their elements can be accessed using buttons[n] as a normal array. Because of this, every library that is coded to be able to handle javascript array like objects will work perfectly with them. You can use:
_.forEach( buttons, ... );
jQuery is not deriving from Array. Their array like object is actually just a normal object that has a length property and a ‘0’, ‘1’, ‘2’, etc property. Like this:
var jArray = { length: 3, '0': a, '1': b, '2': c };
Or equivalent:
var jArray = {};jArray.length = 3;jArray[0] = a;jArray[1] = b;jArray[2] = c;
So this works fine:
for( var n = 0; n < jArray.length; n++ ) { var v = jArray[n]; // ...}
Performance
Ecen if using an object {}
instead of an array []
has performance implications, the best argument to show that it is not that bad is that jQuery arrays are actually created like this. There is a test page in jsPerf. You can look at the bar graphs, the three first are Array Like (assign, create and for each), the second three are Native Array. Some conclusions:
1) Chrome is awesome. Using plain objects is actually faster than native arrays.
2) Firefox also have better iteration and assign behavior in Array like objects.
3) IE is more consistent with ~2.5x performance penalty.
The test are using 1000 elements arrays, quite above the mean size of our own arrays (10 figures are big arrays in our system). So it seems that the price tag is quite low, given that the iteration cost almost disappear in the big picture of the computations Canvas or clients are performing.