Lorsque vous définissez une fonction dans JavaScript, elle comporte quelques propriétés prédéfinies. l'un d'entre eux est le prototype illusoire. Dans cet article, je détaillerai ce que c'est et pourquoi vous devriez l'utiliser dans vos projets..
La propriété prototype est initialement un objet vide, et des membres peuvent y être ajoutés - comme tout autre objet..
var myObject = fonction (nom) this.name = nom; retournez ceci; ; console.log (typeof myObject.prototype); // objet myObject.prototype.getName = function () return this.name; ;
Dans l'extrait ci-dessus, nous avons créé une fonction, mais si nous appelons myObject ()
, il retournera simplement le la fenêtre
objet, car il a été défini dans la portée globale. ce
retournera donc l'objet global, car il n'a pas encore été instancié (plus sur cela plus tard).
console.log (myObject () === fenêtre); // vrai
Chaque objet dans JavaScript a une propriété «secrète».
Avant de continuer, j'aimerais discuter du lien «secret» qui permet au prototype de fonctionner comme il le fait.
Chaque objet de JavaScript est associé à une propriété «secrète» lorsqu’il est défini ou instancié, nommé __proto__
; Voici comment accéder à la chaîne de prototypes. Cependant, ce n’est pas une bonne idée d’accéder à __proto__
dans votre application, car il n'est pas disponible dans tous les navigateurs.
le __proto__
Une propriété ne doit pas être confondue avec le prototype d'un objet, car il s'agit de deux propriétés distinctes. cela dit, ils vont de pair. Il est important de faire cette distinction, car cela peut être assez déroutant au début! Qu'est-ce que cela signifie exactement? Laisse-moi expliquer. Quand nous avons créé le monObjet
fonction, nous définissions un objet de type Une fonction
.
console.log (type de myObject); // une fonction
Pour ceux qui ne sont pas au courant, Une fonction
est un objet prédéfini en JavaScript et, par conséquent, possède ses propres propriétés (par exemple,. longueur
et arguments
) et des méthodes (par exemple. appel
et appliquer
). Et oui, il a aussi son propre objet prototype, ainsi que le secret __proto__
lien. Cela signifie que, quelque part dans le moteur JavaScript, il y a un peu de code qui pourrait être semblable au suivant:
Function.prototype = arguments: null, longueur: 0, appel: function () // code secret, apply: function () // code secret…
En vérité, ce ne serait probablement pas si simpliste; ceci est simplement pour illustrer le fonctionnement de la chaîne de prototypes.
Nous avons donc défini monObjet
en tant que fonction et donné un argument, prénom
; mais nous ne définissons jamais de propriétés, telles que longueur
ou des méthodes, telles que appel
. Alors pourquoi les travaux suivants?
console.log (myObject.length); // 1 (soit la quantité d'arguments disponibles)
En effet, lorsque nous avons défini monObjet
, il a créé un __proto__
propriété et définir sa valeur à Fonction.prototype
(illustré dans le code ci-dessus). Alors, quand on accède myObject.length
, il cherche une propriété de monObjet
appelé longueur
et n'en trouve pas; il monte ensuite dans la chaîne, via le __proto__ link
, trouve la propriété et la renvoie.
Vous pourriez vous demander pourquoi longueur
est réglé sur 1
et pas 0
- ou tout autre numéro pour ce fait. Ceci est dû au fait monObjet
est en fait un exemple de Une fonction
.
console.log (instance myObject de fonction); // true console.log (myObject === Fonction); // faux
Lorsqu'une instance d'un objet est créée, le __proto__
propriété est mise à jour pour indiquer le prototype du constructeur, qui, dans ce cas, est Une fonction
.
console.log (myObject .__ proto__ === Function.prototype) // true
De plus, lorsque vous créez un nouveau Une fonction
objet, le code natif à l'intérieur du Une fonction
constructeur comptera le nombre d'arguments et mettra à jour this.length
en conséquence, ce qui, dans ce cas, est 1
.
Cependant, si nous créons une nouvelle instance de monObjet
en utilisant le Nouveau
mot-clé, __proto__
pointera vers myObject.prototype
comme monObjet
est le constructeur de notre nouvelle instance.
var myInstance = new myObject («foo»); console.log (myInstance .__ proto__ === myObject.prototype); // vrai
En plus d’avoir accès aux méthodes natives au sein de la Une fonction
.prototype, tel que appel
et appliquer
, nous avons maintenant accès à monObjet
méthode de, getName
.
console.log (myInstance.getName ()); // foo var mySecondInstance = new myObject («bar»); console.log (mySecondInstance.getName ()); // barre console.log (myInstance.getName ()); // foo
Comme vous pouvez l’imaginer, c’est très pratique, car il peut être utilisé pour créer un modèle d’objet et créer autant d’instances que nécessaire - ce qui m’amène au sujet suivant.!
Disons, par exemple, que nous développons un jeu de canevas et que nous avons besoin de plusieurs objets (éventuellement des centaines) à la fois. Chaque objet requiert ses propres propriétés, telles que X
et y
les coordonnées, largeur
,la taille
, et plein d'autres.
Nous pourrions le faire comme suit:
var GameObject1 = x: Math.floor ((Math.random () * myCanvasWidth) + 1), y: Math.floor ((Math.random () * myCanvasHeight) + 1), largeur: 10, hauteur: 10, draw: function () myCanvasContext.fillRect (this.x, this.y, this.width, this.height); …; var GameObject2 = x: Math.floor ((Math.random () * myCanvasWidth) + 1), y: Math.floor ((Math.random () * myCanvasHeight) + 1), largeur: 10, hauteur: 10, draw: function () myCanvasContext.fillRect (this.x, this.y, this.width, this.height); …;
… Faire 98 fois de plus…
Ce que cela va faire est de créer tous ces objets dans la mémoire - tous avec des définitions séparées pour les méthodes, telles que dessiner
et quelles que soient les autres méthodes nécessaires. Ce n’est certainement pas idéal, car le jeu va gonfler la mémoire JavaScript allouée aux navigateurs et le faire tourner très lentement… ou même cesser de répondre..
Bien que cela ne se produise probablement pas avec seulement 100 objets, cela peut quand même être un véritable succès, car il faudra rechercher cent objets différents, plutôt que le simple. prototype
objet.
Pour accélérer l’application (et suivre les meilleures pratiques), nous pouvons (re) définir la propriété prototype de la GameObject
; chaque instance de GameObject
fera ensuite référence aux méthodes au sein de GameObject.prototype
comme si elles étaient leurs propres méthodes.
// définit la fonction constructeur GameObject var GameObject = fonction (largeur, hauteur) this.x = Math.floor ((Math.random () * myCanvasWidth) + 1); this.y = Math.floor ((Math.random () * myCanvasHeight) + 1); this.width = width; this.height = height; retournez ceci; ; // (re) définit l'objet prototype GameObject GameObject.prototype = x: 0, y: 0, largeur: 5, largeur: 5, draw: function () myCanvasContext.fillRect (this.x, this.y, this .width, this.height); ;
Nous pouvons alors instancier le GameObject 100 fois.
var x = 100, arrayOfGameObjects = []; do arrayOfGameObjects.push (new GameObject (10, 10)); while (x--);
Maintenant, nous avons un tableau de 100 GameObjects, qui partagent tous le même prototype et la même définition du dessiner
méthode, qui économise considérablement la mémoire dans l'application.
Quand on appelle le dessiner
méthode, il fera référence à la même fonction exacte.
var GameLoop = function () pour (gameObject dans arrayOfGameObjects) gameObject.draw (); ;
Le prototype d'un objet est un objet vivant, pour ainsi dire. Cela signifie simplement que si, après avoir créé toutes nos instances GameObject, nous décidons qu'au lieu de dessiner un rectangle, nous voulons dessiner un cercle, nous pouvons mettre à jour notre GameObject.prototype.draw
méthode en conséquence.
GameObject.prototype.draw = function () myCanvasContext.arc (this.x, this.y, this.width, 0, Math.PI * 2, true);
Et maintenant, toutes les instances précédentes de GameObject
et toutes les instances futures vont dessiner un cercle.
Oui, c'est possible Vous connaissez peut-être les bibliothèques JavaScript, telles que Prototype, qui tirent parti de cette méthode.
Prenons un exemple simple:
String.prototype.trim = function () return this.replace (/ ^ \ s + | \ s + $ / g, ");;
Nous pouvons maintenant accéder à cela en tant que méthode de n'importe quelle chaîne:
"Foo bar" .trim (); // "foo bar"
Il y a cependant un inconvénient mineur à cela. Par exemple, vous pouvez utiliser ceci dans votre application. mais dans un an ou deux, un navigateur peut implémenter une version mise à jour de JavaScript qui inclut une version native réduire
méthode dans le Chaîne
Le prototype de. Cela signifie que votre définition de réduire
remplacera la version native! Beurk! Pour surmonter cela, nous pouvons ajouter une simple vérification avant de définir la fonction.
if (! String.prototype.trim) String.prototype.trim = function () renvoie this.replace (/ ^ \ s + | \ s + $ / g, ");;
Maintenant, s’il existe, il utilisera la version native du réduire
méthode.
En règle générale, il est généralement considéré comme une pratique recommandée d'éviter toute extension d'objets natifs. Mais, comme pour tout, les règles peuvent être enfreintes si nécessaire.
J'espère que cet article a jeté un peu de lumière sur l'épine dorsale de JavaScript qu'est le prototype. Vous devriez maintenant être sur votre chemin pour créer des applications plus efficaces.
Si vous avez des questions concernant le prototype, faites-le moi savoir dans les commentaires et je ferai de mon mieux pour y répondre..