Grundlegendes zur ECMAScript-Spezifikation, Teil 1



Guten Tag, Freunde!

In diesem Artikel nehmen wir eine Funktion aus der Spezifikation und analysieren ihre Erklärung. Gehen.

Vorwort


Selbst wenn Sie JavaScript gut kennen, kann das Lesen der Spezifikation schwierig sein. Der folgende Code demonstriert die Verwendung von Object.prototype.hasOwnProperty:

const o = {
    foo: 1
}
o.hasOwnProperty('foo') // true
o.hasOwnProperty('bar') // false

In diesem Beispiel verfügt das Objekt "o" nicht über die Methode "hasOwnProperty". Daher verweisen wir auf den Prototyp "Object.prototype" (Prototypenkette).

Um zu beschreiben, wie Object.hasOwnProperty funktioniert, verwendet die Spezifikation den folgenden Pseudocode:

Object.prototype.hasOwnProperty (V)

Wenn hasOwnProperty mit Argument V aufgerufen wird, werden die folgenden Schritte ausgeführt: ... und HasOwnProperty (O, P) Mit der abstrakten Operation HasOwnProperty wird bestimmt, ob das Objekt eine eigene Eigenschaft mit einem bestimmten Schlüssel hat. Boolescher Wert wird zurückgegeben. Die Operation wird mit den Argumenten O und P aufgerufen. Diese Operation besteht aus den folgenden Schritten: Was ist eine "abstrakte Operation"? Was [[ ]]? Warum hat eine Funktion ein Fragezeichen? Was bedeutet "Bestätigung"? Lass es uns herausfinden.

  1. P ? ToPropertyKey(V)
  2. O ? ToObject( this)
  3. ? HasOwnProperty(O, P).







  1. (assert): Type(O) Object.
  2. : IsPropertyKey(P) true.
  3. desc ? O.[[GetOwnProperty]](P).
  4. desc undefined, false.
  5. true.





Typen in der Sprache und Typen in der Spezifikation


Beginnen wir mit etwas Bekanntem. In der Spezifikation gibt es Werte wie undefiniert, wahr und falsch, die uns JS bekannt sind. Alle von ihnen sind "Sprachwerte" , "Werte von Sprachtypen" , die auch durch die Spezifikation definiert sind.

Die Spezifikation verwendet integrierte Sprachwerte. Beispielsweise kann ein interner Datentyp ein Feld mit dem Wert true oder false enthalten. JS-Engines verwenden in ihren internen Mechanismen im Allgemeinen keine sprachlichen Bedeutungen. Wenn die JS-Engine beispielsweise in C ++ geschrieben ist, werden höchstwahrscheinlich True und False aus C ++ anstelle der internen Darstellung von Booleschen Werten aus JS verwendet.

Zusätzlich zu den Sprachtypen werden in der Spezifikation spezielle Typen (Spezifikationstypen) verwendet - Typen, die nur in der Spezifikation verwendet werden. JS-Engines müssen sie nicht ausführen (können es aber). In diesem Artikel lernen wir den Spezialtyp „Datensatz“ (Datensatz) und dessen Untertyp „Abschlussdatensatz“ (Abschlussdatensatz) kennen.

Abstrakte Operationen


Abstrakte Operationen sind Funktionen, die in der Spezifikation definiert sind. Sie werden definiert, um die Spezifikation zu reduzieren. JS-Engines müssen diese nicht als eigenständige Funktionen ausführen. In JS können sie nicht direkt aufgerufen werden.

Interne Slots und interne Methoden


Interne Slots und interne Methoden werden durch Namen in [[]] angegeben.

Interne Slots sind Datenelemente (Mengen) eines JS-Objekts oder eines speziellen Typs. Sie werden verwendet, um Informationen über den Status des Objekts zu speichern. Interne Methoden sind Elementfunktionen eines JS-Objekts.

Beispielsweise verfügt jedes JS-Objekt über einen internen [[Prototype]] - Steckplatz und eine interne [[GetOwnProperty]] -Methode.

Interne Slots und Methoden sind in JS nicht verfügbar. Zum Beispiel können wir nicht auf o. [[Prototype]] zugreifen oder o. [[GetOwnProperty]] () aufrufen. Die JS-Engine kann sie für ihre eigenen (internen) Anforderungen ausführen, muss dies jedoch nicht.

Manchmal werden interne Methoden zu abstrakten Operationen mit demselben Namen, wie dies bei [[GetOwnProperty]] der Fall ist:

[[GetOwnProperty]] (P)

Wenn die interne Methode [[GetOwnProperty]] des Objekts "O" mit dem Schlüssel "P" aufgerufen wird, werden die folgenden Aktionen ausgeführt: OrdinaryGetOwnProperty ist keine interne Methode, da sie keinem Objekt zugeordnet ist. Das Objekt, mit dem es arbeitet, wird als Parameter an es übergeben. OrdinaryGetOwnProperty wird als "normal" bezeichnet, da es mit normalen Objekten arbeitet. Objekte in ECMAScript sind gewöhnlich und ungewöhnlich (exotisch). Ein Objekt ist normal, wenn es sich als Reaktion auf eine Reihe von Methoden, die als wesentliche interne Methoden bezeichnet werden, vorhersehbar verhält. Andernfalls (wenn sich das Objekt unvorhersehbar verhält; nicht wie erwartet; wenn das Verhalten des Objekts vom Normalen abweicht, abweicht) wird es als ungewöhnlich angesehen.

  1. ! OrdinaryGetOwnProperty(O, P)





Das bekannteste ungewöhnliche Objekt ist Array, da sich die Eigenschaft "length" nicht als Standard verhält: Durch Festlegen dieser Eigenschaft können Elemente aus dem Array entfernt werden.

Eine Liste der grundlegenden internen Methoden finden Sie hier .

Abschlussprotokoll


Was ist mit Frage- und Ausrufezeichen? Um dies zu verstehen, müssen Sie verstehen, was ein Abschlussdatensatz ist .

Ein Abschlussdatensatz ist ein spezieller Typ (ausschließlich zu Spezifikationszwecken definiert). Die JS-Engine muss nicht denselben internen Datentyp haben.

Ein Abschlussdatensatz ist ein Datentyp mit einem festen Satz benannter Felder. Der Abschlussdatensatz enthält drei Felder:
[[Art]]normal, break, continue, return throw. , normal, « () » (abrupt comlpetions)
[[Value]], , , , ,
[[Target]]( )

Jede abstrakte Operation gibt implizit einen Abschlussdatensatz zurück. Auch wenn das Ergebnis der abstrakten Operation ein einfacher logischer Wert ist, wird es in einen Abschlussdatensatz vom Typ normal eingeschlossen (siehe Implizite Abschlusswerte ).

Anmerkung 1: Die Spezifikation ist in diesem Teil nicht sehr konsistent. Es gibt mehrere Hilfsfunktionen, die bloße Werte zurückgeben, die unverändert verwendet werden, ohne aus dem Abschlussdatensatz abgerufen zu werden.

Anmerkung 2: Spezifikationsautoren versuchen, die Verarbeitung von Abschlussdatensätzen expliziter zu gestalten.

Wenn der Algorithmus eine Ausnahme auslöst, bedeutet dies, dass ein Abschlussdatensatz mit dem Typ ([[Typ]]) throw und dem Wert ([[Wert]]) als Ausnahmeobjekt empfangen wird. Wir werden vorerst keine anderen Typen (brechen, fortfahren und zurückkehren) berücksichtigen.

ReturnIfAbrupt (Argument) bedeutet, dass die folgenden Operationen ausgeführt werden: Dies ist der Abschlussdatensatz. Wenn es abrupt endet, kehren Sie sofort zurück. Andernfalls extrahieren wir den Wert aus dem Abschlussdatensatz. ReturnIfAbrupt sieht aus wie ein Funktionsaufruf, ist es aber nicht. Wir rufen eine Funktion auf, die ReturnIfAbrupt () und nicht ReturnIfAbrupt selbst zurückgibt. Sein Verhalten ähnelt eher einem Makro in C-ähnlichen Programmiersprachen. ReturnIfAbrupt kann wie folgt verwendet werden: Hier kommt ins Spiel

  1. argument - (abrupt), argument.
  2. argument argument.[[Value]].







  1. obj Foo() (obj - ).
  2. ReturnIfAbrupt(obj).
  3. Bar(obj) ( , , obj - , ).

Fragezeichen : Rekord  ? Foo () entspricht ReturnIfAbrupt (Foo ()). Die Verwendung dieser Abkürzung hat einen praktischen Wert: Wir müssen nicht jedes Mal einen Fehlerbehandlungscode schreiben.

In Analogie sei val der Eintrag ! Foo () entspricht dem Folgenden: Mit diesem Wissen können wir Object.prototype.hasOwnProperty wie folgt umschreiben: Object.prototype.hasOwnProperty (P) ... HasOwnProperty kann folgendermaßen umgeschrieben werden: HasOwnProperty (O, P) Wir können auch die interne Methode umschreiben [ [GetOwnProperty]] ohne Ausrufezeichen: O. [[GetOWnProperty]] Wir gehen davon aus, dass temp eine neue temporäre Variable ist, die mit nichts interagiert.

  1. val Foo().
  2. : val .
  3. val val.[[Value]].





  1. P ToProperty(V).
  2. P , P.
  3. P P.[[Value]].
  4. O ToObject( this).
  5. O , O.
  6. O O.[[Value]].
  7. temp HasOwnProperty(O, P).
  8. temp , temp.
  9. temp temp.[[Value]].
  10. NormalCompletion(temp).





  1. : Type(O) Object.
  2. : IsPropertyKey(P) true.
  3. desc O.[[GetOWnProperty]](P).
  4. desc , desc.
  5. desc desc.[[Value]].
  6. desc undefined, NormalCompletion(false).
  7. NormalCompletion(true).





  1. temp OrdinaryGetOwnProperty(O, P).
  2. : temp .
  3. temp temp.[[Value]].
  4. NormalCompletion(temp).



Wir wissen auch, dass in dem Fall, in dem die return-Anweisung etwas anderes als einen Abschlussdatensatz zurückgibt, dies implizit in NormalCompletion eingeschlossen wird.

Fallback: Rückkehr? Foo ()


Verwendet die Spezifikation Return? Foo () - warum gibt es hier ein Fragezeichen?

Nehmen Sie Rückkehr? Foo () kann wie folgt erweitert werden: Rückgabeverhalten? Foo () ist für normale und plötzliche Beendigung gleich. Rekordrückgabe? Mit Foo () können Sie deutlicher angeben, dass Foo einen Abschlussdatensatz zurückgibt.

  1. temp Foo().
  2. temp , temp.
  3. temp temp.[[Value]].
  4. NormalCompletion.





Aussagen


Aussagen in der Spezifikation "bestätigen" die invarianten Bedingungen der Algorithmen. Sie werden der Übersichtlichkeit halber zur Spezifikation hinzugefügt, enthalten jedoch keine Implementierungsanforderungen. Daher müssen sie nicht durch eine bestimmte Implementierung überprüft werden.

Was weiter?


Wir haben gelernt, die Spezifikation für einfache Methoden wie Object.prototype.hasOwnProperty und abstrakte Operationen wie HasOwnProperty zu lesen. Mit diesem Wissen können wir verstehen, was andere abstrakte Operationen tun, was im nächsten Teil besprochen wird. Im nächsten Artikel werden wir uns auch mit Eigenschaftsbeschreibungen befassen, die ein weiterer spezieller Typ sind.


Vielen Dank für Ihre Aufmerksamkeit. Viel Spaß beim Codieren!

All Articles