date 17 janvier 2026
prototype plus élégant que celui de class.Dans cet article je vais tenter d'en comprendre les secrets.Je me suis largement inspiré de JavaScript avancé de David Gayerie.
Les exemples que je propose ici ont été testés avec quickjs de Fabrice Bellard. La console de Firefox est imbuvable.
On peut définir un point A(-1, 1) par :
let A = {x: -1, y: 1};
Un peu plus élaboré :
let pt = {x: 0,y: 0,set: function (x, y) {this.x = x;this.y = y;}};
le dernier membre est une méthode. On retrouve le mot-clé this de C++.
J'ai éssayé de rajouter à l'objet pt une méthode après coup par :
pt.prototype.mv = function(dx, dy) ..
mais il n'en veut pas, il faut en passer par un constructeur (1).
Un constructeur est une fonction (2):
function Pt(x, y) {this.x = x;this.y = y;}
Gayerie dit que l'initiale en majuscule est l'usage pour un constructeur.
On va ajouter trois méthodes à Pt.
Pt.prototype.set = function (x, y) {this.x = x; this.y = y;}Pt.prototype.mv = function (dx, dy) {this.x += dx; this.y += dy;}Pt.prototype.str= function () {return "Pt(" + this.x + "," + this.y + ")";}
Grognements
Généralement les sites qui traitent de la question me mettent en pétard. Exemple sur MDN.
function Personne(prenom, nom, age, genre, interets) {// Définitions des propriétés et méthodes}
Nom d'une pipe ! Quel intéret de noyer le lecteur dans des kilomètres de code ? Est-ce pour le distraire de ce qu'il voudrait comprendre par un verbiage immonde ? Pensent-ils qu'on ne vient au JS que pour coder des applications de e-commerce ?
Ne leur vient-il jamais à l'esprit d'envisager
let a = {x: 1, y: 2} ou function Pt(x, y) ou fonction Complex(x, y) ou encore function RGB(r, g, b) ? «Ce que l'on conçoit bien s'énonce clairement, et les mots pour le dire viennent aisément». Jean de La Bruyère.
Disons qu'on veut créer un objet Vect, qui hérite de l'objet Pt :
function Vect(x, y) {}Vect.prototype = new Pt;Vect.prototype.str = function () {return "Vect(" + this.x + "," + this.y + ")";}
Après avoir copié-collé le code ci-dessus dans qjs j'ai testé la nouvelle bêt dans quickjs :
qjs > let v = new Vect(1, 1);undefinedqjs > v.str();"Vect(undefined,undefined)"qjs > v.set(1,1);undefinedqjs > v.str();"Vect(1,1)"qjs >
Il a bien crée l'instance v de Vect mais a négligé les coordonnées qui lui ont été transmises.
Il a cependant bien hérité de la méthode set comme on le voit ci-dessus.
Voci ce que j'ai encore testé avec quickjs :
qjs > sqrt = Math.sqrt;undefinedqjs > sqrt(4);2qjs > Vect.prototype.norm = function() {{ ... return sqrt(this.x * this.x + this.y * this.y);{ ... }function ()qjs > v.norm()1.4142135623730951qjs >
Au passage je ne comprends pas qu'il faille dans JS préfixer les fonctions mathématiques avec Math.. Grognon comme je suis, du temps où j'utilisais JS (voir mes articles Repères cartésiens, Nombres complexes) je commençais par définir sqrt, sin, cos, etc. C'est ce que j'ai fait ici dès le début.
Une alternative à l'héritage tel que présenté au paragraphe Héritage consiste à appeler la fonction prototype call() comme suit :
function Vect(x, y) {Pt.call(this, x, y);}
De cette manière let v = new Vect(1, 1); va créer l'instance v comme au paragraphe précédent mais initialiser correctement ses propriétés x et y.
J'arrête là. C'est parce que les classes de JS ne m'intéressent pas, que je me suis fendu de cet article. J'en ai assez soupé dans le temps avec Python, C++, D, Java et Turbo Pascal.
J'ai essayé la surcharge des opérateurs, mais semble-t-il cette faculté n'est pas prévue dans JS. Si on a let u = new Vect(-1, 2); et let v = new Vect(1, 1) on ne peut pas écrire
let w = u + v;.
Accessoirement je fais de la pub à Quick JS. Pour tester du code JS dans un terminal je ne connais pas mieux. Les messages renvoyés par Fabrice Bellard sont parfois déroutants, comme par exemple la reproduction des accolades ouvrantes non encore fermées dans la marge gauche. Je pense que son intention est de nous guider pour ne pas oublier de la fermer. Dans le REPL de guile ou de Lisp, une telle attention serait bien utile, tellement le dénombrement des parenthèses est crucial. Ça me rappelle un sketch de Raymond Devos «chaque fois que j'ouvre une parenthèse, j'oublie de la fermer»..
Les dates dans Unix ou Linux c'est l'enfer. Les jours commencent à 1, mais les mois à 0. L'epoch est datée au 1er janvier 1970 (cf. time_t) en C, mais les années sont comptabilisées à partir de 1900. En JS c'est pire.
Dans mon artcle précédent Agenda minimaliste en HTML pur j'ai fait une lib date.h, date.c pour palier cet écueil. Ici je la retranscris en js.
Le script Day.js définit un objet Day qui est en fait une date. Je l'ai appelé ainsi pour lever toute ambiguïté avec le Date() de JS.
Exemple let date = new Day(20, 1, 2026) crée la date '20/01/2026'
Day() construit un objet avec la date courante Day(j) construit un objet avec le jour j et le mois et l'année courante. Day(j, m) construit la date 'j/m/année courante'. Day(j, m, a) construit la date 'j/m/a'. wday() renvoie le numéro du jour de la semaine 'dimanche' -> 0, 'lundi' -> 1, ... , 'samedi' -> 6. eu() est une sorte de toString qui renvoie la notation eurpéenne 'j/m/a'. fr() renvoie la date en français e.g. 'mardi 20 janvier 2026'. yday() est l'ordinal de la date dans l'année qui va de 0 à 364 ou de 0 à 365 pour les années bissextiles. from_yday(n, y) n'est pas une méthode mais un autre constructeur qui est la réciproque de yday. Pour l'ordinal n et l'année y et renvoie l'objet Day() correspondant.
puts() est un alias de console.log(). Je donne souvent des alias de fonctions js.
Exemple : sin = Math.sin, parce que Math.sin me gonfle, évidemment qui sin c'est des maths. Je ne sais pas si c'est réglo mais au moins ça marche.
Au moment où j''écris ces lignes on est le 20 janvier 2026
$ qjs -i --script Day.jsQuickJS - Type "\h" for helpqjs > let d = new Day(); // aujourd'huid: 20, m: 1, y: 2026undefinedqjs > puts(d.eu());20/1/2026undefinedqjs > puts(d.fr());mardi 20 janvier 2026undefinedqjs > let n = d.yday(); // ordinal dans l'année i.e. 19undefinedqjs > puts("n = d.yday() = " + n);n = d.yday() = 19undefinedqjs > n += 12; // dans 12 jours31qjs > let d12 = from_yday(n, 2026);d: 1, m: 2, y: 2026undefinedqjs > puts("Dans 12 jours on sera " + d12.fr());Dans 12 jours on sera dimanche 1 février 2026undefinedqjs >
Remarque :
const _Day_d_es = ["domingo", "lunes", "martes", ... ];const _Day_m_es = ["", "enero", "febrero", ... ];this.es = function () { ...
Venons-en à mon modeste script.
/*:w | !qjs --script %:!qjs -i --script %This script is designed for dates in French. To adapt it to your language, simply add two tables and a method, for example for Spanish:const _Day_d_es = ["domingo", "lunes", "martes", ... ];const _Day_m_es = ["", "enero", "febrero", ... ];copy the fr() method and replace all instances of _fr by _es in the copy.this.es = function () { ...Example of session with Quick js$ qjs -i --script Day.jsQuickJS - Type "\h" for helpqjs > let td = new Day(); // today is 2026-1-20undefinedqjs > td.eu();"20/1/2026"qjs > let n =td.yday(); //2026-1-1 -> 0, 2026-12-31 -> 364undefinedqjs > puts(n);19undefinedqjs > let d = from_yday(n+7, 2026); // 7 days afterundefinedqjs > d.eu();"27/1/2026"qjs >*/// _Day_mdays[m] : length of month m for not leap year, or else mdays[2] = 29const _Day_mdays = [ 0, // unused: m ∈ ⟦1, 12⟧31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];// _Day_ydays[m] : days before the month m for not leap year, else +1const _Day_ydays = [0, //unused0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];// TODO add here your language// french week daysconst _Day_d_fr = ["dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi","samedi"];// french months. Notice the empty string.const _Day_m_fr = ["", "janvier", "février", "mars", "avril", "mai", "juin","juillet", "août", "septembre", "octobre", "novembre", "décembre"]// check if d/m/y is validfunction _Day_valid(d, m, y) {let msg = "";if(y <= 2000 || y >= 2100) msg += "y ∉ ⟦2001, 2099⟧\n";if(m < 1 || m > 12) msg = "m ∉ ⟦1, 12⟧\n";// len of month i.e. d ∈ ⟦1, ml⟧let ml = (y % 4 == 0 && m == 2) ? 29 : _Day_mdays[m];if(d< 0 || d > ml) msg += "d ∉ ⟦1, " + ml + "⟧\n";if(msg == "") return true;puts(msg);return false;}puts = console.log;function Day(d, m, y) {// managing default argumentslet td = new Date(); // todaylet l = arguments.length;if(l < 3) { y = td.getFullYear(); }if(l < 2) { m = td.getMonth() + 1; }if(l < 1) { d = td.getDate(); }/* let d = new Day(19, 1, 2026); d.eu() -> "19/1/2026" */if(!_Day_valid(d, m, y)) {console.log("Date invalide\n");return undefined;}// setting argumentsthis.d = d; // day this.d ∈ ⟦0, 28 | 29 | 30 | 31⟧this.m = m; // this.m ∈ ⟦1, 12⟧this.y = y; // this.y ∈ ⟧200, 2100⟦/* let d = new Day(19, 1, 2026); d.eu() -> "19/1/2026" */this.eu = function () {return this.d + "/" + this.m + "/" + this.y;}// week day sunday -> 0, ... , saturday ->6this.wday = function() {let date = new Date(this.y, this.m - 1, this.d);return date.getDay();}// TODO add here for your language/* let d = new Day(19, 1, 2026); d.fr() -> "lundi 18 janvier 2026" */this.fr = function () {// one can replace the two line below by let wd = this.yday()let date = new Date(this.y, this.m - 1, this.d);let wd = date.getDay();return _Day_d_fr[wd] + " " + this.d + " " + _Day_m_fr[this.m] + " " + y;}/* yday is the ordinal of the day in the year starting at 0(1, 1, 2026) -> 0, (19, 1, 2026) -> 18, ... */this.yday = function() {let yday = this.d + _Day_ydays[this.m] - 1;if(this.m > 2 && y % 4 == 0) yday++;return yday;}}/* converse of Day().yday */function from_yday(n, y) {// algorithm picked up from source Python-3.14/from_ordinallet m = (n + 50) >> 5; //estimate monthlet prior = _Day_ydays[m]; // days before month mif(m > 2 && y % 4 == 0) prior++; // leap year// length of the estimate monthlet mlen = (m == 2 && y% 4 == 0) ? 29 : _Day_mdays[m];if(prior > n) { // estimate is too largem--; // one step backwordsmlen = (m == 2 && y% 4 == 0) ? 29 : _Day_mdays[m];prior -= mlen; // updating preceding days}let d = n - prior + 1;return new Day(d, m, y);}
Notes
(1)
Gayerie dit qu'on peut ajouter à un objet des propriétés avec Object.defineProperty, j'ai jeté un œil à la prose (toujours imbuvable) de MDN, mais je n'ai pas essayé.
Ce constructeur me fait penser aux fonctions du C avec des «membres» static.