最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

Is there a better way to simulate pointers in JavaScript? - Stack Overflow

programmeradmin1浏览0评论

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.

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.

Share Improve this question edited May 23, 2017 at 10:29 CommunityBot 11 silver badge asked Apr 23, 2012 at 13:05 Aadit M ShahAadit M Shah 74.2k31 gold badges175 silver badges307 bronze badges 17
  • 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 Commented Apr 23, 2012 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 Commented Apr 23, 2012 at 13:07
  • 1 If you need pointers without eval use arrays. – Dewfy Commented Apr 23, 2012 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 Commented Apr 23, 2012 at 15:19
  • 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 plex 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 Commented Apr 24, 2012 at 7:29
 |  Show 12 more ments

5 Answers 5

Reset to default 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?" };
}

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

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

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

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 transpiled 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 transpiler.

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 bees 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.

发布评论

评论列表(0)

  1. 暂无评论