Search code examples
javascriptpointersclosuressimulatedynamic-scope

Is there a better way to simulate pointers in JavaScript?


I'm using dynamic scoping to simulate pointers in JavaScript as follows:

var ptr = (function () {
    var ptr = "(" + String(function (value) {
    if (value === void 0) return upvalue;
    else upvalue = value;
}) + ")";

    return function (upvalue) {
        return ptr.replace(/upvalue/g, upvalue);
    };
}());

function swap(xptr, yptr) {
    var t = xptr();
    xptr(yptr());
    yptr(t);
}

var x = 2;
var y = 3;

alert([x, y]);
swap(eval(ptr("x")), eval(ptr("y")));
alert([x, y]);

Is there any other way to achieve the same results (i.e. without resorting to eval)? It just seems like too much boilerplate.


Solution

  • Since the only thing you're using the pointer for is to dereference it to access another variable, you can just encapsulate it in a property.

    function createPointer(read, write) {
      return { get value() { return read(); }, set value(v) { return write(v); } };
    }
    

    To create a pointer, pass the accessor methods which read and write the variable being pointed to.

    var i;
    var p = createPointer(function() { return i; }, function(v) { i = v; });
    // p is now a "pointer" to i
    

    To dereference a pointer, access its value. In other words, where in C you would write *p here you write p.value.

    i = "initial";
    alert(p.value); // alerts "initial"
    p.value = "update";
    alert(i); // alerts "update"
    p.value += "2";
    alert(i); // alerts "update2"
    

    You can create multiple pointers to the same variable.

    var q = createPointer(function() { return i; }, function(v) { i = v; });
    // q is also a "pointer" to i
    alert(q.value); // alerts "update2"
    q.value = "written from q";
    alert(p.value); // alerts "written from q"
    

    You can change what a pointer points to by simply overwriting the pointer variable with another pointer.

    var j = "other";
    q = createPointer(function() { return j; }, function(v) { j = v; });
    // q is now a "pointer" to j
    

    You can swap two variables through pointers.

    function swap(x, y) {
        var t = x.value;
        x.value = y.value;
        y.value = t;
    }
    

    Let's swap the values of i and j by using their pointers.

    swap(p, q);
    alert(i); // alerts "other"
    alert(j); // alerts "written from q"
    

    You can create pointers to local variables.

    function example() {
        var myVar = "myVar as local variable from example";
        var r = createPointer(function() { return myVar; }, function(v) { myVar = v; });
        swap(p,r);
        alert(i); // alerts "myVar as local variable from example"
        alert(myVar); // alerts "other"
    }
    example();
    

    Through the magic of closures, this gives you a way to simulate malloc.

    function malloc() {
        var i;
        return createPointer(function() { return i; }, function(v) { i = v; });
    }
    var p = malloc(); // p points to a variable we just allocated from the heap
    p.value = 2; // write a 2 into it
    

    Your magic trick works too:

    var flowers = new Misdirection(
           createPointer(function() { return flowers; }, function(v) { flowers = v; }));
    flowers.abracadabra();
    alert(flowers);
    
    function Misdirection(flowers) {
        this.abracadabra = function() {
            flowers.value = new Rabbit;
        };
    }
    
    function Rabbit() {
        this.toString = function() { return "Eh... what's up doc?" };
    }