Aller au contenu

Javascript • Système de types • Objet

Dans cette série d’articles, nous faisons un focus sur les fonctionnalités de base de Javascript.

Cet article concerne le système de types proposé par Javascript. Nous allons détailler maintenant le type object.

Javascript possède un typage dynamique. Qu’est-ce-que cela signifie exactement ?

🔑 Nous ne déclarons pas le type d’une variable avant de l’utiliser.

🔑 Le type d’une variable est déterminé à l’exécution (et pas à la compilation).

🔑 Le type d’une variable peut changer pendant sa durée de vie.

// toto est une chaine de caractères (string)
let toto = "text";
// toto est maintenant un nombre (number)
toto = 69;
// toto est maintenant un booléen (boolean)
toto = true;

Javascript est également faiblement typé. Il permet ainsi des conversions implicites de type.

const oneTwoThree = 123; // number
const fourFive = "45"; // string
const result = oneTwoThree + fourFive; // string
console.log(result); // ← "12345"

Le typage Javascript est divisé en deux mondes : les types de valeurs primitives (ou types primitifs) et les types objet. Les types Objet regroupent :

Les types objet sont des types référence (reference type) : une variable d’un type object contient la référence à la valeur. Nous pouvons aussi dire qu’elle pointe vers cette valeur.

Les variables d’un type objet sont muables (mutable ) : nous pouvons changer la valeur d’un objet après sa création, contrairement aux valeurs primitives.

Nous allons maintenant nous concentrer dans cet article sur le type object.

Le type object permet de stocker une collection de paires de clef-valeur (key-value pairs). Le mot « propriété » est souvent utilisé pour désigner la paire clef-valeur.

La clef est nécessairement de type string ou symbol et doit être unique au sein d’un objet.

La valeur peut être de n’importe quel type (type primitif ou type objet).

// Objet fighter
const fighter = {
// Clef = "firstName" - Valeur = "Rocky"
firstName: "Rocky",
// Clef = "lastName" - Valeur = "Balboa"
lastName: "Balboa",
// Clef = "age" - Valeur = 33
age: 33
};

La plupart des objets héritent de Object. Ils accèdent ainsi aux méthodes d’instance, par exemple hasOwnProperty() ou valueOf().

La façon la plus simple d’accéder à la valeur d’une propriété est d’utiliser l’opérateur de chainage .. Dans ce cas, la clef à droite de . doit être valide.

const fighter = {
firstName: "Rocky",
lastName: "Balboa",
age: 33
};
console.log(fighter.firstName); // ← "Rocky"
console.log(fighter.gender); // ← undefined

Nous pouvons également utiliser l’indexeur []. La valeur entre [ et ] peut être une valeur littérale ou une variable de type string.

const fighter = {
firstName: "Rocky",
lastName: "Balboa",
age: 33
};
const key = "lastName";
console.log(fighter["firstName"]); // ← "Rocky"
console.log(fighter[key]); // ← "Balboa"
console.log(fighter["gender"]); // ← undefined

La façon la plus simple de vérifier la présence (ou l’absence) d’une propriété dans un objet est d’utiliser l’opérateur in.

const fighter = {
firstName: "Rocky",
lastName: "Balboa",
age: 33
};
console.log("firstName" in fighter); // ← true
const key = "lastName";
console.log(key in fighter); // ← true
console.log("gender" in fighter); // ← false

Nous pouvons également utiliser la méthode statique Object.hasOwn() pour vérifier l’existence d’une propriété.

const fighter = {
firstName: "Rocky",
lastName: "Balboa",
age: 33
};
console.log(Object.hasOwn(fighter, "firstName")); // ← true
const key = "lastName";
console.log(Object.hasOwn(fighter, key)); // ← true
console.log(Object.hasOwn(fighter, "gender")); // ← false

Nous pouvons aussi utiliser la méthode d’instance Object.prototype.hasOwnProperty() pour vérifier l’existence d’une propriété.

const fighter = {
firstName: "Rocky",
lastName: "Balboa",
age: 33
};
console.log(fighter.hasOwnProperty("firstName")); // ← true
const key = "lastName";
console.log(fighter.hasOwnProperty(key)); // ← true
console.log(fighter.hasOwnProperty("gender")); // ← false

Nous pouvons parcourir toutes les clefs des propriétés d’un objet en utilisant l’instruction for…in.

const fighter = {
firstName: "Rocky",
lastName: "Balboa",
age: 33
};
// ← "firstName" → "Rocky"
// ← "lastName" → "Balboa"
// ← "age" → 33
for (const propertyKey in fighter) {
console.log(`${propertyKey}${fighter[propertyKey]}`);
}

Nous pouvons également utiliser la méthode statique Object.entries(). Cette méthode retourne un tableau de toutes les propriétés de l’objet passé en paramètre, chaque élément étant lui-même un tableau [clef, valeur].

const fighter = {
firstName: "Rocky",
lastName: "Balboa",
age: 33
};
// ← "firstName" → "Rocky"
// ← "lastName" → "Balboa"
// ← "age" → 33
for (const [propertyKey, propertyValue] of Object.entries(fighter)) {
console.log(`${propertyKey}${propertyValue}`);
}

La méthode statique Object.keys() retourne un tableau des clefs des propriétés de l’objet passé en paramètre.

const fighter = {
firstName: "Rocky",
lastName: "Balboa",
age: 33
};
// ← "firstName"
// ← "lastName"
// ← "age"
for (const key of Object.keys(fighter)) {
console.log(key);
}

La méthode statique Object.values() retourne un tableau des valeurs des propriétés de l’objet passé en paramètre.

const fighter = {
firstName: "Rocky",
lastName: "Balboa",
age: 33
};
// ← "Rocky"
// ← "Balboa"
// ← 33
for (const value of Object.values(fighter)) {
console.log(value);
}

Nous pouvons effectuer une copie superficielle grace au spread operator (…).

const presidentDeGaulle = {
firstName: "Charles",
lastName: "De Gaulle",
gender: "male",
address: {
houseNumber: 55,
street: "rue du Faubourg-Saint-Honoré",
zipCode: "75008",
city: "Paris",
country: "France"
}
};
const presidentPompidou = {
...presidentDeGaulle,
firstName: "Georges",
lastName: "Pompidou",
nextPresident: true
};
// ← Object {
// address: Object { houseNumber: 55, street: "rue du Faubourg-Saint-Honoré", … }
// firstName: "Georges",
// gender: "male",
// lastName: "Pompidou",
// nextPresident: true
// }
console.log(presidentPompidou);
console.log(presidentDeGaulle.address === presidentPompidou.address); // ← true
  • ...presidentDeGaulle retourne la liste des propriétés de l’objet presidentDeGaulle : firstName: "Charles", lastName: "De Gaulle", gender: "male", address: { houseNumber: 55, street: "rue du Faubourg-Saint-Honoré", zipCode: "75008", … } ;
  • la propriété firstName: "Georges" remplace firstName: "Charles" ;
  • la propriété lastName: "Pompidou" remplace lastName: "De Gaulle" ;
  • la propriété nextPresident: true est créée ;
  • ces propriétés constituent le nouvel objet presidentPompidou.

Les propriétés presidentDeGaulle.address et presidentPompidou.address sont strictement identiques. Elles partagent la même référence. Ainsi si nous modifions l’une, l’autre le sera également.

C’est pour cela que nous parlons de copie superficielle :

  • les propriétés avec une valeur primitive sont effectivement dupliquées ;
  • les propriétés avec une valeur objet ont leur référence copiée.

La méthode statique Object.assign() nous permet également de faire une copie superficielle d’un objet.

const address = {
houseNumber: 29,
street: "rue de Rivoli",
zipCode: "75004",
city: "Paris",
country: "France"
};
const administrativeArea = {
department: "Paris",
region: "Île-de-France",
country: "France"
};
const coordinates = {
latitude: 48.85676140144051,
longitude: 2.352542244281719
};
const cityHallOfParis = {
name: "Hôtel de ville"
};
Object.assign(cityHallOfParis, address, administrativeArea, coordinates);
// ← Object {
// name: "Hôtel de ville",
// houseNumber: 29,
// street: "rue de Rivoli",
// zipCode: "75004",
// city: "Paris",
// country: "France",
// department: "Paris",
// region: "Île-de-France",
// latitude: 48.85676140144051,
// longitude: 2.352542244281719
// }
console.log(cityHallOfParis);

Le premier argument de Object.assign() est l’objet cible. Les arguments suivants sont les objets dont les propriétés seront copiées dans l’objet cible.

La copie profonde d’un objet est une copie complète de toutes les propriétés et sous-propriétés, qu’elles soient des valeurs primitives ou des valeurs objet.

Même si nous pouvons réaliser une copie profonde nous même, en utilisant le spread operator par exemple, cela peut devenir vite compliqué s’il y a beaucoup d’objets imbriqués.

Javascript ne propose pas de fonction pour réaliser de copie profonde.

Nous pouvons utiliser la sérialisation JSON.stringify() et dé-sérialisation JSON.parse(). Il faut néanmoins que l’objet à copier puisse être sérialisé.

const receipe = {
name: "Flan pâtissier",
ingredients: [
{ what: "lait", howMuch: { quantity: 1, unit: "litre" } },
{ what: "sucre", howMuch: { quantity: 120, unit: "gramme" } },
{ what: "farine", howMuch: { quantity: 80, unit: "gramme" } },
{ what: "oeufs", howMuch: { quantity: 4 } }
],
baking: {
where: "four",
howLong: { duration: 30, unit: "min" }
}
};
const copyOfReceipe = JSON.parse(JSON.stringify(receipe));
// ← Object { name: "Flan pâtissier", ingredients: (4) […], baking: {…} }
console.log(copyOfReceipe);

Les navigateurs proposent la fonction structuredClone() via l’objet global window. Cette fonction a les mêmes limitations : il faut que l’objet à copier soit sérialisable. Cette fonction n’est pas disponible dans tous les environnements d’exécution Javascript.

Nous avons maintenant une vue d’ensemble du type object. Ce type est la base de nombreux types que nous allons détailler par la suite.

Dans l’article suivant, nous allons détailler les types de collection proposés par Javascript.