Was macht das Reflect-Objekt in JavaScript?

86

Ich habe vor einiger Zeit einen leeren Stub auf MDN für das ReflectObjekt in Javascript gesehen, aber ich kann für mein ganzes Leben nichts bei Google finden. Heute habe ich dieses http://people.mozilla.org/~jorendorff/es6-draft.html#sec-reflect-object gefunden und es klingt ähnlich wie das Proxy-Objekt, abgesehen von der Realm- und Loader-Funktionalität.

Grundsätzlich weiß ich nicht, ob diese Seite, die ich gefunden habe, nur die Implementierung von Reflect erklärt oder ob ich den Wortlaut einfach nicht verstehen kann. Könnte mir bitte jemand allgemein erklären, wie die Methoden Reflectaussehen?

Auf der Seite, die ich gefunden habe, heißt es beispielsweise, dass der Aufruf Reflect.apply ( target, thisArgument, argumentsList ) "das Ergebnis des Aufrufs der internen Zielmethode [[Call]] mit den Argumenten thisArgument und args zurückgibt". aber wie ist das anders als nur anzurufen target.apply(thisArgument, argumentsList)?

Aktualisieren:

Dank @Blue fand ich diese Seite im Wiki http://wiki.ecmascript.org/doku.php?id=harmony:reflect_api&s=reflect, das nach meinem besten Wissen besagt, dass das Reflect-Objekt Methodenversionen von allen bereitstellt die Aktionen, die von Proxys abgefangen werden können, um die Weiterleitung zu vereinfachen. Aber das kommt mir etwas komisch vor, da ich nicht sehe, wie notwendig es ist. Aber es scheint ein wenig mehr zu tun, insbesondere das Par, das sagt, double-liftingaber das auf die alte Proxy-Spezifikation hinweist /

Jim Jones
quelle
1
Die Spezifikation sagt "Das Reflect-Objekt ist ein einzelnes gewöhnliches Objekt.", Nach meinem Verständnis Reflectist es nur ein Container für Realmund LoaderObjekte, aber ich weiß auch nicht, was letztere tun.
Simonzack
Danke :), auf der Seite, auf die ich verlinkt habe (ich weiß nicht, wie legitim es ist), scheint es, dass jedes Realm seinen eigenen "Kontext des Java-Skripts" hat und ein Loader Realms wie Module oder ähnliches lädt, basierend auf den Ähnlichkeiten zwischen Reflect und Proxy und die Tatsache, dass Proxy Art von "Überladungen" eingebaute Funktionalität könnte Reflect.Loaderund Reflect.Realmetwas mit Überlastung Modulfunktionalität zu tun haben?
Jim Jones
1
Sieht aus wie es ist eine ‚statische Klasse‘ (wie JSON) mit statischen Methoden: isExtensible, ownKeysusw. ES 6, mit dem tatsächlichen Klassen, dies nützlich mehr über eine Klasse , um herauszufinden , ( targetin 16.1.2 glaube ich).
Rudie
Haben Sie github.com/tvcutsem/harmony-reflect/wiki gesehen ?
Kangax

Antworten:

125

UPDATE 2015: Wie aus der Antwort von 7th hervorgeht , ist nach Abschluss von ES6 (ECMAScript 2015) eine geeignetere Dokumentation verfügbar:


Ursprüngliche Antwort (für (historisches) Verständnis und zusätzliche Beispiele) :

Der Reflection proposalscheint zum Entwurf der ECMAScript 6-Spezifikation übergegangen zu sein . Dieses Dokument beschreibt derzeit die ReflectMethoden des Objekts und gibt nur Folgendes über das ReflectObjekt selbst an:

Das Reflect-Objekt ist ein einzelnes gewöhnliches Objekt.

Der Wert des internen Steckplatzes des Reflect-Objekts ist das standardmäßige integrierte Objektprototypobjekt (19.1.3).

Das Reflect-Objekt ist kein Funktionsobjekt. Es gibt keine interne Methode; Es ist nicht möglich, das Reflect-Objekt als Konstruktor mit dem neuen Operator zu verwenden. Das Reflect-Objekt verfügt auch nicht über eine interne Methode [[Call]]. Es ist nicht möglich, das Reflect-Objekt als Funktion aufzurufen.

Es gibt jedoch eine kurze Erklärung zu seinem Zweck in ES Harmony :

Das Modul "@reflect" dient mehreren Zwecken:
  • Jetzt, da wir Module haben, ist ein "@reflect" -Modul ein natürlicherer Ort für viele der zuvor für Object definierten Reflexionsmethoden. Aus Gründen der Abwärtskompatibilität ist es unwahrscheinlich, dass die statischen Methoden für Object verschwinden. Es sollten jedoch wahrscheinlich neue Methoden zum Modul "@reflect" und nicht zum Objektkonstruktor hinzugefügt werden.
  • Ein natürliches Zuhause für Proxys, ohne dass eine globale Proxy-Bindung erforderlich ist.
  • Die meisten Methoden in diesem Modul ordnen Eins-zu-Eins Proxy-Traps zu. Proxy-Handler benötigen diese Methoden, um Vorgänge wie unten gezeigt bequem weiterzuleiten.



Daher bietet das ReflectObjekt eine Reihe von Dienstprogrammfunktionen, von denen sich viele mit den im globalen Objekt definierten ES5-Methoden zu überschneiden scheinen.

Dies erklärt jedoch nicht wirklich, welche vorhandenen Probleme dadurch gelöst werden sollen oder welche Funktionen hinzugefügt werden. Ich vermutete, dass dies abgeglichen werden könnte, und tatsächlich verweist die obige Harmonie-Spezifikation auf eine „nicht normative, ungefähre Implementierung dieser Methoden“ .

Das Untersuchen dieses Codes könnte (weitere) Ideen über seine Verwendung geben, aber zum Glück gibt es auch ein Wiki, das eine Reihe von Gründen aufzeigt, warum das Reflect-Objekt nützlich ist :
(Ich habe den folgenden Text kopiert (und formatiert), um später darauf zurückgreifen zu können Quelle, da sie die einzigen Beispiele sind, die ich finden konnte. Außerdem sind sie sinnvoll, haben bereits eine gute Erklärung und berühren das applyBeispiel der Frage .)


Weitere nützliche Rückgabewerte

Viele Operationen in Reflectähneln den auf definierten ES5-Operationen Objectwie Reflect.getOwnPropertyDescriptorund Reflect.defineProperty. Während Object.defineProperty(obj, name, desc)jedoch entweder zurückgegeben wird, objwenn die Eigenschaft erfolgreich definiert wurde, oder ein TypeErroranderes Reflect.defineProperty(obj, name, desc)ausgelöst wird, wird nur ein Boolescher Wert zurückgegeben, der angibt, ob die Eigenschaft erfolgreich definiert wurde oder nicht. Auf diese Weise können Sie diesen Code umgestalten:

try {
  Object.defineProperty(obj, name, desc);
  // property defined successfully
} catch (e) {
  // possible failure (and might accidentally catch the wrong exception)
}

Dazu:

if (Reflect.defineProperty(obj, name, desc)) {
  // success
} else {
  // failure
}

Andere Methoden, die einen solchen booleschen Erfolgsstatus zurückgeben, sind Reflect.set(um eine Eigenschaft zu aktualisieren), Reflect.deleteProperty(um eine Eigenschaft zu löschen), Reflect.preventExtensions(um ein Objekt nicht erweiterbar zu machen) und Reflect.setPrototypeOf(um die Prototypverknüpfung eines Objekts zu aktualisieren).


Erstklassiger Betrieb

In ES5 können Sie feststellen, ob ein Objekt objeinen bestimmten Eigenschaftsnamen definiert oder erbt (name in obj). Ebenso verwendet man zum Löschen einer Eigenschaft delete obj[name]. Die dedizierte Syntax ist zwar nett und kurz, bedeutet aber auch, dass Sie diese Operationen explizit in Funktionen einschließen müssen, wenn Sie die Operation als erstklassigen Wert weitergeben möchten.

Mit Reflectkönnen diese Operationen leicht als erstklassige Funktionen definiert werden:
Reflect.has(obj, name)ist das funktionale Äquivalent von (name in obj)und Reflect.deleteProperty(obj, name)ist eine Funktion, die dasselbe tut wiedelete obj[name].


Zuverlässigere Funktionsanwendung

Wenn man in ES5 eine Funktion fmit einer variablen Anzahl von Argumenten aufrufen möchte, die als Array gepackt sind argsund an die der thisWert gebunden ist obj, kann man schreiben:

f.apply(obj, args)

Könnte fjedoch ein Objekt sein, das absichtlich oder unbeabsichtigt seine eigene applyMethode definiert . Wenn Sie wirklich sicherstellen möchten, dass die integrierte applyFunktion aufgerufen wird, schreibt man normalerweise:

Function.prototype.apply.call(f, obj, args)

Dies ist nicht nur ausführlich, es wird auch schnell schwer zu verstehen. Mit Reflectkönnen Sie jetzt einen zuverlässigen Funktionsaufruf kürzer und verständlicher durchführen:

Reflect.apply(f, obj, args)


Konstruktoren mit variablen Argumenten

Stellen Sie sich vor, Sie möchten eine Konstruktorfunktion mit einer variablen Anzahl von Argumenten aufrufen. In ES6 ist es dank der neuen Spread-Syntax möglich, Code wie folgt zu schreiben:

var obj = new F(...args)

In ES5 ist dies schwieriger zu schreiben, da nur eine Funktion mit einer variablen Anzahl von Argumenten verwendet F.applyoder F.callaufgerufen werden kann, die Funktion mit einer variablen Anzahl von Argumenten jedoch keine F.constructFunktion hat new. Mit Reflectkann man jetzt in ES5 schreiben:

var obj = Reflect.construct(F, args)


Standardweiterleitungsverhalten für Proxy-Traps

Wenn Sie ProxyObjekte verwenden, um vorhandene Objekte zu verpacken, ist es sehr üblich, eine Operation abzufangen, etwas zu tun und dann "die Standardeinstellung" auszuführen, bei der die abgefangene Operation normalerweise auf das umschlossene Objekt angewendet wird. Angenommen, ich möchte einfach alle Eigenschaftszugriffe auf ein Objekt protokollieren obj:

var loggedObj = new Proxy(obj, {
  get: function(target, name) {
    console.log("get", target, name);
    // now do the default thing
  }
});

Die Reflectund ProxyAPIs wurden zusammen entworfen , sodass für jede ProxyFalle eine entsprechende Methode vorhanden ist Reflect, die "die Standardeinstellung ausführt". Wenn Sie also feststellen, dass Sie die Standardeinstellung in einem Proxy-Handler ausführen möchten, müssen Sie immer die entsprechende Methode im ReflectObjekt aufrufen :

var loggedObj = new Proxy(obj, {
  get: function(target, name) {
    console.log("get", target, name);
    return Reflect.get(target, name);
  }
});

Der Rückgabetyp der ReflectMethoden ist garantiert mit dem Rückgabetyp der ProxyTraps kompatibel .


Kontrollieren Sie diese Bindung von Accessoren

In ES5 ist es ziemlich einfach, einen allgemeinen Eigenschaftszugriff oder eine Eigenschaftsaktualisierung durchzuführen. Zum Beispiel:

var name = ... // get property name as a string
obj[name] // generic property lookup
obj[name] = value // generic property update

Mit den Methoden Reflect.getund Reflect.setkönnen Sie dasselbe tun, aber zusätzlich als letztes optionales Argument einen receiverParameter akzeptieren , mit dem Sie die thisBindung explizit festlegen können, wenn die Eigenschaft, die Sie erhalten / festlegen, ein Accessor ist:

var name = ... // get property name as a string
Reflect.get(obj, name, wrapper) // if obj[name] is an accessor, it gets run with `this === wrapper`
Reflect.set(obj, name, value, wrapper)

Dies ist gelegentlich nützlich, wenn Sie einpacken objund möchten, dass alle Selbstsendungen innerhalb des Accessors an Ihren Wrapper weitergeleitet werden, z. B. wenn Folgendes objdefiniert ist:

var obj = {
  get foo() { return this.bar(); },
  bar: function() { ... }
}

Durch einen Anruf Reflect.get(obj, "foo", wrapper)wird der this.bar()Anruf umgeleitet wrapper.


Vermeiden Sie Vermächtnis __proto__

Wird in einigen Browsern __proto__als spezielle Eigenschaft definiert, die den Zugriff auf den Prototyp eines Objekts ermöglicht. ES5 standardisierte eine neue Methode Object.getPrototypeOf(obj)zur Abfrage des Prototyps. Reflect.getPrototypeOf(obj)macht genau das gleiche, außer dass Reflectauch ein entsprechender definiert wird Reflect.setPrototypeOf(obj, newProto), um den Prototyp des Objekts festzulegen. Dies ist die neue ES6-kompatible Methode zum Aktualisieren des Prototyps eines Objekts.
Beachten Sie, dass: setPrototypeOf auch vorhanden aufObject (wie korrekt darauf hingewiesen Knu ‚s Kommentar )!


BEARBEITEN:
Randnotiz (Adressieren von Kommentaren zum Q): Es gibt eine kurze und einfache Antwort zu 'Q: ES6-Module vs. HTML-Importe' , die erklärt Realmsund LoaderObjekte.

Eine weitere Erklärung bietet dieser Link :

Ein Realm-Objekt abstrahiert den Begriff einer bestimmten globalen Umgebung mit einem eigenen globalen Objekt, einer Kopie der Standardbibliothek und "intrinsics" (Standardobjekte, die nicht an globale Variablen gebunden sind, wie der Anfangswert von Object.prototype).

Extensible Web : Dies ist das dynamische Äquivalent eines Ursprungs <iframe>ohne DOM.

Erwähnenswert: All dies ist noch im Entwurf, dies ist keine in Stein gemeißelte Spezifikation! Es ist ES6, denken Sie also an die Browserkompatibilität!

Hoffe das hilft!

GitaarLAB
quelle
@Spencer Killen: Nun ... hat diese Antwort Ihre Gedanken in die richtige Richtung gelenkt und erklärt, wie dies mit dem Unterschied zwischen Reflect.applyund zusammenhängt target.apply? Oder was soll ich hinzufügen, bevor das Kopfgeld endet?
GitaarLAB
2
setPrototypeOf existiert auch auf Object.
Knu
1
Beachten Sie, dass die Verwendung Reflect.getals Standardimplementierung für Proxy-Get nicht gut funktioniert, wenn Sie ein Objekt mit Prototyp-Eigenschaften als Proxy verwenden. Es beschwert sich nur, dass es nicht funktioniert. Wenn Sie jedoch stattdessen verwenden, Reflect.get(target, property)ohne das zu übergeben receiver, funktioniert es.
CMCDragonkai
Meine Tests, bei denen Sie immer über den Proxy auf Eigenschaften zugreifen, führen zu einer Situation, in der targetdas ursprüngliche Ziel ist, das der Proxy umschließt, während receiverder Proxy selbst ist. Dies kann jedoch auch anders sein, wenn Sie anders auf die Eigenschaften zugreifen können.
CMCDragonkai
5

Nach dem Entwurf des Dokuments im Wiki,

http://wiki.ecmascript.org/doku.php?id=harmony:specification_drafts

Wir erhalten die Zeile über "einzelnes gewöhnliches Objekt", die im Entwurf klargestellt wird. Es hat auch die Funktionsdefinitionen.

Das Wiki sollte zuverlässig sein, da Sie auf der Emcascript-Website einen Link dazu finden

http://www.ecmascript.org/dev.php

Ich habe den ersten Link von Google gefunden und hatte kein Glück, ihn durch direktes Durchsuchen des Wikis zu finden.

Blau
quelle
Vielen Dank, die Schritte, die unternommen werden, wenn jede Methode aufgerufen wird, die in der offiziellen Spezifikation aufgeführt ist, scheinen ziemlich gleich zu sein wie die in meinem Link, aber ich kann immer noch nicht herausfinden, was jede Methode tut, wenn sie aufgerufen wird, zum Beispiel unter Reflect. Wenden Sie die aufgeführten Schritte an. 4. Führen Sie die abstrakte Operation PrepareForTailCall aus. 5. Geben Sie das Ergebnis des Aufrufs der internen Zielmethode [[Call]] mit den Argumenten thisArgument und args zurück ------- was bedeutet das?
Jim Jones
Ich gehe davon aus, dass es sich in Schritt 4 auf die Schwanzrekursion bezieht und Schritt 5 auf die Prototypfunktion verweist. Es sieht so aus, als ob die allgemeine Idee darin besteht, zu überprüfen, ob die Apply-Methode auf dem angewendet werden kann, worauf sie angewendet wurde (Schritte 1-2), Fehlerbehandlung (Schritt 3), und dann die Funktion aufzurufen, für die Apply ausgeführt wird (Schritte 4-5). Meine beste Vermutung beim Scannen der Dokumentation ist, dass der Punkt des Reflect-Moduls darin besteht, dass Sie Funktionen ausführen, die eine Art Selbstbeobachtung des Objekts erfordern. Die Verwendung von call ist wahrscheinlich auch der Grund, warum "keine interne Methode [[Call]] vorhanden ist".
Blau