Wednesday, July 16, 2008

JavaScript Relator Object, aka unobtrusive informations

Have you never though about add, modify, or remove a generic information from a variable, and without changing variable itself?
This is all about what my last simple creation does.



Relator Object Concept


The main purpose for this object is to associate every kind of information, value, or function, into a generic variable, whatever it is, and without modifying its native state.
To obtain this result, I have used a 1:1 relationship between a stack that will contain every stored variable, and another one that will contain related objects.

Stack = [1, 2, 3];
RelatedObject = [{}, {}, {}];

// add a property
RelatedObject[Stack.indexOf(2)].description = "Number 2";

// remove a property
Stack.splice(0, 1);
RelatedObject.splice(0, 1);

// situation
Stack = [2, 3];
RelatedObject = [{description:"Number 2"}, {}];

Using above strategy we obtain 2 benefits:

  1. The Stack does not cause memory leaks

  2. both Stack and RelatedObject are alway as small, and fast, as possible





Related Object Code



var Relator = function(Array, Object){
// (c) Andrea Giammarchi - Mit Style License
if(!Array.indexOf)
Array.indexOf = function(value){
for(var i = 0, length = Array.length; i < length; i++)
if(Array[i] === value)
return i;
return -1
};
return {
get:function(value){
return Object[Array.indexOf(value)]
},
set:function(value){
var i = Array.indexOf(value);
return ~i ? Object[i] : Object[Array.push(value) - 1] = {}
},
del:function(value){
var i = Array.indexOf(value);
if(~i){
Array.splice(i, 1);
Object.splice(i, 1);
};
return this
}
}
}([], []);

I hope, and I suppose, the 3 methods API talks by itself.
set is used to create a relation, if this does not exists, returning associated object.
get is used only to get a relation or undefined, if this does not exist.
Finally, del, is used to delete the relation, reducing the Array size, and deleting related Object.



Some Example


Try to imagine that we are using a library, but we would be able to add any sort of info about them, or implement something for our purpose.

var jQueryMore = Relator.set(jQuery);
jQueryMore.details = "jQuery library";
jQueryMore.isCompatible = function(){
return window.$ === jQuery;
};

// in every other piece of code ...
if(Relator.get(jQuery).isCompatible())
alert("I am using the " + Relator.get(jQuery).details);
// I am using the jQuery library




Relator Performances


IE a part, since it still does not implement natively the old indexOf Array method, performance to set, access, modify, or delete related informations, are probably the best possible, closes to zero value.
We can test by ourself using 10000 stored relations, and accessing to the last one.

for(var i = 0; i < 10000; i++)
Relator.set("number " + i).description = "The " + i + " number";

time = new Date;
description = Relator.get("number " + 9999).description;
time = new Date - time;
alert([description, time]);




Conclusion


The Relator object is based on our own variables, and it is not a container, a register, or a IOC emulator, at all.
At the same time, to be able to relate an object with a generic variable (undefined included, as example), it needs to store them in the Stack.
This could be a problem for memory leaks, but only if we forget to use the del method, to remove the assigned, and protected, relation between our global scope whatever, and the internal object dedicated relation.
Applications? In my mind, this object could make a lot of stuff simpler than ever, specially in those case where we would like, for example, monitor variables, singleton or global instances, during our script life :)

5 comments:

R M said...

Great idea, as always :)

But if someone were to just plop that into their code without thinking with their head, they will wake up to find some strange bugs.

They are really not strange at all, but simply a limitation of JavaScript.

Any non-unique variables wind up sharing their descriptions. Also, changing a primitive* variable before putting it thru the Relator again will result in not getting the correct information.

For DOM elements on the other hand, it is much safer to use, since they may have style or attributes that differ.

*to be honest, i don't know if that is the correct word to use. I mean it's a variable that does not contain children, thus is assigned not by reference, but by assignment

- :)

R M said...

Ummkay i'm back, if you don't mind, and i've done some follow-up testing now.

Anything like a string literal or a boolean or a number, which is not declared thru a constructor, will have its own type, and that's what fails. Anything that has typeof "object", seems to do well.

Feel free to not approve either of my posts. Just please put a note for the rent-a-coders out there, that this compact and wonderful framework will only work for objects that contain other members, but are not destroyed/replaced in-between being accessed.

Another point i hope to make, nicely, is Firefox seems to choke on the var name Object. The error was something like "Object is not a Constructor" If i rename it, everything runs smoothly.

I feel that some may mis-understand those two things and think it's a quality problem on your part. I'd hate to have that happen, because you're one of the most inventive minds when it comes to JS, and deserve a good reputation.

Once again, feel free to dump both comments. They are sorta technical grade...

Just make note, ok ;)

- :)

Andrea Giammarchi said...

I am not sure I understand your problems with primitives but I can tell you the Relator has been updated a couple of times here :)

R M said...

Thank you, will check.

Anyway, i usually speed-read, and did not see the use of the word "object".
As soon as i decided to try your code, it came to me that if someone were to use Relator on a
var myStr = "just a random string";
it will only work as long as the string stays the same, and as long as there is only one copy of that string. Creating a string thru the constructor on the other hand will allow two identical strings to have different allocation spaces and info. Assigning a child object to the string object will not change the quality of it in getting information, so that is good.

Sorry for having been unclear. I was just looking for a way to allow something as simple as a "false" to have some way of keeping extra info for debugging purposes.


Thanks again for all your brilliant work. I really enjoy reading your blog. Keeps the mind from sleeping :)

Andrea Giammarchi said...

strings are immutable, what you assign is a reference, you change string, you change reference