JavaScript nous permet de travailler avec des primitives (chaînes, nombres, etc.) comme s'il s'agissait d'objets. Ils fournissent également des méthodes pour appeler en tant que tels. Nous les étudierons bientôt, mais nous verrons d'abord comment cela fonctionne car, bien sûr, les primitives ne sont pas des objets (et ici nous allons le rendre encore plus clair).
Examinons les principales distinctions entre les primitives et les objets.
Un primitif
Est une valeur d'un type primitif.
Il existe 7 types primitifs : string
, number
, bigint
, boolean
, symbol
, null
et undefined
.
Un objet
Est capable de stocker plusieurs valeurs en tant que propriétés.
Peut être créé avec {}
, par exemple : {name: "John", age: 30}
. Il existe d'autres types d'objets en JavaScript : les fonctions, par exemple, sont des objets.
L’un des avantages des objets est que nous pouvons stocker une fonction comme l’une de ses propriétés.
laissez John = { nom : "Jean", disBonjour : function() { alert("Salut mon pote!"); } } ; john.sayHi(); // Salut mon pote !
Nous avons donc ici créé un objet john
avec la méthode sayHi
.
De nombreux objets intégrés existent déjà, comme ceux qui fonctionnent avec les dates, les erreurs, les éléments HTML, etc. Ils ont des propriétés et des méthodes différentes.
Mais ces fonctionnalités ont un coût !
Les objets sont « plus lourds » que les primitifs. Ils ont besoin de ressources supplémentaires pour soutenir les mécanismes internes.
Voici le paradoxe auquel est confronté le créateur de JavaScript :
Il y a beaucoup de choses que l'on voudrait faire avec une primitive, comme une chaîne ou un nombre. Ce serait formidable d'y accéder en utilisant des méthodes.
Les primitives doivent être aussi rapides et légères que possible.
La solution semble un peu délicate, mais la voici :
Les primitifs restent primitifs. Une seule valeur, au choix.
Le langage permet d'accéder aux méthodes et propriétés des chaînes, des nombres, des booléens et des symboles.
Pour que cela fonctionne, un « wrapper d’objet » spécial qui fournit des fonctionnalités supplémentaires est créé, puis détruit.
Les « wrappers d'objet » sont différents pour chaque type primitif et sont appelés : String
, Number
, Boolean
, Symbol
et BigInt
. Ainsi, ils proposent différents ensembles de méthodes.
Par exemple, il existe une méthode de chaîne str.toUpperCase() qui renvoie un str
en majuscule.
Voici comment cela fonctionne :
let str = "Bonjour"; alert( str.toUpperCase() ); // BONJOUR
Simple, non ? Voici ce qui se passe réellement dans str.toUpperCase()
:
La chaîne str
est une primitive. Ainsi, au moment d'accéder à sa propriété, un objet spécial est créé qui connaît la valeur de la chaîne et dispose de méthodes utiles, comme toUpperCase()
.
Cette méthode s'exécute et renvoie une nouvelle chaîne (affichée par alert
).
L'objet spécial est détruit, laissant la str
primitive seule.
Les primitives peuvent donc fournir des méthodes, mais elles restent légères.
Le moteur JavaScript optimise fortement ce processus. Il peut même ignorer la création de l'objet supplémentaire. Mais il doit quand même adhérer à la spécification et se comporter comme s’il en créait une.
Un nombre a ses propres méthodes, par exemple toFixed(n) arrondit le nombre à la précision donnée :
soit n = 1,23456 ; alert( n.toFixed(2) ); // 1.23
Nous verrons des méthodes plus spécifiques dans les chapitres Nombres et Chaînes.
Les constructeurs String/Number/Boolean
sont destinés à un usage interne uniquement
Certains langages comme Java nous permettent de créer explicitement des « objets wrapper » pour les primitives en utilisant une syntaxe comme new Number(1)
ou new Boolean(false)
.
En JavaScript, cela est également possible pour des raisons historiques, mais fortement déconseillé . Les choses vont devenir folles à plusieurs endroits.
Par exemple:
alert( type de 0 ); // "nombre" alert( type de nouveau numéro (0) ); // "objet"!
Les objets sont toujours véridiques dans if
, donc ici l'alerte s'affichera :
soit zéro = nouveau Nombre (0); if (zero) { // zéro est vrai, car c'est un objet alert( "zéro c'est vrai !?!" ); }
D'un autre côté, utiliser les mêmes fonctions String/Number/Boolean
sans new
est une chose tout à fait correcte et utile. Ils convertissent une valeur en type correspondant : en chaîne, en nombre ou en booléen (primitif).
Par exemple, ceci est tout à fait valable :
soit num = Nombre("123"); // convertit une chaîne en nombre
null/indéfini n'a pas de méthodes
Les primitives spéciales null
et undefined
sont des exceptions. Ils n’ont pas d’« objets wrapper » correspondants et ne fournissent aucune méthode. En un sens, ce sont « les plus primitifs ».
Une tentative d'accès à une propriété d'une telle valeur donnerait l'erreur :
alerte (null.test); // erreur
Les primitives, à l'exception null
et undefined
fournissent de nombreuses méthodes utiles. Nous les étudierons dans les prochains chapitres.
Formellement, ces méthodes fonctionnent via des objets temporaires, mais les moteurs JavaScript sont bien réglés pour optimiser cela en interne, leur appel n'est donc pas coûteux.
importance : 5
Considérez le code suivant :
let str = "Bonjour"; str.test = 5 ; alert(str.test);
Qu'en pensez-vous, est-ce que ça marchera ? Que sera montré ?
Essayez de l'exécuter :
let str = "Bonjour"; str.test = 5 ; // (*) alert(str.test);
Selon que vous avez use strict
ou non, le résultat peut être :
undefined
(pas de mode strict)
Une erreur (mode strict).
Pourquoi? Rejouons ce qui se passe à la ligne (*)
:
Lorsqu’une propriété de str
est accédée, un « objet wrapper » est créé.
En mode strict, y écrire est une erreur.
Sinon, l'opération avec la propriété se poursuit, l'objet obtient la propriété test
, mais après cela, « l'objet wrapper » disparaît, donc dans la dernière ligne str
n'a aucune trace de la propriété.
Cet exemple montre clairement que les primitives ne sont pas des objets.
Ils ne peuvent pas stocker de données supplémentaires.