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

ポインタを介して2つの変数を交換できます。

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

iとの値jをそれらのポインタを使用して交換してみましょう。

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

そんな感じ?

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

オブジェクトを使用してこれを行う1つの方法は次のとおりです。

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 に答える
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。ただし、そうすることで、ミスディレクションの魔法のトリックはコンストラクターの1つのインスタンスに対してのみ機能します。ポインターを使用すると、任意の数のインスタンスで機能させることができます。

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

また、uberクラスコンストラクターがから呼び出されない限りCube、のインスタンスはのインスタンスにはなりません。SquareCube

于 2012-04-26T15:10:25.260 に答える