Frage Objekt "diesem" zuordnen


Angenommen, ich habe eine Klasse und einige statische Hilfsmethoden wie diese:

function MyClass (myVar) {
    this.myVar = myVar;

    this.replaceMe = function (value) {
        // this will fail
        this = MyClass.staticHelper( value );

        return this;
    }

    this.revealVar = function () {
        alert( this.myVar );
    }
}

MyClass.staticHelper = function (instance, value) {
    return new MyClass( instance.myVar + value );
}

Was ich tun möchte, ist etwa so:

var instance = new MyClass( 2 );

instance.revealVar(); // alerts 2
instance.replaceMe( 40 ).revealVar(); // alerts 42

Der Grund ist, dass meine Klasse eine etwas kompliziertere Struktur hat und ich möchte nicht jedes Mal alle internen Variablen manuell zuweisen, sondern das gesamte Objekt ersetzen. Gibt es einen einfachen Weg dazu?


5
2018-03-24 12:38


Ursprung


Antworten:


instance.replaceMe( 40 ).revealVar(); Warnungen 42

OK, dafür return MyClass.staticHelper(this, value); würde genügen. Die Frage ist nur, ob der nächste Anruf an instance.revealVar() sollte jetzt 2 oder 42 alarmieren - wenn du willst instance in 42 geändert werden, wird es komplizierter:

this = MyClass.staticHelper( value ); // this will fail

…weil this ist keine gewöhnliche Variable, aber a Stichwort und wertet den Wert von ThisBinding des aktuellen Ausführungskontexts aus welche wird abhängig davon eingestellt, wie die Funktion eingegeben wird - Sie können ihm nicht zuweisen, Sie können ihn nur beim Aufruf der Funktion einstellen.

Ich möchte nicht jedes Mal alle internen Variablen manuell zuweisen, sondern das gesamte Objekt ersetzen.

Leider müssen Sie dies tun, ohne die Eigenschaften von instance Objekt (und die Schließung-versteckte Variablen) werden Sie nicht ändern instance und revealVar() wird 2 bleiben.

Gibt es einen einfachen Weg dazu?

Ja, es kann programmgesteuert erfolgen. Die einfachste Methode wäre es Anruf der Konstruktor (wieder) auf die aktuelle Instanz, wie es passiert, wenn mit dem aufgerufen wird new Stichwort:

MyClass.call( instance, instance.myVar + value );

Sie können dies jedoch nicht wie die statische Funktion verwenden, die eine komplett neue Instanz erzeugt. Entweder legst du es in eine statische Methode und rufst das von an replaceMe mit thisoder du legst es einfach direkt hinein replaceMe.

Wenn Sie eine statische Methode benötigen, die zunächst eine komplett neue Instanz zurückgibt, können Sie diese auch verwenden, indem Sie die neuen Eigenschaften der alten Instanz kopieren:

….replaceMe = function(val) {
    var newInst = MyClass.staticHelper(this, val); // new MyClass(this.myVar+val);
    for (var prop in newInst)
        if (newInst.hasOwnProperty(prop))
            this[prop] = newInst[prop];
    return this;
};

Das bedeutet, dass die alten Attribute überschrieben werden müssen, und auch die alten Closures können jetzt als Garbage-Collected erfasst werden, da sich nichts mehr auf sie bezieht.

Übrigens, ich würde es empfehlen Setzen Sie Ihre Methoden auf den Prototyp, anstatt sie im Konstruktor zuzuweisen.


1
2018-03-24 14:24



Wie wäre es mit der Rückgabe der neuen Instanz?

function MyClass(myVar) {
    // ...

    this.replaceMe = function (value) {
        return MyClass.staticHelper(this, value);
    }

    // ...
}

MyClass.staticHelper = function (instance, value) {
    return new MyClass( instance.myVar += value );
}

0
2018-03-24 12:43



Es gibt zwei Gründe, warum dies in Javascript nicht funktioniert.

Erstens, obwohl es wie eine Variable aussieht, this ist eigentlich ein Funktionsaufruf * und kann daher nicht zugeordnet werden. this=foo ist das gleiche wie bar()=baz. Es ist also nicht möglich, einen Code wie diesen zu haben:

a = 5
a.change(10)
alert(a == 10) // nope

Zweitens, auch wenn this=z Wenn das möglich wäre, würde dieser Ansatz sowieso fehlschlagen, weil Javascript nach Wert vergeht, daher ist es nicht möglich, eine Funktion zu haben, die den Wert ihres Arguments ändert:

a = 5
change(a)
alert(a == 10) // nope

* "ist" bedeutet "in jeder Hinsicht vollkommen identisch"


0
2018-03-24 12:50



Ich wollte vor einiger Zeit etwas sehr Ähnliches machen. Leider gibt es keine Möglichkeit, einen Wert zuzuweisen this - das this Zeiger ist eine schreibgeschützte Variable. Die nächste beste Sache ist jedoch, ein Getter- und Setter-Objekt zu verwenden, um die Variable zu ändern, die Ihre Instanz selbst enthält.

Beachten Sie, dass dadurch nur ein einzelner Verweis auf die Instanz aktualisiert wird. Sie können mehr darüber hier lesen: Gibt es eine bessere Möglichkeit, Zeiger in JavaScript zu simulieren?

So funktioniert es:

function MyClass(pointer, myVar) {
    this.myVar = myVar;

    this.replaceMe = function (value) {
        pointer.value = MyClass.staticHelper(this, pointer, value);
        return pointer.value;
    };

    this.revealVar = function () {
        alert(this.myVar);
    };
}

MyClass.staticHelper = function (instance, pointer, value) {
    return new MyClass(pointer, instance.myVar + value);
};

So erstellen Sie das pointer und benutze es:

var instance = new MyClass({
    get value() { return instance; },
    set value(newValue) { instance = newValue; }
}, 2);

instance.revealVar();               // alerts 2
instance.replaceMe(40).revealVar(); // alerts 42

Es ist nicht die eleganteste Lösung, aber es erledigt die Arbeit. Sie können diesen Code in Aktion sehen: http://jsfiddle.net/fpxXL/1/


0
2018-03-24 14:37