Is there a better way to simulate pointers in JavaScript?
Asked Answered
R

5

3

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.

Roughen answered 23/4, 2012 at 13:5 Comment(17)
Why do you want to do this? Generally speaking, if you try to write JavaScript like it's a different language you're going to end up with some really ugly code.Carlson
Ugh, that's wrong on so many levels... Is there any particular problem you want to solve or is this an academic question?Sholeen
If you need pointers without eval use arrays.Salmons
@JamesMcLaughlin - It doesn't matter if the code is ugly. It's just boilerplate for a language that I'm building on top of JavaScript (like CoffeeScript). The end user doesn't need to interact with the JavaScript boilerplate at all. Hence it's alright if it's not pretty. =)Roughen
@Sholeen - Care to explain how it is wrong on so many levels? No, I'm not trying to solve any problem in particular. I'm just writing some boilerplate for ECMAScript Harmony proposals (and pointers seem like a really good idea for JavaScript 2.0). In general any new Harmony proposal should be able to be implemented in most JavaScript engines as boilerplate. So I'm messing around with a few new ideas including dynamic scopes, pointers, classes, interfaces, polymorphs, etc. You can watch my current project on GitHub. =)Roughen
@Salmons - Care to explain how you'll use arrays to simulate pointers?Roughen
@ElliotBonneville - JavaScript doesn't pass functions and variables as pointers. Primitives are passed by value, while objects and functions are passed by reference. There's a difference. A pointer is a value that points to a memory location (in this case a variable). Passing a pointer is not the same as passing a reference around. Assigning a new value to a reference will not overwrite the old value. Doing so to a dereferenced pointer will. =)Roughen
@AaditMShah see answer of missingno, but instead of dictioanry vars use just array by index [0]Salmons
@Salmons - I don't see how that will help. I already explained that I can't use properties of objects (or for that matter elements of arrays). The reason is that I'm writing boilerplate for a Harmony proposal, but thank you for your help. Appreciated. =)Roughen
Maybe I just don't understand what problem you are trying to solve. I don't see any benefit in pointers within the context of JavaScript. But I do see you using eval and mutating stringified function bodies with regular expressions and that can't be right. For one: There are no "memory locations" in a memory-managed language. Further: Big and complex JS libraries have been written without the need to "simulate" pointers. It just does not make sense to me, and it probably won't until you can point out a reasonable use case.Sholeen
@Sholeen - I agree. It's not my job to question if a certain feature is worth implementing. It is however my job to write the boilerplate for the feature, add a little syntactic sugar, and then release it to the public to get feedbacks. If it receives positive responses then it's is approved by TC39 and it becomes a part of JavaScript v 2.0.0 - that's the only role I play. I'll write a small demo for "simulated pointers" (because JavaScript doesn't have real pointers) which you can compile and execute on the fly. I'll notify you when I'm done with it. Shouldn't be too difficult now. Cheers. =)Roughen
I'm Interested to see the results.Sholeen
@Sholeen - I created a simple transcompiler that allows you to use C style pointers in JavaScript. An advantage is that it doesn't use eval. You can play around with the fiddle. Tell me if you have any issues with it. Like I said, I'm only creating boilerplate for new ECMAScript Harmony features. If you think this is a good feature for JavaScript v2.0 then I'll send a proposal to the TC39 committee. Cheers. Knock yourself out. =)Roughen
@Aadit It's a nice hack, but I still fail to see the use. I've always thought the whole point of having a garbage-collected, memory-managed language would be to not have to do pointer handling anymore. Is there some problem that can only be solved using your proposed approach? Or is it just to make C programmers feel at home?Sholeen
@Sholeen - There are quite a lot of problems which can only be solved by using pointers. One of them is to modify the value of the this pointer. JavaScript doesn't allow values to be directly assigned to the this pointer. Hence we need find a workaround. It's not possible to update all the references to this. However it is possible to use pointers to update a single reference (as demonstrated in my answer below). Usually this is what you want anyway. Beside this case I can't think of any other innovative ways to use pointers so I guess it's indeed to make C programmers feel more at home.Roughen
@Aadit But there is Function.call() and Function.apply() to determine the object this will point to. Also things like Array.map() provide a way to do this. Why would I want to modify this dynamically? If you have a second object reference that you'd want to swap with this you could just as well use it directly. No?Sholeen
@Sholeen - I explained my point of view in my answer below. It was too much matter to be explained in the comments. =)Roughen
C
5

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?" };
}
Crinkleroot answered 28/4, 2012 at 0:36 Comment(3)
This is exactly what I was looking for. Thank you. I guess I stand corrected about using properties to simulate pointers. However as exciting as this is I'm afraid it may not work in older browsers (getters and setters aren't supported in older browsers), so we'll still have to use my method as a fallback. Nevertheless, greatly appreciated. =)Roughen
You can simulate properties with explicit get and put methods.Crinkleroot
Maybe shorter: function createPointer(r, w) { return Object.defineProperty({}, "value", {get: r, set: w}); }Excreta
A
2

Unfortunately, the only ways to reference a variable in Javascript are accessing it directly (something we don't want, since it does static binding) or passing its name in string form to eval.

If you really want to avoid eval, what you can do is try to have your variables inside objects that act as scopes, since this would let you use [] subscript notation to access a variable given its name. Note that if all pointers you create are to global variables then this is already the case, since global variables are also made to be properties of the global window object.


function pointer(scope, varname){
    return function(x){
        if(arguments.length <= 0){ //The explicit arguments.length lets us set the pointed variable to undefined too.
            return scope[varname];
        }else{
            return (scope[varname] = x);
        }
    }
};

var vars = {
    x: 1
};

var y = 2; // "normal" variables will only work if they are global.

swap( pointer(vars, 'x'), pointer(window, 'y') );
Austin answered 23/4, 2012 at 13:47 Comment(1)
+1 for allowing the pointer to point to undefined (although I think that null would be a better placeholder for nothing since undefined is meant specifically for variables and properties which haven't been defined as yet). Nevertheless, like I commented on other answers I need the pointers to point to global or local variables and not properties of an object. So I guess closures are the only way of implementing that. :|Roughen
K
0

something like that?

function swap(a,b,scope) {
    var t = scope[a];
    scope[a] = scope[b];
    scope[b] = t; 
}

x = 2;
y = 3;
alert([x,y]);
swap('x', 'y',this);
alert([x,y]);
Kasiekask answered 23/4, 2012 at 13:31 Comment(1)
No. In this case we either have to use global variables or properties of an object. All variables are properties of an activation object but we don't have access to the activation object so this method won't work.Roughen
I
0

Here is one way of doing it with an object:

var obj = {
    x:2,
    y:3
},
swap = function(p1, p2){
    var t = obj[p1];
    obj[p1] = obj[p2];
    obj[p2] = t;
};

console.log( obj.x, obj.y );
swap('x', 'y');
console.log( obj.x, obj.y );
Inquisitive answered 23/4, 2012 at 13:39 Comment(4)
This answer is similar to that posted by @chumkiu below. However like I said, I need to use variables and not properties of an object; and since we can't access the activation object of functions it'll only work for global variables (which is exactly what we don't want).Roughen
A variable is a property of the window object. And you can use a global object window.yourObject like all the JS lib out there.Inquisitive
That's not true for local variables. Plus it's better if we don't populate the global scope with lots of variables.Roughen
if you wrap my code in a (function(){...})() the variable are not global anymore, and you can use them as closures inside. Many JS libs add a single global variable: $, dojo, jQuery,... with their own methods.Inquisitive
R
0

Edit:

@Tomalak - Consider the following JavaScript program:

var flowers = new Misdirection;
flowers.abracadabra();
alert(flowers);

function Misdirection() {
    this.abracadabra = function () {
        this = new Rabbit;
    };
}

function Rabbit() {
    this.toString = function () {
        return "Eh... What's up, doc?";
    };
}

The above program throws ReferenceError: Cannot assign to 'this'. Pointers can be used to solve this problem; and although it won't update the this pointer, it will do the next best thing - update the only reference to the this pointer.

We could make the above program work without using pointers by replacing this with flowers. However by doing so the misdirection magic trick will only work for one instance of the constructor. Pointers allow us to make it work for any number of instances.

It's not possible to achieve the same results by using Function.call, Function.apply, or Array.map. In addition if the constructor returns an explicit value then it's useless to override the this pointer anyway. The program I wrote below (using pointers) will work even if I returned the abracadabra function from Misdirection and called flowers() instead of flowers.abracadabra().

Original:

Simulating pointers in JavaScript is a really powerful hack. For example it can be used to perform a magic trick as follows:

var flowers = new Misdirection(&flowers);
flowers.abracadabra();
alert(flowers);

function Misdirection(flowers) {
    this.abracadabra = function () {
        *flowers = new Rabbit;
    };
}

function Rabbit() {
    this.toString = function () {
        return "Eh... What's up, doc?";
    };
}

That's all nice and dandy, but the real power of simulating pointers in JavaScript shines through when we push the envelope:

var Square = new Class(function (ctor, uber) {
    *ctor = constructor;

    var side;

    function constructor(length) {
        side = length;
    }

    this.area = function () {
        return side * side;
    };

    return &uber;
});

var Cube = new Class(function (ctor, uber) {
    *ctor = constructor;

    function constructor(side) {
        uber(side);
    }

    this.area = function () {
        return 6 * uber.area();
    };

    return &uber;
}, Square);

var cube = new Cube(5);
alert(cube.area());

function Class(claus, Uber) {
    Claus.__proto__ = Uber === void 0 ? Class.prototype : Uber;

    return Claus;

    function Claus() {
        var self = this;
        var called;

        var uber = Uber === void 0 ? function () {
            throw new Error("No uber class specified.");
        } : function () {
            if (!called) {
                called = "Cannot call uber class constructor more than once.";

                var args = Array.prototype.slice.call(arguments);
                args = Array.prototype.concat.call([null], args);
                var base = new (Function.prototype.bind.apply(Uber, args));

                self.__proto__.__proto__ = base;
                self.__proto__.__proto__.constructor = Claus;

                *uber = base;
            } else throw new Error(called);
        };

        var constructor = new Function;
        uber = claus.call(this, &constructor, uber);
        constructor.apply(this, arguments);
    };
}

Obviously this is too much boilerplate, but it does demonstrate just how powerful closures are. What's truly amazing is that we can use this boilerplate to simulate classical object oriented programming in JavaScript. For example the following code can be transcompiled to the above program (although we'll need to write a full fledged parser to do so):

class Square {
    var side;

    function constructor(length) {
        side = length;
    }

    this.area = function () {
        return side * side;
    };
}

class Cube extends Square {
    function constructor(side) {
        uber(side);
    }

    this.area = function () {
        return 6 * uber.area();
    };
}

var cube = new Cube(5);
alert(cube.area());

Notice that the lines *ctor = constructor; and return &uber; have been removed. This is just redundant code necessary to make the constructor and inheritance work. Also the Class function is not written in the source as it's automatically added by the transcompiler.

The beauty of simulating pointers in JavaScript is demonstrated in the above program where the variable uber in the class Cube is initially the base class constructor function. However when it's called it's replaced by that instance of the base class which becomes the prototype of this.

It also means that an instance of Cube will not be an instance of Square unless and until the uber class constructor is called from Cube.

Roughen answered 26/4, 2012 at 15:10 Comment(3)
It seems that what you are simulating is not pointers but variable references. These can easily be simulated via properties.Crinkleroot
No. What I'm simulating is in fact a pointer. Reference variables once initialised do not need to be dereferenced. They are simply an alias for another variable. In addition they are constant variables. They cannot be modified. You are confused between a reference and a reference variable. References are aliases for values. Reference variables are aliases for variables. What I'm simulating is a pointer to a variable. It's not the same as a property and the same use cases can't be implemented using properties.Roughen
You are not performing any pointer operations beyond dereferencing. (No pointer arithmetic for example.) You can therefore just use a Javascript property to perform the dereferencing.Crinkleroot

© 2022 - 2024 — McMap. All rights reserved.