JavaScript orienté objet : Guide complet du débutant

En JavaScript et dans tout autre langage de programmation, il existe différentes façons d’écrire et d’organiser votre code. Il peut être procédural, fonctionnel, orienté objet, etc.

C’est ce qu’on appelle le paradigme de programmation.

La connaissance de ces principaux styles de programmation est une compétence essentielle pour tout développeur. Vous serez toujours en contact avec eux, que vous travailliez sur votre projet ou sur un autre existant une fois.

Dans le style procédural, par exemple, nous organisons le code en termes de procédure ou de tâche. C’est un style d’écriture facile et vous en avez très probablement écrit un.

Examinons le code suivant :

const btn = document.querySelector("button")

function displayButtonText() {
  console.log(`C'est le texte du bouton cliqué : ${btn.innerText}`)
}

btn.addEventListener("click", displayButtonText)

Ici, nous séparons les instructions du code en procédures. De même, les données et la fonction qui les exploite sont séparées les unes des autres, c’est-à-dire qu’elles sont découplées.

Pour voir le code en action, vous pouvez créer un simple fichier HTML et ajouter un élément bouton. Ajoutez le code ci-dessus à un fichier .js lié. Regardez ensuite dans la console de votre navigateur pour voir le message au clic.

Ok.

Ce n’est pas l’approche du JavaScript orienté objet. Elle vise à utiliser l’objet pour mettre en œuvre des entités de la vie réelle (ce qui nous aide à atteindre facilement la modularité) au lieu de penser en termes de variables et de fonctions individuelles.

Dans ce tutoriel sur la POO, nous allons examiner ce style de programmation en JavaScript. Nous abordons les principes fondamentaux des objets, les concepts de la POO, la fonction constructeur ES5, le concept de prototype, l’héritage et la syntaxe des classes ES6.

C’est parti !

Qu’est-ce que la programmation orientée objet (POO) ?

Comme son nom l’indique, il s’agit d’un style de programmation centré sur l’objet plutôt que sur la fonction. Si nous prenons le code ci-dessus et le réécrivons en mode POO, vous obtiendrez quelque chose comme ceci :

class DisplayButtonText {
  constructor() {
    this.btn = document.querySelector("button")
    this.btn.addEventListener("click", this.buttonClick.bind(this))
  }

  buttonClick() {
    console.log(`C'est le texte du bouton cliqué : ${this.btn.innerText}`)
  }
}

let myButton = new DisplayButtonText()

« Mais…. Je viens de lire que la POO est centrée autour d’un objet. Pourquoi est-ce que je vois class dans le code ci-dessus ? Encore une fois, qu’est-ce que c’est et la nouveauté que je vois ? »

Ne vous préoccupez pas d’eux pour l’instant, nous allons le prendre à la base.

Continuez à lire !

Un regard rapide sur les objets JavaScript

Un objet est une collection de paires nom/valeur appelées membres de l’objet. Chacun de ces membres peut être soit une propriété, soit une méthode, selon leur type respectif.

Si la valeur d’un membre d’un objet est une fonction, on parle de méthode de l’objet. Dans le cas contraire, il s’agit d’une propriété de l’objet (il s’agit de tout type de valeur, de toute primitive ou de tout autre objet, y compris un tableau).

La propriété est la donnée associée à l’objet tandis que la méthode est une action qui peut être exécutée sur l’objet.

Voyons cela en pratique.

Création d’objets en JavaScript

La manière la plus simple de créer un objet est de suivre la structure ci-dessous :

const book = {}

C’est ce qu’on appelle un objet littéral.

À partir de là, vous pouvez ajouter une collection de paires clé/valeur à l’intérieur comme suit :

// object literal
const book = {
  title: "book_one",
  genre: "sci-fi",
  author: "John Doe",
  bookDetails: function() {
    return `Name: ${this.author} | Title: ${this.title} | Genre: ${this.genre}.`
  },
}

A ce stade, nous avons accès aux valeurs du membre. Et nous pouvons les obtenir en utilisant la notation par points ou par crochets.

Pour les propriétés de l’objet, nous utilisons la notation par points ou par crochets. Mais on ne peut accéder à la méthode de l’objet qu’en utilisant la notation point.

Si vous mettez à jour votre code avec ce qui suit et que vous vérifiez ensuite la console, vous devriez voir vos informations.

console.log(book.title) // book_one
console.log(book["genre"]) // sci-fi
console.log(book.bookDetails()) // Nom: John Doe | Titre: book_one | Genre: sci-fi.

Tout comme nous avons pu avoir accès à la valeur du membre, nous pouvons également définir des valeurs en utilisant la notation par points ou par crochets. Vous verrez cela plus loin dans ce guide.

Qu’est-ce que « this » ?

il s’agit d’un mot-clé qui fait simplement référence à l’objet auquel il appartient. Dans notre objet livre, ce mot-clé est présent dans sa méthode. Par conséquent, il fait référence à l’objet livre qui possède la méthode.

« this » peut également faire référence à l’objet global (c’est-à-dire la fenêtre) ou même à un objet indéfini, selon l’endroit où il est appelé.

Conseils: Toujours console.log le mot clé this partout où vous voulez l’utiliser si vous n’êtes pas sûr de ce à quoi il fait référence.

Si nous revenons sur la classe DisplayButtonText plus tôt dans notre code, nous avons également ce mot-clé. Jetons un coup d’œil à son comportement pour mieux le comprendre.

Normalement, nous pouvons créer une instance d’objet à partir d’une classe avec l’opérateur new (nous y reviendrons plus tard). Maintenant, ce mot-clé dans le constructeur de la classe fera référence à l’instance actuelle de la classe. Dans le code, la variable myButton.

Si vous enregistrez ce mot dans le constructeur et que vous enregistrez également la variable myButton après l’instanciation en bas de page, vous verrez qu’ils pointent vers la même chose.

Tout va bien.

Mais un problème se pose dans la fonction de traitement des événements. En effet, dans le gestionnaire, cette variable est liée à l’élément sur lequel le listener est placé. Dans ce cas, l’élément bouton.

C’est le comportement par défaut.

Pour le contourner, nous avons utilisé la méthode ES5 bind(). Cela garantit que la valeur de this pointe vers l’instance de la classe, tout comme l’autre this dans le constructeur de la classe.

Pour voir une image plus claire, enregistrons ce mot-clé dans la méthode handler et cliquons sur le bouton.

buttonClick() {
  console.log(this); // DisplayButtonText {btn: button}
  ...
}

Si vous vérifiez dans la console, vous verrez que ce mot clé pointe vers l’instance de la classe.

Mais si vous supprimez temporairement la méthode bind(this) de l’écouteur d’événements et que vous cliquez à nouveau sur le bouton, le mot clé this dans la méthode du gestionnaire pointera sur l’élément bouton.

class DisplayButtonText {
  constructor() {
    ...
    this.btn.addEventListener("click", this.buttonClick);
  }

  buttonClick() {
    console.log(this); 
    ...
  }
}

Une autre façon de faire pointer ce mot-clé vers l’objet correct sans la méthode bind() est de modifier le gestionnaire pour utiliser la fonction flèche ES6.

class DisplayButtonText {
  constructor() {
    ...
    this.btn.addEventListener("click", this.buttonClick);
  }

  buttonClick = () => {    console.log(this);    ...  };}

Cela fonctionne bien car le mot this dans la fonction flèche fera référence à l’instance de l’objet de la classe.

Maintenant que vous avez une compréhension de base de l’objet, discutons brièvement des concepts OOPS.

Concepts orientés objet

Un programme est dit orienté objet s’il respecte les principes fondamentaux suivants : encapsulation, polymorphisme, héritage et abstraction.

Pour vous en souvenir facilement, j’ai inventé cet acronyme pour vous – EnPIA.

Dans cette section, nous allons examiner brièvement chacun de ces principes.

Héritage – Comme son nom l’indique, la POO nous dit que vous pouvez avoir une classe qui hérite des attributs et des comportements d’une autre classe, créant ainsi une relation parent-enfant.

La classe parent est également appelée super ou base, tandis que la classe enfant est également appelée sub ou dérivée.

Polymorphisme – Signifie simplement la condition de se présenter sous plusieurs formes différentes. Dans la POO, cela signifie que vous pouvez avoir une méthode ou une fonction qui peut être présentée sous plusieurs formes. Vous pouvez décider laquelle de ces formes sera exécutée en fonction des types d’objets.

Vous verrez leurs implémentations plus tard dans ce guide.

Abstraction – Il s’agit d’une façon de modéliser un objet du monde réel avec les caractéristiques les plus essentielles. Lorsque vous pensez à l’abstraction, l’accent est mis sur ce que l’objet fait plutôt que sur la manière dont il le fait.

Prenons une analogie :

Imaginez un système comme une machine à micro-ondes. En tant qu’utilisateur, lorsque vous appuyez sur un bouton, une séquence d’opérations est exécutée derrière la scène. Mais vous n’avez pas besoin de connaître les détails des opérations. Ce qui vous intéresse, c’est que si vous appuyez sur le bouton « ceci et ceci », « ceci et ceci » se produisent.

Donc, en tant que développeur, ce que l’abstraction nous dit, c’est que, lorsque vous modélisez un système ou un objet, vous ne devez révéler que les caractéristiques les plus nécessaires. Et non les implémentations internes.

Encapsulation – signifie littéralement enfermer les données et les informations dans une unité. Dans la POO, nous pouvons lier les données et les méthodes qui opèrent sur elles à l’intérieur d’une classe et restreindre l’accès direct à leur état. Cela nous permet de cacher les détails de mise en œuvre ou l’état qui n’est pas censé être exposé au monde extérieur.

J’espère que c’est clair ?

Quoi qu’il en soit, prenez note de ces concepts, car vous verrez certaines de leurs mises en œuvre au fil de votre lecture.

Revenons à notre objet livre.

Création d’objets multiples

Supposons que nous voulions créer plus d’un objet livre similaire. Nous pourrions simplement dupliquer l’objet littéral pour autant de livres et modifier leurs valeurs en conséquence. Mais cela n’est pas réalisable.

Nous pouvons donc atteindre notre objectif en créant une fonction.

Création d’objets à l’aide de fonctions

Commencez par ajouter le code suivant à votre fichier .js.

// function
function createBook(title, genre, author) {
  const book = {}
  book.title = title,
  book.genre = genre,
  book.author = author,
  book.bookDetails = function() {
    return `Name: ${book.author} | Title: ${book.title} | Genre: ${book.genre}.`
  }
  return book
}

Rien de complexe ne se passe dans le code. Nous avons créé une fonction normale qui accepte les données de notre livre comme argument. A l’intérieur de cette fonction, nous avons défini l’objet livre et l’avons également retourné.

Plus tôt, j’ai dit que nous pouvons ajouter des membres d’objet (c’est-à-dire des propriétés et des méthodes) à un objet littéral par le biais des notations point ou parenthèse. C’est ce que nous avons fait dans la fonction. Nous créons simplement un objet vide et nous lui ajoutons des membres.

Le code ci-dessus est le même que le suivant :

function createBook(title, genre, author) {
  const book = {
    title: title,
    genre: genre,
    author: author,
    bookDetails: function() {
      return `Name: ${book.author} | Title: ${book.title} | Genre: ${book.genre}.`
    },
  }
  return book
}

Ici, nous ajoutons les membres directement.

Note : Puisque nous définissons un objet dont la paire clé/valeur est la même, vous pouvez utiliser la sténographie et simplement remplacer par le nom. Par exemple, titre : titre devient titre.

À ce stade, nous pouvons créer autant d’objets que nous le souhaitons en appelant la fonction comme ceci :

const book1 = createBook("book_one", "sci-fi", "John Doe")
const book2 = createBook("book_two", "fantasy", "Melicia H.")

Ensuite, vous pouvez appeler n’importe quel membre de l’objet en utilisant la notation par points ou par crochets, selon le cas.

console.log(book1)
console.log(book2)
console.log(book1["author"])
console.log(book1.bookDetails())
console.log(book2.bookDetails())

Création d’objets à l’aide de la fonction Constructeur

JavaScript offre une approche plus élégante de la création d’objets à l’aide de la fonction de construction.

Plus tard dans ce guide, vous apprendrez à utiliser la syntaxe des classes qui est une sorte de « sucre syntaxique plus des extras ». Pour l’instant, considérez cette fonction comme une classe.

Comme vous le savez peut-être, une classe est simplement un plan ou un modèle pour créer des objets.

Maintenant, pour créer plusieurs objets livres via la fonction constructeur, nous devons d’abord définir un modèle via cette fonction comme suit :

// Constructor function
function Book(title, genre, author) {
  this.title = title,
  this.genre = genre,
  this.author = author,
  this.bookDetails = function() {
    return `Name: ${this.author} | Title: ${this.title} | Genre: ${this.genre}.`
  }
}

Remarque : par convention, le nom de la fonction constructeur doit toujours commencer par une majuscule.

En utilisant ce modèle de livre, nous pouvons créer des objets réels comme suit :

const book1 = new Book("book_one", "sci-fi", "John Doe")
const book2 = new Book("book_two", "fantasy", "Melicia H.")

À partir de là, vous pouvez appeler n’importe quel membre de l’objet en utilisant la notation par points ou par crochets.

Que se passe-t-il dans le code ?

Rien vraiment, le code est similaire à celui de l’objet créé par la fonction normale plus tôt. En fait, les constructeurs sont aussi des fonctions comme vous pouvez le voir.

Mais ici, nous l’utilisons pour référencer l’instance actuelle de l’objet. Dans ce scénario, lorsque nous créons l’objet book1, il pointe vers book1. La même chose s’applique à book2 et à toute autre instance d’objet que vous créez.

Le nouvel opérateur

Cet opérateur nous permet de créer une nouvelle instance d’objet à partir de notre blueprint réutilisable (dans ce cas, Book).

Si vous regardez de près, la façon dont nous créons un objet à l’aide de la fonction normale est similaire à celle de la fonction constructeur. Sauf que dans la fonction constructeur, nous ne créons et ne retournons pas explicitement un objet.

L’opérateur new s’en charge pour nous en coulisse. Il crée un objet vide, définit this pour pointer sur cet objet et le renvoie.

Bien que cela fonctionne, nous n’avons pas besoin de la fonction bookDetails() dans l’objet réel, comme le montre l’image ci-dessus. Vous trouverez également cette fonction pour chaque nouvelle instance de la fonction constructeur Book. Cela peut poser un problème pour les applications sensibles à la mémoire.

Quelle est donc la solution ?

Cela nous amène à un autre sous-thème important.

Le concept de prototype en JavaScript

L’idée derrière le prototype est très simple. Il s’agit de la logique qui permet à un objet d’hériter des caractéristiques d’un autre objet. Nous avons deux prototypes distincts : le prototype de l’objet et la propriété prototype.

Ces deux éléments sont un peu déroutants. Vous devez donc lire attentivement.

Vous pouvez considérer le prototype de l’objet comme un modèle dont les caractéristiques sont héritées. Par exemple, si vous tapez book1. – de notre dernier code ci-dessus – dans la console, vous verrez quelque chose comme ceci :

 

javascript prototype

Nous comprenons d’où viennent le titre, le genre, l’auteur et les bookDetails. Ce sont des membres que nous avons définis sur le constructeur Book() et qui sont ensuite disponibles pour chaque instance d’objet créée à partir de celui-ci.

Dans ce cas, le Book() est le modèle de l’objet book1 – en d’autres termes, le Book() est l’objet prototype book1.

Sur la même ligne, la fonction constructrice Book() se réfère également à un modèle (qui devient son objet prototype) dont elle a hérité des membres. Cet objet est l’objet global. Et il contient tous les autres membres que vous voyez dans l’image.

L’image ci-dessous rendra justice à l’explication.

 

 

Cela explique que l’objet book1 hérite des membres de son constructeur, Book(). Et parce que le modèle/prototype du Book est l’Object() global, book1 peut également hériter des membres de l’Object().

Maintenant, nous savons que ces membres hérités proviennent de la fonction constructeur – soit l’objet global, soit notre livre.

Mais où pouvons-nous les trouver exactement ?

Ils sont définis dans la propriété prototype des fonctions de construction. Si vous tapez Book.prototype. ou Object.prototype. dans la console, vous verrez les méthodes.

 

prototype property

Avec cette propriété prototype, nous pouvons ajouter de nouveaux membres à la liste des méthodes à hériter par les instances de l’objet. Maintenant, nous n’avons pas besoin de définir les méthodes directement dans la fonction constructeur.

Voyons l’implémentation.

Si vous modifiez la fonction constructeur de Book pour utiliser cette propriété prototype, vous devriez avoir :

function Book(title, genre, author) {
  this.title = title
  this.genre = genre
  this.author = author
}

Book.prototype.bookDetails = function() {
  return `Name: ${this.author} | Title: ${this.title} | Genre: ${this.genre}.`
}

const book1 = new Book("book_one", "sci-fi", "John Doe")
const book2 = new Book("book_two", "fantasy", "Melicia H.")

Comme vous pouvez le voir, la méthode bookDetails() est définie sur la propriété prototype de la fonction constructeur Book.

Sauvegardez le fichier et tapez book1 dans la console.

Comme on le voit ci-dessous, la méthode bookDetails() est maintenant ajoutée au prototype de l’objet (propriété __proto__) plutôt qu’à l’objet lui-même. N’oubliez pas que toutes les instances du constructeur Book auront accès à cette méthode.

Inheritance

Maintenant que nous avons un modèle – c’est-à-dire notre constructeur de livre. Vous voudrez peut-être réutiliser certaines de ses caractéristiques dans un autre modèle. Ainsi, au lieu de créer un nouveau modèle à partir de zéro, vous pouvez vous appuyer sur celui qui existe déjà.

Voyons un exemple pratique.

Nous allons créer un constructeur de Journal qui acceptera non seulement le titre, le genre, l’auteur mais aussi l’année.

Puisque notre livre possède déjà la plupart de ces données, le journal peut hériter des caractéristiques communes pour les utiliser avec sa caractéristique unique, à savoir l’année. Dans ce cas, le Livre est le constructeur parent tandis que le Journal est le constructeur enfant.

Mettons à jour notre code pour avoir :

// Constructor function
function Book(title, genre, author) {
  // ...
}

Book.prototype.bookDetails = function() {
  // ...
}

function Journal(title, genre, author, year) {
  Book.call(this, title, genre, author)
  this.year = year
}

const journal1 = new Journal("Journal_one", "technology", "John Marcus", "2020")

La zone d’intérêt est le constructeur de Journal. Ici, nous créons le constructeur enfant comme n’importe quel autre constructeur. En plus de cela, nous appelons les propriétés définies dans le constructeur parent, Book(), en utilisant la méthode call().

Attention, la valeur de this dans la méthode call() pointera vers l’instance actuelle de l’objet créé par le constructeur Journal. A la fin de la journée, tout ce que nous faisons dans le constructeur enfant est ceci :

function Journal(title, genre, author, year) {
  this.title = title
  this.genre = genre
  this.author = author
  this.year = year
}

Si vous enregistrez votre fichier et tapez journal1 dans la console, vous devriez voir vos données

Et si vous essayez d’accéder aux méthodes du prototype du constructeur parent, vous obtiendrez une erreur, comme le montre l’image.

La plupart du temps, vous voudrez définir une nouvelle méthode qui tiendra compte de vos nouveaux ajouts (dans notre cas, nous avons ajouté une année à la propriété). Nous y reviendrons dans un instant.

Pour l’instant, voyons comment hériter des méthodes parentales. Il suffit d’ajouter ce code sous la fonction constructeur de Journal mais au-dessus de l’appel au constructeur.

// inherit Book prototype
Journal.prototype = Object.create(Book.prototype)

En utilisant la méthode Object.create(), vous pouvez créer un nouvel objet basé sur un objet existant. Ainsi, dans le code ci-dessus, nous avons simplement récupéré les méthodes de Book.prototype et les avons assignées à la propriété prototype du constructeur de Journal.

De cette façon, toutes les instances du constructeur Journal peuvent y avoir accès. Maintenant, enregistrez votre fichier et vérifiez si journal1 peut maintenant accéder à la méthode bookDetails().

Si vous tapez journal1 et que vous le développez pour voir son constructeur, vous verrez qu’il pointe vers son constructeur parent, Book, au lieu de Journal. Cela se produit parce que le Journal hérite des méthodes définies dans la propriété prototype (qui inclut la fonction constructeur) du Livre.

Vous pouvez simplement corriger cela en ajoutant la ligne suivante en bas de page :

// Utiliser le constructeur de journal
Journal.prototype.constructor = Journal

Sauvegardez votre fichier. Retournez à la console et vérifiez à nouveau le constructeur, il devrait pointer vers la fonction attendue.

Ensuite, définissons une nouvelle méthode bookDetails() sur le constructeur Journal() pour capturer son nouvel ajout – c’est-à-dire la propriété année.

Cela devrait être un jeu d’enfant !

Tout ce que nous avons à faire est d’ajouter le code suivant à la fin :

Nous avons fait quelque chose comme ça plus tôt.

Maintenant, chaque fois que nous invoquons ou appelons la méthode bookDetails() sur l’objet enfant – c’est-à-dire journal1, cette méthode remplace celle du parent.

 

La méthode bookDetails() est présentée sous deux formes différentes. Dont, chacune des instances de l’objet implémente sa version de la méthode. C’est le polymorphisme en action.

Les classes ES6

Nous avons vu comment créer des objets en appelant une fonction constructeur. Vous allez maintenant apprendre une façon moderne de faire la même chose en utilisant la syntaxe des classes, qui offre une méthode beaucoup plus simple.

En outre, nous verrons comment mettre en œuvre l’héritage à l’aide de cette syntaxe. Commençons par une simple définition de classe :

class Book {
  constructor(...) {
    // propriétées
  }

  // autres méthodes...
}

Ici, nous utilisons l’ES6 class pour générer un modèle à partir duquel nous pouvons créer notre objet. Cette class contiendra vos méthodes ainsi que le constructeur – où vous pouvez attribuer des propriétés. pour générer un modèle à partir duquel nous pouvons créer notre objet. Ce site Si nous reconstruisons notre fonction Book à partir du constructeur basé sur le prototype, vous aurez :

class Book {
  constructor(title, genre, author) {
    this.title = title
    this.genre = genre
    this.author = author
  }
  bookDetails() {
    return `Name: ${this.author} | Title: ${this.title} | Genre: ${this.genre}.`
  }
}

const book1 = new Book("book_one", "sci-fi", "John Doe")

Comme vous pouvez le voir, le code est soigné et plus facile à écrire. Mais derrière la scène, cette classe crée une fonction appelée Book, comme nous l’avons vu précédemment avec la fonction constructeur. Ensuite, elle prend les propriétés du constructeur de la classe et enregistre également la méthode bookDetails() dans le prototype. Si vous enregistrez le fichier et tapez book1 dans la console.

Comme vous pouvez le constater, l’utilisation de la syntaxe de classe est beaucoup plus simple. Il n’est pas nécessaire d’appeler une méthode sur la propriété du prototype pour qu’ils y soient stockés.

Création d’un héritage de classe (sous-classe ou classe enfant ou classe dérivée)

Pour créer une sous-classe à partir de notre livre existant, nous allons utiliser le mot-clé extends. Il indique à JavaScript de quel élément la classe enfant doit hériter. Si vous avez travaillé avec le composant de classe React, vous verrez souvent ce mot-clé. Reconstruisons la fonction du constructeur de notre Journal (dans le prototype) pour utiliser la syntaxe de classe. Il suffit d’ajouter le code suivant après la classe Book :

// Book sub class
class Journal extends Book {
  constructor(title, genre, author, year) {
    super(title, genre, author)
    this.year = year
  }
}
// instantiate Journal
const journal1 = new Journal("Journal_one", "technology", "John Marcus", "2020")

En plus de la façon dont nous construisons les classes, nous appelons le constructeur super/parent (ainsi que ses propriétés) en utilisant l’opérateur super(). Si vous enregistrez votre fichier et tapez journal1 dans la console, vous verrez vos données. Et vous serez en mesure d’appeler les membres définis sur sa classe ainsi que sur son parent.

 

Comme nous l’avons fait dans le prototype, nous pouvons également définir une nouvelle méthode bookDetails() sur la classe Journal pour capturer l’année. Ajoutez donc ceci dans la classe Journal :
bookDetails() {
  return `Name: ${this.author} | Title: ${this.title} | Genre: ${this.genre} | Year: ${this.year}.`;
}

Sauvegardez votre fichier et testez votre travail. Vous devriez être bon !

Conclusion

Après avoir parcouru tout ce chemin, vous avez appris beaucoup de choses sur le sujet. Vous savez maintenant comment représenter une entité du monde réel en utilisant l’objet. En plus de comprendre les principes fondamentaux de l’objet, vous avez également appris les concepts de la POO : la fonction constructeur, le prototype et l’héritage. En outre, vous avez appris à utiliser la syntaxe de classe ES6 pour écrire un code plus élégant. Donc, si vous aimez ce guide, essayez de le partager sur le web et suivez-moi pour d’autres mises à jou

Nouveau Tutoriel

Newsletter

Ne manquez jamais les nouveaux conseils, tutoriels et autres.

Pas de spam, jamais. Nous ne partagerons jamais votre adresse électronique et vous pouvez vous désabonner à tout moment.