Selbstreferenzen in Objektliteralen / Initialisierern

Lesezeit: 9 Minuten

Selbstreferenzen in Objektliteralen Initialisierern
kpozin

Gibt es eine Möglichkeit, etwas wie das Folgende in JavaScript zum Laufen zu bringen?

var foo = {
    a: 5,
    b: 6,
    c: this.a + this.b  // Doesn't work
};

In der aktuellen Form wirft dieser Code da offensichtlich einen Referenzfehler this bezieht sich nicht auf foo. Aber ist Gibt es eine Möglichkeit, Werte in den Eigenschaften eines Objektliterals von anderen zuvor deklarierten Eigenschaften abhängig zu machen?

1646887517 456 Selbstreferenzen in Objektliteralen Initialisierern
Christian C. Salvado

Nun, das einzige, was ich Ihnen sagen kann, sind Getter:

var foo = {
  a: 5,
  b: 6,
  get c() {
    return this.a + this.b;
  }
}

console.log(foo.c) // 11

Dies ist eine syntaktische Erweiterung, die durch die ECMAScript 5th Edition-Spezifikation eingeführt wurde, die Syntax wird von den meisten modernen Browsern (einschließlich IE9) unterstützt.

  • Sehr hilfreiche Antwort. Weitere Informationen zu “get” finden Sie hier: developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/…

    – jake

    2. Februar 2013 um 16:21 Uhr

  • Beachten Sie, dass bei dieser Lösung die Werte von foo.a oder foo.b werden dann der Wert von geändert foo.c ändert sich auch synchron. Dies kann erforderlich sein oder auch nicht.

    – HBP

    2. Mai 2015 um 6:21 Uhr

  • @HBP Das wäre genau das Gleiche wie in der Frage, also scheint mir genau das das erwartete Ergebnis zu sein.

    – Randy

    11. März 2016 um 14:28 Uhr

  • Beachten Sie, dass this bindet an das am tiefsten verschachtelte Objekt. Z.B: ... x: { get c () { /*this is x, not foo*/ } } ...

    – Bernardo Dal Corno

    18. April 2018 um 20:22 Uhr

  • Um meine obige Aussage zu vervollständigen, da foo wird als Variable deklariert und c wird nur zum Zeitpunkt des Aufrufs mit ausgewertet foo Innerhalb c wird funktionieren, im Gegensatz zu this (sei aber vorsichtig)

    – Bernardo Dal Corno

    19. April 2018 um 20:11 Uhr

Du könntest so etwas tun:

var foo = {
   a: 5,
   b: 6,
   init: function() {
       this.c = this.a + this.b;
       return this;
   }
}.init();

Dies wäre eine Art einmalige Initialisierung des Objekts.

Beachten Sie, dass Sie tatsächlich den Rückgabewert von zuweisen init() zu fooalso musst du return this.

  • du kannst auch delete this.init Vor return this damit foo ist nicht verschmutzt

    – Billy Mond

    26. Juli 2011 um 1:12 Uhr

  • @BillyMoon: Ja, obwohl ich es tue wirkt sich auf die Leistung aus aller nachfolgenden Eigenschaftszugriffe auf dieses Objekt auf vielen Engines (z. B. V8).

    – TJ Crowder

    19. Mai 2014 um 7:30 Uhr

  • @MuhammadUmer: Ich bin mir nicht sicher, wie ES6-Klassen für die Frage relevant sind.

    – Felix Klinge

    1. Juni 2015 um 3:02 Uhr

  • @MuhammadUmer: Klassen sind nur syntaktischer Zucker für Konstruktorfunktionen, bieten also nicht wirklich etwas Neues. In jedem Fall liegt der Schwerpunkt dieser Frage auf Objektliteralen.

    – Felix Klinge

    1. Juni 2015 um 3:45 Uhr

  • @akantoword: Großartig 🙂 Da Objektliterale ein einzelner Ausdruck sind, ist die init() call wurde direkt an das Literal angehängt, damit es ein einzelner Ausdruck bleibt. Sie können die Funktion aber natürlich auch separat aufrufen.

    – Felix Klinge

    26. Juli 2016 um 19:51 Uhr

Selbstreferenzen in Objektliteralen Initialisierern
TJ Crowder

Die offensichtliche, einfache Antwort fehlt, also der Vollständigkeit halber:

Aber ist Gibt es eine Möglichkeit, Werte in den Eigenschaften eines Objektliterals von anderen zuvor deklarierten Eigenschaften abhängig zu machen?

Nein. Alle Lösungen hier verschieben es, bis das Objekt (auf verschiedene Weise) erstellt wurde, und weisen dann die dritte Eigenschaft zu. Die einfachste Weg ist, einfach dies zu tun:

var foo = {
    a: 5,
    b: 6
};
foo.c = foo.a + foo.b;

Alle anderen sind nur indirektere Wege, dasselbe zu tun. (Die von Felix ist besonders schlau, erfordert aber das Erstellen und Zerstören einer temporären Funktion, wodurch die Komplexität erhöht wird, und hinterlässt entweder eine zusätzliche Eigenschaft auf dem Objekt oder [if you delete that property] wirkt sich auf die Leistung aus nachfolgender Eigenschaftszugriffe auf dieses Objekt.)

Wenn Sie alles in einem Ausdruck haben möchten, können Sie dies ohne die temporäre Eigenschaft tun:

var foo = function(o) {
    o.c = o.a + o.b;
    return o;
}({a: 5, b: 6});

Oder natürlich, wenn Sie dies mehr als einmal tun müssen:

function buildFoo(a, b) {
    var o = {a: a, b: b};
    o.c = o.a + o.b;
    return o;
}

dann, wo Sie es verwenden müssen:

var foo = buildFoo(5, 6);

  • Für meine eigene geistige Gesundheit versuche ich, eine Art offizielle Dokumentation zu finden, die im Grunde dasselbe sagt – dass es sich um ein Objekt handelt this steht nur zur Verfügung Methoden dieses Objekts und keine anderen Arten von Eigenschaften. Irgendeine Idee, wo ich das finden könnte? Danke!

    – David Kennel

    5. Mai 2018 um 1:27 Uhr

  • @DavidKennell: Offizieller geht es nicht die Spezifikation. 🙂 Sie würden wahrscheinlich anfangen Hier und durchziehen. Es ist eine ziemlich umständliche Sprache, aber im Grunde werden Sie in den verschiedenen Unterabschnitten von sehen Bewertung der Eigenschaftsdefinition dass das Objekt nicht für die Operationen verfügbar ist, die die Werte von Eigenschaftsinitialisierern bestimmen.

    – TJ Crowder

    5. Mai 2018 um 9:24 Uhr

  • Ich kann die nicht sehen Browserscope-Ergebnisse hier, aber das ist nicht mehr der Fall richtig? In meiner Umgebung v8: delete ist 10% schneller und gecko : delete ist nur 1% langsamer.

    – Der Meister

    7. Mai 2020 um 15:32 Uhr


  • @TheMaster – Ja, ich glaube nicht, dass BrowserScope wirklich eine Sache mehr ist. Sieht so aus, als wäre das Löschen nicht mehr so ​​​​schlecht wie früher, zumindest nicht in V8 (Chrome usw.) oder SpiderMonkey. Immer noch langsamer, aber nur ein kleines bisschen, und diese Dinger sind heutzutage verdammt schnell.

    – TJ Crowder

    7. Mai 2020 um 16:02 Uhr

Selbstreferenzen in Objektliteralen Initialisierern
zzzzov

Instanziieren Sie einfach eine anonyme Funktion:

var foo = new function () {
    this.a = 5;
    this.b = 6;
    this.c = this.a + this.b;
};

1646887518 682 Selbstreferenzen in Objektliteralen Initialisierern
voscusa

Jetzt können Sie in ES6 faule zwischengespeicherte Eigenschaften erstellen. Bei der ersten Verwendung wird die Eigenschaft einmal ausgewertet, um eine normale statische Eigenschaft zu werden. Ergebnis: Beim zweiten Mal wird der Mathematikfunktions-Overhead übersprungen.

Die Magie liegt im Getter.

const foo = {
    a: 5,
    b: 6,
    get c() {
        delete this.c;
        return this.c = this.a + this.b
    }
};

Im Pfeil-Getter this holt die ab umgebenden lexikalischen Umfang.

foo     // {a: 5, b: 6}
foo.c   // 11
foo     // {a: 5, b: 6 , c: 11}  

  • es5 hat auch Eigenschaften, die Sie nur verwenden müssen Object.defineProperty(foo, 'c', {get:function() {...}}) sie zu definieren. Das geht in einer Fabrik wie dieser ganz einfach und unauffällig. Natürlich, wenn Sie die verwenden können get Zucker ist es besser lesbar, aber die Fähigkeit war da.

    – Aluan Haddad

    18. April 2017 um 1:56 Uhr

  • das funktioniert perfekt, aber darf ich wissen, warum Sie this.c löschen, wenn es nicht einmal existiert? Ich habe es versucht, ohne zu schreiben delete this.c aber es hat nicht funktioniert

    – Ahsan Alii

    30. März 2021 um 12:07 Uhr

  • Ich kratzte mir den Kopf über die delete auch. Ich denke, was es tut, ist das Entfernen der get c -Eigenschaft aus dem Objekt und überschreibt sie mit einer Standardeigenschaft. Ich denke, auf diese Weise wird es nur einmal und dann berechnet foo.c aktualisiert seine Werte nicht, wenn a oder b später ändern, aber das funktioniert/cachet/rechnet auch nur beim Aufruf foo.c.

    – CTS_AE

    27. Januar um 17:34 Uhr

  • Ja, das nennt man Lazy Evaluation. Mehr hier: en.wikipedia.org/wiki/Lazy_evaluation

    – voscusa

    28. Januar um 0:07 Uhr

1646887519 379 Selbstreferenzen in Objektliteralen Initialisierern
Henry Wrightson

Einige Schließungen sollten sich damit befassen;

var foo = function() {
    var a = 5;
    var b = 6;
    var c = a + b;

    return {
        a: a,
        b: b,
        c: c
    }
}();

Alle darin deklarierten Variablen foo sind privat foowie Sie es bei jeder Funktionsdeklaration erwarten würden, und da sie alle im Gültigkeitsbereich liegen, haben sie alle Zugriff aufeinander, ohne dass darauf verwiesen werden muss this, genau wie Sie es von einer Funktion erwarten würden. Der Unterschied besteht darin, dass diese Funktion ein Objekt zurückgibt, das die privaten Variablen verfügbar macht und diesem Objekt zuweist foo. Am Ende geben Sie nur die Schnittstelle zurück, die Sie als Objekt mit dem exponieren möchten return {} Aussage.

Die Funktion wird dann am Ende mit ausgeführt () wodurch das gesamte foo-Objekt ausgewertet wird, alle darin enthaltenen Variablen instanziiert und das Rückgabeobjekt als Eigenschaften von hinzugefügt werden foo().

  • es5 hat auch Eigenschaften, die Sie nur verwenden müssen Object.defineProperty(foo, 'c', {get:function() {...}}) sie zu definieren. Das geht in einer Fabrik wie dieser ganz einfach und unauffällig. Natürlich, wenn Sie die verwenden können get Zucker ist es besser lesbar, aber die Fähigkeit war da.

    – Aluan Haddad

    18. April 2017 um 1:56 Uhr

  • das funktioniert perfekt, aber darf ich wissen, warum Sie this.c löschen, wenn es nicht einmal existiert? Ich habe es versucht, ohne zu schreiben delete this.c aber es hat nicht funktioniert

    – Ahsan Alii

    30. März 2021 um 12:07 Uhr

  • Ich kratzte mir den Kopf über die delete auch. Ich denke, was es tut, ist das Entfernen der get c -Eigenschaft aus dem Objekt und überschreibt sie mit einer Standardeigenschaft. Ich denke, auf diese Weise wird es nur einmal und dann berechnet foo.c aktualisiert seine Werte nicht, wenn a oder b später ändern, aber das funktioniert/cachet/rechnet auch nur beim Aufruf foo.c.

    – CTS_AE

    27. Januar um 17:34 Uhr

  • Ja, das nennt man Lazy Evaluation. Mehr hier: en.wikipedia.org/wiki/Lazy_evaluation

    – voscusa

    28. Januar um 0:07 Uhr

Du könntest es so machen

var a, b
var foo = {
    a: a = 5,
    b: b = 6,
    c: a + b
}

Diese Methode hat sich für mich als nützlich erwiesen, wenn ich auf das Objekt verweisen musste, für das eine Funktion ursprünglich deklariert wurde. Das Folgende ist ein minimales Beispiel dafür, wie ich es verwendet habe:

function createMyObject() {
    var count = 0, self
    return {
        a: self = {
            log: function() {
                console.log(count++)
                return self
            }
        }
    }
}

Indem Sie self als das Objekt definieren, das die Druckfunktion enthält, erlauben Sie der Funktion, auf dieses Objekt zu verweisen. Das bedeutet, dass Sie die Druckfunktion nicht an ein Objekt „binden“ müssen, wenn Sie sie woanders weitergeben müssen.

Wenn Sie stattdessen verwenden würden this wie unten dargestellt

function createMyObject() {
    var count = 0
    return {
        a: {
            log: function() {
                console.log(count++)
                return this
            }
        }
    }
}

Dann protokolliert der folgende Code 0, 1, 2 und gibt dann einen Fehler aus

var o = createMyObject()
var log = o.a.log
o.a.log().log() // this refers to the o.a object so the chaining works
log().log() // this refers to the window object so the chaining fails!

Durch die Verwendung der Methode self stellen Sie sicher, dass print immer dasselbe Objekt zurückgibt, unabhängig vom Kontext, in dem die Funktion ausgeführt wird. Der obige Code läuft einwandfrei und protokolliert 0, 1, 2 und 3, wenn Sie die Self-Version von verwenden createMyObject().

986230cookie-checkSelbstreferenzen in Objektliteralen / Initialisierern

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy