Pewarta Proxy

📋 Server Request & Proxy Logs

Loading active proxy settings...
Developed by Khairul Adha (adhexa.id)
Mengambil log server...

⚙ Pengaturan Proxy Target

14 juin 2023

Prototypes natifs

La propriĂ©tĂ© "prototype" est largement utilisĂ©e au centre de JavaScript lui-mĂȘme. Toutes les fonctions constructeurs intĂ©grĂ©es l’utilisent.

Nous verrons d’abord les dĂ©tails, puis comment l’utiliser pour ajouter de nouvelles fonctionnalitĂ©s aux objets intĂ©grĂ©s.

Object.prototype

Disons que nous produisons un objet vide :

let obj = {};
alert( obj ); // "[object Object]" ?

OĂč est le code qui gĂ©nĂšre la chaĂźne "[object Object]" ? C’est une mĂ©thode toString intĂ©grĂ©e, mais oĂč est-elle ? L’objet obj est vide !


Mais la notation abrĂ©gĂ©e obj = {} est identique Ă  obj = new Object(), oĂč Object est une fonction constructeur de l’objet intĂ©grĂ©e, avec son propre prototype rĂ©fĂ©rençant un Ă©norme objet avec toString et d’autres mĂ©thodes.

Voici ce qui se passe :

Lorsque new Object() est appelé (ou un objet littéral {...} est créé), le [[Prototype]] de celui-ci est défini sur Object.prototype conformément à la rÚgle dont nous avons parlé dans le chapitre précédent :

Ainsi, quand on appelle obj.toString(), la méthode est extraite de Object.prototype.

Nous pouvons le vérifier comme ceci :

let obj = {};

alert(obj.__proto__ === Object.prototype); // true

alert(obj.toString === obj.__proto__.toString); //true
alert(obj.toString === Object.prototype.toString); //true

Veuillez noter qu’il n’y a plus de [[Prototype]] dans la chaüne au dessus de Object.prototype :

alert(Object.prototype.__proto__); // null

Autres prototypes intégrés

D’autres objets intĂ©grĂ©s, tels que Array, Date, Function et autres, conservent Ă©galement des mĂ©thodes dans des prototypes.

Par exemple, lorsque nous crĂ©ons un tableau [1, 2, 3], le constructeur new Array() par dĂ©faut est utilisĂ© en interne. Donc Array.prototype devient son prototype et fournit des mĂ©thodes. C’est trĂšs efficace en mĂ©moire.

Par spĂ©cification, tous les prototypes intĂ©grĂ©s ont Object.prototype en haut. C’est pourquoi certaines personnes disent que “tout hĂ©rite d’objets”.

Voici la vue d’ensemble :

Vérifions les prototypes manuellement :

let arr = [1, 2, 3];

// il hérite de Array.prototype ?
alert( arr.__proto__ === Array.prototype ); // true

// puis de Object.prototype ?
alert( arr.__proto__.__proto__ === Object.prototype ); // true

// et null tout en haut.
alert( arr.__proto__.__proto__.__proto__ ); // null

Certaines méthodes dans les prototypes peuvent se chevaucher, par exemple, Array.prototype a son propre toString qui répertorie les éléments délimités par des virgules :

let arr = [1, 2, 3]
alert(arr); // 1,2,3 <-- le résultat de Array.prototype.toString

Comme nous l’avons vu prĂ©cĂ©demment, Object.prototype a aussi toString, mais Array.prototype est plus proche dans la chaĂźne, la variante de tableau est donc utilisĂ©e.

Les outils intĂ©grĂ©s au navigateur, tels que la console de dĂ©veloppement Chrome, affichent Ă©galement l’hĂ©ritage (il faut Ă©ventuellement utiliser console.dir pour les objets intĂ©grĂ©s) :

Les autres objets intĂ©grĂ©s fonctionnent Ă©galement de la mĂȘme maniĂšre. MĂȘme les fonctions – ce sont des objets d’un constructeur intĂ©grĂ© Function, et leurs mĂ©thodes (call/apply et autres) sont extraites de Function.prototype. Les fonctions ont aussi leur propre toString.

function f() {}

alert(f.__proto__ == Function.prototype); // true
alert(f.__proto__.__proto__ == Object.prototype); // true, hérite d'objets

Primitives

Une chose complexe se produit avec les chaßnes, les nombres et les booléens.

Comme on s’en souvient, ce ne sont pas des objets. Mais si nous essayons d’accĂ©der Ă  leurs propriĂ©tĂ©s, des objets wrapper temporaires sont créés Ă  l’aide des constructeurs intĂ©grĂ©s String, Number et Boolean, ils fournissent les mĂ©thodes et disparaissent.

Ces objets sont créés de maniÚre invisible pour nous et la plupart des moteurs les optimisent, mais la spécification le décrit exactement de cette façon. Les méthodes de ces objets résident également dans des prototypes, disponibles sous les noms String.prototype, Number.prototype et Boolean.prototype.

Les valeurs null et undefined n’ont pas de wrappers d’objet

Les valeurs spĂ©ciales null et undefined se dĂ©marquent. Elles n’ont pas de wrapper d’objet, donc les mĂ©thodes et les propriĂ©tĂ©s ne sont pas disponibles pour eux. Et il n’y a pas non plus de prototypes correspondants.

Modification des prototypes natifs

Les prototypes natifs peuvent ĂȘtre modifiĂ©s. Par exemple, si nous ajoutons une mĂ©thode Ă  String.prototype, elle devient disponible pour toutes les chaĂźnes :

String.prototype.show = function() {
  alert(this);
};

"BOOM!".show(); // BOOM!

Au cours du processus de dĂ©veloppement, nous pouvons avoir des idĂ©es de nouvelles mĂ©thodes intĂ©grĂ©es que nous aimerions avoir et nous pourrions ĂȘtre tentĂ©s de les ajouter Ă  des prototypes natifs. Mais c’est gĂ©nĂ©ralement une mauvaise idĂ©e.

Important :

Les prototypes sont globaux, il est donc facile de crĂ©er un conflit. Si deux bibliothĂšques ajoutent une mĂ©thode String.prototype.show, l’une d’elles remplacera la mĂ©thode de l’autre.

Donc, généralement, modifier un prototype natif est considéré comme une mauvaise idée.

Dans la programmation moderne, il n’y a qu’un seul cas oĂč la modification de prototypes natifs est approuvĂ©e. Le polyfilling.

Polyfilling est un terme utilisĂ© pour remplacer une mĂ©thode existante dans la spĂ©cification JavaScript, mais qui n’est pas encore prise en charge par un moteur JavaScript particulier.

Ensuite, nous pouvons l’implĂ©menter manuellement et y ajouter le prototype intĂ©grĂ©.

Par exemple :

if (!String.prototype.repeat) { // s'il n'y a pas une telle méthode
  // ajouter le au prototype

  String.prototype.repeat = function(n) {
    // répéter la chaßne n fois

    // en fait, le code devrait ĂȘtre un peu plus complexe que cela
    // (l'algorithme complet est dans la spécification)
    // mais mĂȘme un polyfill imparfait est souvent considĂ©rĂ© comme suffisant
    return new Array(n + 1).join(this);
  };
}

alert( "La".repeat(3) ); // LaLaLa

Emprunt de prototypes

Dans le chapitre DĂ©corateurs et transferts, call/apply nous avons parlĂ© de l’emprunt de mĂ©thode.

C’est quand nous prenons une mĂ©thode d’un objet et le copions dans un autre.

Certaines méthodes de prototypes natifs sont souvent empruntées.

Par exemple, si nous crĂ©ons un objet semblable Ă  un tableau, nous voudrons peut-ĂȘtre y copier des mĂ©thodes Array.

Par exemple :

let obj = {
  0: "Hello",
  1: "world!",
  length: 2,
};

obj.join = Array.prototype.join;

alert( obj.join(',') ); // Hello,world!

Cela fonctionne car l’algorithme interne de la mĂ©thode join intĂ©grĂ©e ne se prĂ©occupe que des index corrects et de la propriĂ©tĂ© length. Il ne vĂ©rifie pas que l’objet est bien un tableau. Et beaucoup de mĂ©thodes intĂ©grĂ©es sont comme ça.

Une autre possibilité consiste à hériter en fixant obj.__proto__ sur Array.prototype, afin que toutes les méthodes Array soient automatiquement disponibles dans obj.

Mais c’est impossible si obj hĂ©rite dĂ©jĂ  d’un autre objet. N’oubliez pas que nous ne pouvons hĂ©riter que d’un objet Ă  la fois.

L’emprunt des mĂ©thodes est flexible, cela permet de mĂ©langer les fonctionnalitĂ©s provenants d’objets diffĂ©rents en cas de besoin.

Résumé

  • Tous les objets intĂ©grĂ©s suivent le mĂȘme schĂ©ma :
    • Les mĂ©thodes sont stockĂ©es dans le prototype (Array.prototype, Object.prototype, Date.prototype, etc.).
    • L’objet lui-mĂȘme ne stocke que les donnĂ©es (Ă©lĂ©ments de tableau, propriĂ©tĂ©s de l’objet, date).
  • Les primitives stockent Ă©galement des mĂ©thodes dans des prototypes d’objets wrapper : Number.prototype, String.prototype, Boolean.prototype. Seuls undefined et null n’ont pas d’objets wrapper.
  • Les prototypes intĂ©grĂ©s peuvent ĂȘtre modifiĂ©s ou remplis avec de nouvelles mĂ©thodes. Mais il n’est pas recommandĂ© de les changer. La seule cause possible est probablement l’ajout d’un nouveau standard, mais pas encore pris en charge par le moteur JavaScript.

Exercices

importance: 5

Ajoutez au prototype de toutes les fonctions la méthode defer(ms), qui exécute la fonction aprÚs ms millisecondes.

Une fois que vous le faites, ce code devrait fonctionner :

function f() {
  alert("Hello!");
}

f.defer(1000); // montre "Hello!" aprĂšs 1 seconde
Function.prototype.defer = function(ms) {
  setTimeout(this, ms);
};

function f() {
  alert("Hello!");
}

f.defer(1000); // montre "Hello!" aprĂšs 1 seconde
importance: 4

Ajoutez au prototype de toutes les fonctions la mĂ©thode defer(ms), qui renvoie un wrapper, retardant l’appel de ms millisecondes.

Voici un exemple de la façon dont cela devrait fonctionner :

function f(a, b) {
  alert( a + b );
}

f.defer(1000)(1, 2); // montre 3 aprĂšs 1 seconde

Veuillez noter que les arguments doivent ĂȘtre passĂ©s Ă  la fonction d’origine.

Function.prototype.defer = function(ms) {
  let f = this;
  return function(...args) {
    setTimeout(() => f.apply(this, args), ms);
  }
};

// vérification
function f(a, b) {
  alert( a + b );
}

f.defer(1000)(1, 2); // montre 3 aprĂšs 1 seconde

Notez que nous utilisons this dans f.apply pour que notre dĂ©coration fonctionne pour les mĂ©thodes d’objets.

Ainsi, si la fonction wrapper est appelĂ©e en tant que mĂ©thode d’objet, alors this est passĂ© Ă  la mĂ©thode originale f.

Function.prototype.defer = function(ms) {
  let f = this;
  return function(...args) {
    setTimeout(() => f.apply(this, args), ms);
  }
};

let user = {
  name: "John",
  sayHi() {
    alert(this.name);
  }
}

user.sayHi = user.sayHi.defer(1000);

user.sayHi();
Carte du tutoriel

Commentaires

lire ceci avant de commenter

  • Si vous avez des amĂ©liorations Ă  suggĂ©rer, merci de soumettre une issue GitHub ou une pull request au lieu de commenter.
  • Si vous ne comprenez pas quelque chose dans l'article, merci de prĂ©ciser.
  • Pour insĂ©rer quelques bouts de code, utilisez la balise <code>, pour plusieurs lignes – enveloppez-les avec la balise <pre>, pour plus de 10 lignes - utilisez une sandbox (plnkr, jsbin, codepen
)