110 votes

Comment écrire une méthode d'extension en JavaScript ?

Je dois écrire quelques méthodes d'extension en JS. Je sais exactement comment faire cela en C#. Exemple :

public static string SayHi(this Object name)
{
    return "Hi " + name + "!";
}

et ensuite appelé par :

string firstName = "Bob";
string hi = firstName.SayHi();

Comment puis-je faire quelque chose comme ça en JavaScript ?

174voto

T.J. Crowder Points 285826

JavaScript n'a pas d'équivalent exact des méthodes d'extension de C#. JavaScript et C# sont des langages très différents.

La chose la plus proche est de modifier l'objet prototype de tous les objets chaîne : String.prototype . En général, la meilleure pratique est pas pour modifier les prototypes d'objets intégrés dans du code de bibliothèque destiné à être combiné avec d'autres codes que vous ne contrôlez pas. (Le faire dans une application où vous contrôlez quel autre code est inclus dans l'application est acceptable).

Si vous faire modifier le prototype d'un intégré, il est préférable (de loin) de faire de ce dernier un non dénombrable en utilisant Object.defineProperty (ES5+, donc en gros tout environnement JavaScript moderne, et non IE8¹ ou antérieur). Pour correspondre à la possibilité d'énumération, d'écriture et de configuration des autres méthodes de chaîne, cela ressemblerait à ceci :

Object.defineProperty(String.prototype, "SayHi", {
    value: function SayHi() {
        return "Hi " + this + "!";
    },
    writable: true,
    configurable: true
});

(La valeur par défaut pour enumerable est false .)

Si vous aviez besoin de prendre en charge des environnements obsolètes, alors pour String.prototype En particulier, vous pourriez probablement vous en sortir en créant une propriété énumérable :

// Don't do this if you can use `Object.defineProperty`
String.prototype.SayHi = function SayHi() {
    return "Hi " + this + "!";
};

Ce n'est pas une bonne idée, mais vous pourriez vous en sortir. Jamais faire cela avec Array.prototype ou Object.prototype ; créer des propriétés énumérables sur ceux-ci est une mauvaise chose™.

Détails :

JavaScript est un langage prototypique. Cela veut dire que chaque objet est soutenu par un objet objet prototype . En JavaScript, ce prototype est attribué de l'une des quatre manières suivantes :

  • Par le fonction constructeur pour l'objet (par exemple, new Foo crée un objet avec Foo.prototype comme son prototype)
  • Par le Object.create fonction ajoutée dans ES5 (2009)
  • Par le __proto__ (ES2015+, uniquement sur les navigateurs web, existait dans certains environnements avant d'être normalisé) ou Object.setPrototypeOf (ES2015+)
  • Par le moteur JavaScript lors de la création d'un objet pour une primitive parce que vous appelez une méthode sur celle-ci (ceci est parfois appelé "promotion").

Donc, dans votre exemple, puisque firstName est une primitive de type chaîne de caractères, elle est promue au rang de String à chaque fois que vous appelez une méthode sur lui, et cela String Le prototype de l'instance est String.prototype . Ainsi, l'ajout d'une propriété à String.prototype qui fait référence à votre SayHi rend cette fonction disponible sur tous les String (et effectivement sur les primitives de type chaîne, car elles sont promues).

Exemple :

Object.defineProperty(String.prototype, "SayHi", {
    value: function SayHi() {
        return "Hi " + this + "!";
    },
    writable: true,
    configurable: true
});

console.log("Charlie".SayHi());

Il existe quelques différences essentielles entre cette méthode et les méthodes d'extension C# :

  • (Comme DougR souligné dans un commentaire) Les méthodes d'extension de C# peut être appelé sur null références . Si vous avez un string méthode d'extension, ce code :

    string s = null;
    s.YourExtensionMethod();

    travaux. Ce n'est pas le cas avec JavaScript ; null est son propre type, et toute référence de propriété sur null lance une erreur. (Et même si ce n'était pas le cas, il n'y a pas de prototype à étendre pour le type Null).

  • (Comme ChrisW souligné dans un commentaire) Les méthodes d'extension de C# ne sont pas globales. Elles ne sont accessibles que si l'espace de nom dans lequel elles sont définies est utilisé par le code qui utilise la méthode d'extension. (Il s'agit en fait d'un sucre syntaxique pour les appels statiques, ce qui explique pourquoi elles fonctionnent sur le modèle null .) Ce n'est pas vrai en JavaScript : Si vous modifiez le prototype d'un élément intégré, cette modification est vue par tous dans l'ensemble du domaine dans lequel vous faites cela (un domaine est l'environnement global et ses objets intrinsèques associés, etc.) Donc si vous faites ça dans une page web, tous le code que vous chargez sur cette page voit le changement. Si vous faites ça dans un module Node.js, tous Le code chargé dans le même domaine que ce module verra le changement. Dans les deux cas, c'est pourquoi vous ne faites pas cela dans le code de la bibliothèque. (Les travailleurs Web et les threads de travail Node.js sont chargés dans leur propre domaine, ils ont donc un environnement global différent et des éléments intrinsèques différents de ceux du thread principal. Mais ce domaine est toujours partagé avec tous les modules. ils charge.)


¹ IE8 a Object.defineProperty mais il ne fonctionne que sur les objets DOM, pas sur les objets JavaScript. String.prototype est un objet JavaScript.

0voto

Saleh Points 1363

Utilisation de Augmentation globale

declare global {
    interface DOMRect {
        contains(x:number,y:number): boolean;
    }
}
/* Adds contain method to class DOMRect */
DOMRect.prototype.contains = function (x:number, y:number) {                    
    if (x >= this.left && x <= this.right &&
        y >= this.top && y <= this.bottom) {
        return true
    }
    return false
};

0 votes

Est-ce que ça fonctionne avec du javascript pur/vanille ou est-ce que c'est quelque chose pour lequel je dois trouver comment utiliser typescript ? Je pose la question car vous faites un lien vers typescriptlang.org et non vers les docs ES6 ou MDN.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X