Programmation orientée objet en JavaScript - expliquée avec des exemples

JavaScript n'est pas un langage orienté objet basé sur des classes. Mais il a toujours des moyens d'utiliser la programmation orientée objet (POO).

Dans ce tutoriel, je vais vous expliquer la POO et vous montrer comment l'utiliser.

Selon Wikipédia, la programmation basée sur les classes est

un style de programmation orientée objet (POO) dans lequel l'héritage se produit via la définition de classes d'objets, au lieu de l'héritage se produisant via les objets seuls

Le modèle de POO le plus populaire est basé sur les classes.

Mais comme je l'ai mentionné, JavaScript n'est pas un langage basé sur des classes - c'est un langage basé sur un prototype.

D'après la documentaion de Mozilla:

Un langage basé sur un prototype a la notion d'objet prototypique, un objet utilisé comme modèle à partir duquel obtenir les propriétés initiales d'un nouvel objet.

Jetez un œil à ce code:

let names = { fname: "Dillion", lname: "Megida" } console.log(names.fname); console.log(names.hasOwnProperty("mname")); // Expected Output // Dillion // false 

La variable objet namesn'a que deux propriétés - fnameet lname. Aucune méthode du tout.

Alors d'où hasOwnPropertyvient-il?

Eh bien, cela vient du Objectprototype.

Essayez de consigner le contenu de la variable sur la console:

console.log(names); 

Lorsque vous développez les résultats dans la console, vous obtenez ceci:

Remarquez la dernière propriété - __proto__? Essayez de le développer:

Vous verrez un ensemble de propriétés sous le Objectconstructeur. Toutes ces propriétés proviennent du Objectprototype global . Si vous regardez de près, vous remarquerez également notre caché hasOwnProperty.

En d 'autres termes, tous les objets ont accès au Objectprototype du. Ils ne possèdent pas ces propriétés, mais ont accès aux propriétés du prototype.

La __proto__propriété

Cela pointe vers l'objet qui est utilisé comme prototype.

Il s'agit de la propriété de chaque objet qui lui donne accès à la Object prototypepropriété.

Chaque objet a cette propriété par défaut, qui fait référence au Object Protoypesauf lorsqu'il est configuré autrement (c'est-à-dire lorsque l'objet __proto__est pointé vers un autre prototype).

Modifier la __proto__propriété

Cette propriété peut être modifiée en indiquant explicitement qu'elle doit faire référence à un autre prototype. Les méthodes suivantes sont utilisées pour y parvenir:

Object.create()

function DogObject(name, age) { let dog = Object.create(constructorObject); dog.name = name; dog.age = age; return dog; } let constructorObject = { speak: function(){ return "I am a dog" } } let bingo = DogObject("Bingo", 54); console.log(bingo); 

Dans la console, voici ce que vous auriez:

Remarquez la __proto__propriété et la speakméthode?

Object.create utilise l'argument qui lui est passé pour devenir le prototype.

new mot-clé

function DogObject(name, age) { this.name = name; this.age = age; } DogObject.prototype.speak = function() { return "I am a dog"; } let john = new DogObject("John", 45); 

johnLa __proto__propriété de est dirigée vers DogObjectle prototype de. Mais rappelez-vous, DogObjectle prototype de est un objet ( paire clé et valeur ), donc il a aussi une __proto__propriété qui fait référence au Objectprotoype global .

Cette technique est appelée PROTOTYPE CHAINING .

Notez que: l' newapproche par mot - clé fait la même chose, Object.create()mais la rend seulement plus facile car elle fait certaines choses automatiquement pour vous.

Et donc...

Chaque objet en Javascript a accès au Objectprototype de par défaut. S'il est configuré pour utiliser un autre prototype, par exemple prototype2, il prototype2aurait également accès au prototype de l'objet par défaut, et ainsi de suite.

Combinaison objet + fonction

Vous êtes probablement confus par le fait qu'il DogObjects'agit d'une fonction ( function DogObject(){}) et qu'elle possède des propriétés accessibles avec une notation par points . C'est ce qu'on appelle une combinaison d'objets de fonction .

Lorsque les fonctions sont déclarées, par défaut, de nombreuses propriétés leur sont associées. N'oubliez pas que les fonctions sont également des objets dans les types de données JavaScript.

Maintenant, classe

JavaScript a introduit le classmot - clé dans ECMAScript 2015. Il donne l'impression que JavaScript est un langage POO. Mais ce n'est que du sucre syntatique par rapport à la technique de prototypage existante. Il continue son prototypage en arrière-plan mais fait ressembler le corps extérieur à la POO. Nous allons maintenant voir comment cela est possible.

L'exemple suivant est une utilisation générale de a classen JavaScript:

class Animals { constructor(name, specie) { this.name = name; this.specie = specie; } sing() { return `${this.name} can sing`; } dance() { return `${this.name} can dance`; } } let bingo = new Animals("Bingo", "Hairy"); console.log(bingo); 

Voici le résultat dans la console:

Les __proto__références au Animalsprototype (qui à son tour fait référence au Objectprototype).

À partir de là, nous pouvons voir que le constructeur définit les fonctionnalités majeures tandis que tout ce qui est en dehors du constructeur ( sing()et dance()) sont les fonctionnalités bonus ( prototypes ).

En arrière-plan, en utilisant l' newapproche par mot - clé, ce qui précède se traduit par:

function Animals(name, specie) { this.name = name; this.specie = specie; } Animals.prototype.sing = function(){ return `${this.name} can sing`; } Animals.prototype.dance = function() { return `${this.name} can dance`; } let Bingo = new Animals("Bingo", "Hairy"); 

Sous-classement

Il s'agit d'une fonctionnalité de la POO où une classe hérite des fonctionnalités d'une classe parent mais possède des fonctionnalités supplémentaires que le parent n'a pas.

The idea here is, for example, say you want to create a cats class. Instead of creating the class from scratch - stating the name, age and species property afresh, you'd inherit those properties from the parent animals class.

This cats class can then have extra properties like color of whiskers.

Let's see how subclasses are done with class.

Here, we need a parent which the subclass inherits from. Examine the following code:

class Animals { constructor(name, age) { this.name = name; this.age = age; } sing() { return `${this.name} can sing`; } dance() { return `${this.name} can dance`; } } class Cats extends Animals { constructor(name, age, whiskerColor) { super(name, age); this.whiskerColor = whiskerColor; } whiskers() { return `I have ${this.whiskerColor} whiskers`; } } let clara = new Cats("Clara", 33, "indigo"); 

With the above, we get the following outputs:

console.log(clara.sing()); console.log(clara.whiskers()); // Expected Output // "Clara can sing" // "I have indigo whiskers" 

When you log the contents of clara out in the console, we have:

You'll notice that clara has a __proto__ property which references the constructor Cats and gets access to the whiskers() method. This __proto__ property also has a __proto__ property which references the constructor Animals thereby getting access to sing() and dance(). name and age are properties that exist on every object created from this.

Using the Object.create method approach, the above translates to:

function Animals(name, age) { let newAnimal = Object.create(animalConstructor); newAnimal.name = name; newAnimal.age = age; return newAnimal; } let animalConstructor = { sing: function() { return `${this.name} can sing`; }, dance: function() { return `${this.name} can dance`; } } function Cats(name, age, whiskerColor) { let newCat = Animals(name, age); Object.setPrototypeOf(newCat, catConstructor); newCat.whiskerColor = whiskerColor; return newCat; } let catConstructor = { whiskers() { return `I have ${this.whiskerColor} whiskers`; } } Object.setPrototypeOf(catConstructor, animalConstructor); const clara = Cats("Clara", 33, "purple"); clara.sing(); clara.whiskers(); // Expected Output // "Clara can sing" // "I have purple whiskers" 

Object.setPrototypeOf is a method which takes in two arguments - the object (first argument) and the desired prototype (second argument).

From the above, the Animals function returns an object with the animalConstructor as prototype. The Cats function returns an object with catConstructor as it's prototype. catConstructor on the other hand, is given a prototype of animalConstructor.

Therefore, ordinary animals only have access to the animalConstructor but cats have access to the catConstructor and the animalConstructor.

Wrapping Up

JavaScript leverages its prototype nature to welcome OOP developers to its ecosystem. It also provides easy ways to creating prototypes and organize related data.

True OOP languages do not perform prototyping in the background - just take note of that.

A big thanks to Will Sentance's course on Frontend Masters - JavaScript: The Hard Parts of Object Oriented JavaScript. I learned everything you see in this article (plus a little extra research) from his course. You should check it out.

You can hit me up on Twitter at iamdillion for any questions or contributions.

Thanks for reading : )

Useful Resources

  • Object-oriented JavaScript for beginners
  • Introduction to Object Oriented Programming in JavaScript