Comprendre la spécification ECMAScript, partie 1



Bonjour mes amis!

Dans cet article, nous prenons une fonction de la spécification et analysons son explication. Aller.

Préface


Même si vous connaissez bien JavaScript, la lecture des spécifications peut être difficile. Le code suivant montre l'utilisation d'Object.prototype.hasOwnProperty:

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

Dans l'exemple, l'objet «o» n'a pas la méthode «hasOwnProperty», nous nous référons donc à son prototype - «Object.prototype» (chaîne de prototype).

Pour décrire le fonctionnement d'Object.hasOwnProperty, la spécification utilise le pseudocode suivant:

Object.prototype.hasOwnProperty (V)

Lorsque hasOwnProperty est appelé avec l'argument V, les étapes suivantes sont effectuées: ... et HasOwnProperty (O, P) L' opération abstraite HasOwnProperty est utilisée pour déterminer si l'objet possède sa propre propriété avec une clé spécifique. La valeur booléenne est renvoyée. L'opération est appelée avec les arguments O et P. Cette opération comprend les étapes suivantes: Qu'est-ce qu'une "opération abstraite"? Quoi [[ ]]? Pourquoi une fonction a-t-elle un point d'interrogation? Que signifie «affirmation»? Découvrons-le.

  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.





Types dans la langue et types dans la spécification


Commençons par quelque chose de familier. Dans la spécification, il existe des valeurs telles que non définies, vraies et fausses, que JS nous connaît. Tous sont des «valeurs de langue» , des «valeurs de types de langue» , qui sont également définies par la spécification.

La spécification utilise des valeurs de langage intégrées, par exemple, un type de données interne peut contenir un champ avec une valeur true ou false. Les moteurs JS n'utilisent généralement pas de significations linguistiques dans leurs mécanismes internes. Par exemple, si le moteur JS est écrit en C ++, il utilisera probablement true et false à partir de C ++, plutôt que la représentation interne des valeurs booléennes de JS.

En plus des types de langage, la spécification utilise des types spéciaux (types de spécification) - types qui sont utilisés uniquement dans la spécification. Les moteurs JS ne sont pas requis pour les exécuter (mais ils le peuvent). Dans cet article, nous allons nous familiariser avec le type spécial «Record» (enregistrement) et son sous-type «Completion Record» (enregistrement d'achèvement).

Opérations abstraites


Les opérations abstraites sont des fonctions définies dans la spécification; ils sont définis afin de réduire les spécifications. Les moteurs JS ne sont pas tenus de les exécuter en tant que fonctions autonomes. Dans JS, ils ne peuvent pas être appelés directement.

Emplacements internes et méthodes internes


Les emplacements internes et les méthodes internes sont indiqués par des noms entre [[]].

Les emplacements internes sont des éléments de données (ensembles) d'un objet JS ou d'un type spécial. Ils sont utilisés pour stocker des informations sur l'état de l'objet. Les méthodes internes sont des fonctions membres d'un objet JS.

Par exemple, chaque objet JS possède un emplacement interne [[Prototype]] et une méthode interne [[GetOwnProperty]].

Les emplacements et méthodes internes ne sont pas disponibles dans JS. Par exemple, nous ne pouvons pas accéder à o. [[Prototype]] ou appeler o. [[GetOwnProperty]] (). Le moteur JS peut les exécuter pour ses propres besoins (internes), mais n'est pas obligé de le faire.

Parfois, les méthodes internes deviennent des opérations abstraites du même nom, comme c'est le cas avec [[GetOwnProperty]]:

[[GetOwnProperty]] (P)

Lorsque la méthode interne [[GetOwnProperty]] de l'objet "O" est appelée avec la clé "P", les actions suivantes sont effectuées: OrdinaryGetOwnProperty n'est pas une méthode interne, car elle n'est associée à aucun objet; l'objet avec lequel il travaille lui est passé en paramètre. OrdinaryGetOwnProperty est appelé «ordinaire» car il opère sur des objets ordinaires. Les objets dans ECMAScript sont ordinaires et inhabituels (exotiques). Un objet est ordinaire s'il se comporte de manière prévisible en réponse à un ensemble de méthodes appelées méthodes internes essentielles. Sinon (lorsque l'objet se comporte de façon imprévisible; pas comme prévu; lorsque le comportement de l'objet s'écarte de la normale, est déviant), il est considéré comme inhabituel.

  1. ! OrdinaryGetOwnProperty(O, P)





L'objet inhabituel le plus célèbre est Array, car sa propriété «length» se comporte de manière non standard: la définition de cette propriété peut supprimer des éléments du tableau.

Une liste des méthodes internes de base peut être trouvée ici .

Dossier d'achèvement


Qu'en est-il des points d'interrogation et d'exclamation? Pour comprendre cela, vous devez comprendre ce qu'est un dossier d'achèvement .

Un enregistrement d'achèvement est un type spécial (défini uniquement à des fins de spécification). Le moteur JS n'est pas obligé d'avoir le même type de données internes.

Un enregistrement d'achèvement est un type de données qui possède un ensemble fixe de champs nommés. L'enregistrement d'achèvement comporte trois champs:
[[Type]]normal, break, continue, return throw. , normal, « () » (abrupt comlpetions)
[[Value]], , , , ,
[[Target]]( )

Chaque opération abstraite renvoie implicitement un enregistrement d'achèvement. Même si le résultat de l'opération abstraite est une simple valeur logique, il est encapsulé dans un enregistrement d'achèvement de type normal (voir Valeurs d'achèvement implicites ).

Note 1: la spécification n'est pas très cohérente dans cette partie; Il existe plusieurs fonctions d'assistance qui renvoient des valeurs nues qui sont utilisées telles quelles, sans être extraites de l'enregistrement d'achèvement.

Remarque 2: Les auteurs de spécifications cherchent à rendre le traitement des enregistrements d'achèvement plus explicite.

Si l'algorithme lève une exception, cela signifie qu'un enregistrement d'achèvement sera reçu avec le type ([[Type]]) et la valeur ([[Value]]) comme objet d'exception. Nous ne considérerons pas d'autres types (pause, poursuite et retour) pour l'instant.

ReturnIfAbrupt (argument) signifie effectuer les opérations suivantes: Voici ce qu'est l'enregistrement d'achèvement; s'il se termine brutalement, revenez immédiatement. Sinon, nous extrayons la valeur de l'enregistrement d'achèvement. ReturnIfAbrupt ressemble à un appel de fonction, mais ce n'est pas le cas. Nous appelons une fonction qui retourne ReturnIfAbrupt (), et non ReturnIfAbrupt elle-même. Son comportement ressemble plus à une macro dans les langages de programmation de type C. ReturnIfAbrupt peut être utilisé comme suit: Ici entre en jeu

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







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

Point d'interrogation : enregistrer  ? Foo () est équivalent à ReturnIfAbrupt (Foo ()). L'utilisation de cette abréviation a une valeur pratique: nous n'avons pas besoin d'écrire du code de gestionnaire d'erreur à chaque fois.

Par analogie, que val soit l' entrée ! Foo () est équivalent à ce qui suit: En utilisant ces connaissances, nous pouvons réécrire Object.prototype.hasOwnProperty comme suit: Object.prototype.hasOwnProperty (P) ... HasOwnProperty peut être réécrit comme ceci: HasOwnProperty (O, P) Nous pouvons également réécrire la méthode interne [ [GetOwnProperty]] sans point d'exclamation: O. [[GetOWnProperty]] Nous supposons que temp est une nouvelle variable temporaire qui n'interagit avec rien.

  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).



Nous savons également que dans le cas où l'instruction de retour renvoie autre chose qu'un enregistrement d'achèvement, ce quelque chose s'enroule implicitement dans un NormalCompletion.

Fallback: Retour? Foo ()


La spécification utilise-t-elle Return? Foo () - pourquoi y a-t-il un point d'interrogation ici? Retour d'

enregistrement ? Foo () peut être développé comme suit: Comportement de retour? Foo () est le même pour une résiliation normale et soudaine. Retour d'enregistrement? Foo () vous permet d'indiquer plus clairement que Foo renvoie un enregistrement d'achèvement.

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





Déclarations


Les instructions de la spécification «affirment» les conditions invariantes des algorithmes. Ils sont ajoutés à la spécification pour plus de clarté, mais ne contiennent aucune exigence d'implémentation; par conséquent, ils n'ont pas besoin d'être vérifiés par une implémentation spécifique.

Et après?


Nous avons appris à lire les spécifications des méthodes simples telles que Object.prototype.hasOwnProperty et des opérations abstraites comme HasOwnProperty. Ayant cette connaissance, nous pouvons comprendre ce que font d'autres opérations abstraites, qui seront discutées dans la partie suivante. Toujours dans le prochain article, nous examinerons les descripteurs de propriétés, qui sont un autre type spécial.


Merci de votre attention. Bon codage!

All Articles