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());

9 comments:

  1. Awesome. I need to pass a variable by ref to a function. Your objects are passed via ref insight helped me solve the prob.

    Thanks,

    pfarrell
    The Practical Programmer

    ReplyDelete
  2. Thanks man that your blog helped me a lot :) Keep it up!

    ReplyDelete
  3. thanks , i hope this will be easy in the future to pass by ref like in C/C++
    val= &oldVal;

    ReplyDelete
  4. This is really, really cool... except, it breaks jQuery. Uurgh!

    ReplyDelete
  5. This is very nice.

    However, if you don't want to change the object prototype, you can simply box the value in a array and reference the variable at index 0 to use or change the value.

    for instance

    var a = [1];
    var b = doSomething(a);

    function doSomething(a){
    var x = a[0] + 1;
    a[0] = 5;
    return x;
    }

    /*
    this will result in:
    a = [5]
    a[0] = 5
    b = 2
    */

    This is no as pretty and "correct" as your approach, but you can effectively use it in combination with javascript frameworks such as jquery and AJAX.NET Client Lib without the fear of any collisions.

    Besides that it's just an easy approach if you only need to use a ref variable once or so.

    Anyways, nice job. You gottah love javascript.. it's such a wonderfully flexible language!

    ReplyDelete
  6. It is MUCH easier to just use window["varName"] as all global variables are part of the global window array.

    Ken
    917 741 3377

    ReplyDelete
  7. Interesting solution. Unfortunately for you, as a "grammar nazi" I must denounce your improper use of apostrophes in both plurals and possessive pronouns, and the improper lack of apostrophes in contractions.

    ReplyDelete
  8. Just change the character in the prototyping. No more collision with JQuery ...

    Per

    ReplyDelete