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.

Community
  • 1
  • 1
Aadit M Shah
  • 72,912
  • 30
  • 168
  • 299
  • 4
    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. – James M Apr 23 '12 at 13:06
  • 4
    Ugh, that's wrong on so many levels... Is there any particular problem you want to solve or is this an academic question? – Tomalak Apr 23 '12 at 13:07
  • 1
    If you need pointers without eval use arrays. – Dewfy Apr 23 '12 at 13:08
  • @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. =) – Aadit M Shah Apr 23 '12 at 15:19
  • @Tomalak - 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](https://github.com/JediCorp/lambda "Lambda JS") on GitHub. =) – Aadit M Shah Apr 23 '12 at 15:27
  • @Dewfy - Care to explain how you'll use arrays to simulate pointers? – Aadit M Shah Apr 23 '12 at 15:29
  • @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. =) – Aadit M Shah Apr 23 '12 at 15:33
  • @AaditMShah see answer of missingno, but instead of dictioanry vars use just array by index [0] – Dewfy Apr 23 '12 at 15:46
  • @Dewfy - 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. =) – Aadit M Shah Apr 23 '12 at 15:53
  • 1
    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. – Tomalak Apr 24 '12 at 07:29
  • @Tomalak - 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. =) – Aadit M Shah Apr 24 '12 at 11:09
  • I'm Interested to see the results. – Tomalak Apr 24 '12 at 11:12
  • @Tomalak - 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](http://jsfiddle.net/BU2SV/). 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. =) – Aadit M Shah Apr 25 '12 at 07:43
  • @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? – Tomalak Apr 26 '12 at 09:43
  • @Tomalak - 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. – Aadit M Shah Apr 26 '12 at 15:22
  • @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? – Tomalak Apr 27 '12 at 05:34
  • @Tomalak - I explained my point of view in my answer below. It was too much matter to be explained in the comments. =) – Aadit M Shah Apr 27 '12 at 15:27

5 Answers5

4

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?" };
}
Raymond Chen
  • 44,448
  • 11
  • 96
  • 135
  • 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. =) – Aadit M Shah Apr 28 '12 at 16:14
  • You can simulate properties with explicit `get` and `put` methods. – Raymond Chen Apr 28 '12 at 16:15
  • Maybe shorter: `function createPointer(r, w) { return Object.defineProperty({}, "value", {get: r, set: w}); }` – Bergi Mar 26 '13 at 08:57
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') );
hugomg
  • 68,213
  • 24
  • 160
  • 246
  • +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. :| – Aadit M Shah Apr 23 '12 at 15:16
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]);
Luca Rainone
  • 16,138
  • 2
  • 38
  • 52
  • 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. – Aadit M Shah Apr 23 '12 at 15:09
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 );
Mic
  • 24,812
  • 9
  • 57
  • 70
  • 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). – Aadit M Shah Apr 23 '12 at 15:11
  • 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. – Mic Apr 23 '12 at 19:28
  • That's not true for local variables. Plus it's better if we don't populate the global scope with lots of variables. – Aadit M Shah Apr 24 '12 at 01:19
  • 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. – Mic Apr 24 '12 at 08:14
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.

Aadit M Shah
  • 72,912
  • 30
  • 168
  • 299
  • It seems that what you are simulating is not pointers but variable references. These can easily be simulated via properties. – Raymond Chen Apr 27 '12 at 21:00
  • 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. – Aadit M Shah Apr 27 '12 at 22:42
  • 1
    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. – Raymond Chen Apr 27 '12 at 23:58