3

我正在使用动态范围来模拟JavaScript 中的指针,如下所示

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

有没有其他方法可以达到相同的结果(即不诉诸eval)?这似乎是太多的样板。

4

5 回答 5

3

由于您使用指针的唯一目的是取消引用它以访问另一个变量,因此您可以将其封装在一个属性中。

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

要创建指针,请传递读取和写入指向的变量的访问器方法。

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

要取消引用指针,请访问其值。换句话说,在 C 语言中你会写*p在这里的地方你写p.value.

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

您可以创建指向同一个变量的多个指针。

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"

您可以通过简单地用另一个指针覆盖指针变量来更改指针指向的内容。

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

您可以通过指针交换两个变量。

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

让我们使用它们的指针交换 和 的值ij

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

您可以创建指向局部变量的指针。

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

通过闭包的魔力,这为您提供了一种模拟 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

你的魔术也有效:

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?" };
}
于 2012-04-28T00:36:46.527 回答
2

不幸的是,在 Javascript 中引用变量的唯一方法是直接访问它(我们不希望这样做,因为它进行静态绑定)或将其名称以字符串形式传递给 eval。

如果您真的想避免 eval,您可以尝试将变量放在充当作用域的对象中,因为这将允许您使用[]下标符号来访问给定名称的变量。请注意,如果您创建的所有指针都指向全局变量,那么情况已经如此,因为全局变量也被设为全局window对象的属性。


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') );
于 2012-04-23T13:47:10.183 回答
0

编辑:

@Tomalak - 考虑以下 JavaScript 程序:

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

上面的程序抛出ReferenceError: Cannot assign to 'this'. 指针可以用来解决这个问题;虽然它不会更新this指针,但它会做下一件最好的事情——更新对指针的唯一引用this

this我们可以通过替换为.来使上述程序在不使用指针的情况下工作flowers。然而,通过这样做,误导魔术只适用于构造函数的一个实例。指针允许我们使它适用于任意数量的实例。

Function.call使用、Function.apply或无法获得相同的结果Array.map。此外,如果构造函数返回一个显式值,那么this无论如何覆盖指针都是没有用的。我在下面编写的程序(使用指针)即使我abracadabra从返回函数Misdirection并调用flowers()而不是flowers.abracadabra().

原来的:

在 JavaScript 中模拟指针是一个非常强大的技巧。例如,它可以用来执行魔术,如下所示

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

这一切都很好,但当我们挑战极限时,在 JavaScript 中模拟指针的真正力量就会显现出来

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

显然这是太多的样板,但它确实展示了闭包的强大功能。真正令人惊奇的是,我们可以使用这个样板来模拟 JavaScript 中的经典面向对象编程。例如,可以将以下代码转编译为上述程序(尽管我们需要编写一个完整的解析器来执行此操作):

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

请注意,线条*ctor = constructor;return &uber;已被删除。这只是使构造函数和继承工作所必需的冗余代码。此外,该Class函数未编写在源代码中,因为它是由转编译器自动添加的。

在上面的程序中展示了在 JavaScript 中模拟指针的美妙之处,其中uber类中的变量Cube最初是基类的构造函数。但是,当它被调用时,它会被基类的实例所取代,该实例成为this.

这也意味着 的实例Cube不会是 的实例,Square除非从 调用 uber 类构造函数Cube

于 2012-04-26T15:10:25.260 回答
0

类似的东西?

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]);
于 2012-04-23T13:31:59.423 回答
0

这是使用对象执行此操作的一种方法:

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 );
于 2012-04-23T13:39:06.460 回答