Prototypes natifs de JavaScript
Découvrez les prototypes natifs de JavaScript : Object.prototype, Array, Function et Number, l'emprunt de méthodes avec call et apply, et pourquoi étendre les prototypes natifs est déconseillé.
Explorer les prototypes natifs
Les prototypes natifs en JavaScript sont les objets que les constructeurs intégrés tels que Array, Object, String, Number et Function utilisent pour partager des méthodes et des propriétés avec chaque valeur qu'ils créent. Lorsque vous appelez [1, 2, 3].map(...) ou "hi".toUpperCase(), la méthode appelée ne réside pas sur le tableau ou la chaîne elle-même — elle réside sur Array.prototype ou String.prototype et est trouvée en parcourant la chaîne de prototypes.
Cette page explique où ces prototypes se situent dans la chaîne, comment les inspecter, comment emprunter leurs méthodes pour des objets qui ne sont pas de vrais tableaux, et pourquoi les étendre de manière permanente est déconseillé. Si vous débutez avec le fonctionnement de la chaîne elle-même, lisez d'abord l'héritage prototypal.
Le rôle des prototypes natifs
Les prototypes natifs sont au cœur de JavaScript : c'est grâce à eux que chaque valeur littérale dispose de méthodes utiles sans que vous ayez à définir quoi que ce soit. Comprendre comment ils s'insèrent dans la chaîne de prototypes vous permet de déchiffrer les messages d'erreur, de réutiliser les méthodes intégrées dans des contextes inattendus, et d'éviter des bugs subtils.
Object.prototype : la racine de la chaîne
Presque tout object que vous créez hérite finalement de Object.prototype, qui se trouve au sommet de la chaîne. C'est de là que proviennent des méthodes comme toString, hasOwnProperty et valueOf. Lorsqu'une recherche échoue sur un object et sur chaque prototype intermédiaire, la chaîne se termine à Object.prototype, et le maillon suivant est null.
Les prototypes Array, Function et Number
Les types intégrés superposent leur propre prototype au-dessus de Object.prototype. Un tableau, par exemple, hérite de Array.prototype (qui fournit map, filter, push, …), et Array.prototype hérite à son tour de Object.prototype. Le même schéma s'applique aux fonctions et aux nombres.
Emprunter des méthodes avec call et apply
Étant donné que les méthodes natives résident sur des prototypes, vous pouvez les emprunter et les exécuter sur n'importe quelle valeur compatible en utilisant call ou apply. L'exemple classique consiste à utiliser les méthodes de Array.prototype sur des objets semblables à des tableaux (comme arguments ou une string) qui ne possèdent pas ces méthodes en propre.
Étendre les prototypes natifs
Bien que JavaScript permette d'étendre les prototypes natifs, cette pratique est généralement déconseillée dans la portée globale en raison de conflits potentiels dans des bases de code plus larges ou avec des scripts tiers. Il existe plusieurs raisons concrètes de l'éviter :
- Collisions de noms. Si deux scripts ajoutent une méthode portant le même nom (ou si une future version d'ECMAScript standardise ce nom avec un comportement différent), l'un écrase silencieusement l'autre.
- Énumérabilité. Une méthode ajoutée par simple assignation est énumérable, et apparaît donc dans les boucles
for...insur chaque object de ce type, à moins d'être protégée parhasOwnProperty— une source courante de bugs. - Effets de bord globaux. Le changement affecte chaque valeur de ce type dans tout le programme, y compris le code que vous n'avez pas écrit.
L'extrait ci-dessous illustre le piège de for...in. Une assignation classique se retrouve dans la boucle ; l'utilisation de Object.defineProperty pour rendre la méthode non énumérable permet de l'éviter.
Comprendre cette capacité reste utile pour identifier des problèmes potentiels, lire des polyfills et explorer des patterns avancés dans des environnements contrôlés.
Exemples pratiques avec les prototypes natifs
Manipuler des tableaux avec Array.prototype
Considérez la puissance de Array.prototype qui offre des méthodes comme map, filter et reduce. Ces méthodes fournissent des solutions élégantes pour transformer et traiter les données stockées dans des tableaux. Nous pouvons ajouter de nouvelles méthodes en manipulant Array.prototype.
Dans cet exemple, nous définissons une nouvelle méthode mapToSquare sur le prototype, qui utilise la méthode intégrée map pour retourner le carré de chaque nombre.
Enrichir les chaînes avec String.prototype
String.prototype est également un riche répertoire de méthodes, telles que toLowerCase, toUpperCase et includes, qui facilitent la manipulation et l'interrogation des chaînes de caractères.
Dans cet exemple, nous définissons removeSpace sur le prototype, en utilisant split, filter et join pour supprimer les espaces.
Extensions personnalisées des prototypes natifs
Bien que la prudence soit de mise, l'ajout de méthodes personnalisées aux prototypes natifs peut illustrer la flexibilité de JavaScript. Voici comment vous pourriez étendre Array.prototype pour inclure une méthode calculant la somme des éléments d'un tableau :
Cette méthode personnalisée, sum, ajoute une nouvelle dimension au prototype Array, illustrant à la fois le potentiel et les risques de l'extension des prototypes natifs.
Bonnes pratiques pour l'utilisation des prototypes natifs
Bien que la puissance des prototypes natifs soit indéniable, voici quelques bonnes pratiques pour que votre code reste robuste et sans conflits :
- Évitez d'étendre les prototypes natifs : sauf absolue nécessité, évitez de modifier les prototypes intégrés pour prévenir des comportements inattendus dans votre code ou dans des bibliothèques tierces.
- Utilisez
Object.definePropertypour des extensions plus sûres : lorsque vous devez ajouter des méthodes, utilisezObject.definePropertypour les rendre non énumérables. Cela empêche les bouclesfor...inde récupérer vos propriétés personnalisées et réduit les conflits de noms. - Utilisez les polyfills avec discernement : lorsque vous utilisez des polyfills pour combler des fonctionnalités manquantes dans les navigateurs plus anciens, assurez-vous qu'ils vérifient l'existence de la méthode avant de l'ajouter au prototype.
- Tirez parti des fonctionnalités modernes de JavaScript : avec l'évolution de JavaScript, de nombreuses tâches qui nécessitaient autrefois d'étendre les prototypes natifs peuvent désormais être réalisées avec de nouvelles constructions du langage, telles que les classes et les modules.
Conclusion
Les prototypes natifs sont le mécanisme derrière chaque méthode intégrée que vous utilisez quotidiennement : ils s'inscrivent dans la chaîne de prototypes, avec Object.prototype à la racine, et permettent aux valeurs comme les tableaux, les fonctions et les nombres de partager des comportements. Savoir les inspecter avec Object.getPrototypeOf, emprunter leurs méthodes avec call et apply, et résister à l'envie de les étendre globalement rendra votre code à la fois plus capable et plus prévisible.
Pour aller plus loin, consultez l'héritage prototypal pour comprendre comment la chaîne est construite, et les méthodes de prototype et les objets sans __proto__ pour l'API moderne Object.create / Object.getPrototypeOf.