Saturday, July 07, 2007

Passing Variables by Reference in JavaScript

Long time ago, when I was learning C, and I understood the use of pointers, I started thinking if there was a way to pass the JavaScript variables by reference.
I had a lot of ideas, but they didn't worked as spected.

For example, for global variables in a browser, I could use:

function modifyVar(varName,newVal){
window[varName]=newVal;
}
var x=123;
alert(x);
modifyVar("x",321);
alert(x);


anyway, this was only valid for "Global" variables..

Then I thought about using caller.call (even do it is not exportable).

function modifyVar(varName,newVal){
modifyVar.caller.call(eval,varName+"="+newVal);
}
var x=123;
alert(x);
modifyVar("x",321);
alert(x);

anyway, this didn't work neither, there was an strange error named "Too much recursion".

Then, a lot of time after that, (actually.. yesterday), I realized that the Objects in javascript are passed as reference, so..

function modifyVar(obj,newVal){
obj.value=newVal;
}
var m={value: 1};
alert(x);
modifyVar("x",321);
alert(x);


and the attribute was modified successfully :).


Any way, this wasn't good enough, I wanted to be able to send a variable as a parameter in an instruction, and be able to modify it's content inside the function.

There's when I realize (after some testing), that I can set any variable as an object, and allow it to have any primitive value I want, for example:

var w=Object("some string");


will behave just like:

var w="some string";


and that:

var w=Object(123);


will behave just like:

var w=123;


and the same for regular expressions, functions, other objects, etc..

So by means of this, I was able to transform any variable into a "referenceable" variable.

Any way, for modifying this variable, I couldn't use any Assignment Operators, because they would destroy the Object.. I needed to modify it's contents from "inside", using it's Methods.

So I found three methods that returned the value of an object:
  • toSource();
  • toString();
  • valueOf();

The last one is the most important one, it's value is the one that will be treated "officially", except for String and Source operations.. so by doing:

function modifyVar(obj,val){
obj.valueOf=obj.toSource=obj.toString=function(){return val}
}


we would actually be modifying the value of "obj".

any way, this wasn't just enough.. I wanted a way to reference variables as easy as in C or PHP.. so.. why not making a prototype function of object that allows me to modify the variable..

so I did this:

Object.prototype.$=function $(val){if(val)this.valueOf=this.toSource=this.toString=function(){return val};return val;};

so, variable.$("new val"); will modify the content of variable, "globally".

Here you can see the way this works:

// Object reference maker
Object.prototype.$=function $(val){if(val)this.valueOf=this.toSource=this.toString=function(){return val};return val;};


function value(variable){
// function to modify the variable through =
variable="new_value";
}

function reference(variable){
//function to modify the variable through reference
variable.$("new_value");
}

var w="standard"; // standard value

w=Object(w);// transform to object.
alert(w); // show that the value is still the same
// standard
value(w); // try to change it's content's via =
alert(w); // show if the content's have been modified
// standard
reference(w); // try to modify the content's via reference
alert(w); // show the new value
// new_value


Hope this is useful for anybody that requires to modify a "private" variable where it's not accessible.. here is an example of Object, and a way to modify its private variables (which shouldn't be possible due to the O.O.P. Paradigm)..

Object.prototype.$=function $(val){if(val)this.valueOf=this.toSource=this.toString=function(){return val};return val;};
function DownTown(){
var private=Object("You cant modify me");
this.get=function(){
return private;
}
this.export=function(callback){
callback(private);
}
}
var blackbox=new DownTown();
alert(blackbox.get());
blackbox.export(function(x){x.$("new val!")});
alert(blackbox.get());