11484 sujets

JavaScript, DOM et API Web HTML5

Bonsoir à tous
Durant des années, j'utilisais un style de programmation du genre

function MonBidule(...) {
    ....
}
function MaChose(...) {
    ....
}
function MonObjet(....) {
   this.xxx = ...;
   this.yyy = ...;
   this.bidule =MonBidule;
   this.chose = MaChose;
}

jusqu'à ce que, grâce en particulier à ma fréquentation de ce site, je change de style pour:

function MonObjet(....) {
   this.xxx = ...;
   this.yyy = ...;
   this.bidule = function() {
   }
   this.chose = function() {
   }
}

ce qui est infiniment plus simple, confortable, maintenable ...

Je suis en train de faire truc un peu complexe, dans lequel j'ai quelque chose comme


function MonSousObjet(...) {

}
function MonObjet() {
    ....
   this.truc = new MonSousObjet(....):
}

Question:
Est-il possible et raisonnable de définir un sous-objet à l'intérieur d'un objet, dès lors que le sous-objet n'est pas utilisé directement à l'extérieur de l'objet?
Merci de vos avis.
Bonsoir.

Je ne sais pas si c'est la réponse à votre question mais :
a écrit :

Pour définir une méthode dont disposeront tous les objets qu'on souhaite définir, il faut l'assigner comme propriété de la propriété prototype de l'objet.

quelque chose comme :
MonObjet.protototype.MonBidule = function(){
...
}

C'est ce qu'on m'a toujours dit…

Smiley smile

Edit : peut-être pas la réponse à votre question.
Modifié par Zelena (31 Aug 2017 - 18:51)
Merci de ta réponse
Non, ce n'était pas l'objet de ma question.
Écrire

MonObjet.protototype.MonBidule = function(){
    ...
}

me semble une façon alambiquée d'écrire

function MonObjet(...) {
    this.MonBidule = function() {
    ....
   }
}

sauf dans des cas spécifiques où l'on désirerait dynamiquement modifier la fonction interne à la classe d'objet, ce qui bien entendu peut arriver. Par contre il arrive fréquemment que l'on puisse désirer modifier une méthode dans UN objet, et non dans la classe, ce qui est une façon de faire un héritage.

Ma question c'est: peut on définir un sous-objet dans un objet?

Je ne crois pas cela faisable, et si on pouvait le faire on se mélangerait certainement les pinceaux pour savoir si le "this" qu'on utilise à l'intérieur du sous-objet se réfère à l'objet principal ou au sous-objet. Rien que pour ça, c'est sans doute impossible ou à prohiber.
Mais, de la même façon que j'ai récemment découvert la méthode bind() qui permet plein de choses, en particulier définir un gestionnaire d'évènement qui se réfère à un objet particulier, peut être y a-t-il des moyens de faire fonctionner cela.

Les membres de ce forum possèdent collectivement une telle connaissance et une telle expertise que ça vaut la peine de poser la question.
Modifié par PapyJP (31 Aug 2017 - 19:13)
Modérateur
a écrit :

Écrire
………
me semble une façon alambiquée d'écrire

Pas tout à fait, non seulement l'utilisation des prototypes est mieux pour l'héritage mais aussi pour les performances.
Si j'instancie dix objets avec la méthode déclarée dynamiquement, cette méthode sera chargée 10x en mémoire, avec le prototype une seule fois. Ce qui fait aussi (même si je ne le conseille guère) qu'on peut modifier cette méthode par la suite.
C'est pourquoi les attributs sont généralement définis dynamiquement car on souhaite que leurs valeurs diffèrent, mais c'est rarement le cas des méthodes.

Il n'y a pas de «meilleure» méthode, on peut utiliser l'une ou l'autre (ou les deux) selon les besoins de ce que l'on fait.

a écrit :

Est-il possible et raisonnable de définir un sous-objet à l'intérieur d'un objet, dès lors que le sous-objet n'est pas utilisé directement à l'extérieur de l'objet?

dès lors qu'il n'est pas utilisé ailleurs il me semble mieux de l'instancier à l'intérieur en effet. Dans le petit exemple suivant je montre les deux possibilités:

var Parking = function(){
  this.places = [];
};

// on peut créer l'instance

Parking.prototype = {
  ajouter: function(plaque, modele, place){
    this.places[place] = new Voiture(plaque, modele);
  }
};

// ou alors passer une référence

Parking.prototype = {
  ajouter: function(voiture, place){
    this.places[place] = voiture;
  }
};

Si notre application est une application de gestion de parking et que les voitures n'existent pas en dehors du parking la première solution est plus pertinente. Si on contraire on gère la circulation dans une ville et que le parking n'est qu'une péripétie pour la voiture le second me semble plus indiqué.

Il n'y a pas de contrindication avec le mot clé this. Lorsque on utilise new pour instancier à partir d'une fonction this fait toujours référence à l'instance sans confusion possible avec l'endroit où l'on a instancié l'objet:


var direSonNom = function(){
  alert(this.nom);
};
var Personne = function(){
  this.nom = 'papyJP';
  this.dire = direSonNom;
};
var qqun = {
  nom: 'kusto',
  personne: new Personne(),
  dire: direSonNom
};
qqun.personne.dire(); // papyJP, sans nul doute possible
qqun.dire(); // kusto, dépend du contexte
direSonNom();  // undefined, dépend du contexte;


bind peut servir pour clarifier le this lorsqu'on n'utilise pas «new» et qui peut être changeant selon le contexte, ou alors en POO pour utiliser des méthodes comme callbacks:


Parking.prototype.ajouterPlusTard = function(voiture, place) {
  window.setTimeout( this.ajouter.bind( this, voiture, place ), 1000 );
};
// qui devrait autrement s'écrire:
Parking.prototype.ajouterPlusTard = function(voiture, place) {
  var that = this;
  window.setTimeout( function(){ that.ajouter(voiture, place )}, 1000 );
};


Pour conclure, non seulement c'est faisable, mais c'est très courant.
Modifié par kustolovic (31 Aug 2017 - 23:08)
Merci de ta réponse
Je vais avoir besoin d'un peu de temps pour bien comprendre les implications de tout cela.
Je te reviendrai sans doute avec des questions
J'ai commencé par inclure mon sous-objet dans la définition de mon objet, c'est à dire que j'ai maintenant:

function MonObjet(...) {
    this.MonSousObjet = function (...) {
        .............................
   }
   this.sousObjet1 = new this.MonSousObjet();
   this.sousObjet2 = new this.MonSousObjet();
}

au lieu de

function MonSousObjet (...) {
        .............................
}
function MonObjet(...) {
   this.sousObjet1 = new MonSousObjet();
   this.sousObjet2 = new MonSousObjet();
}

ça marche effectivement très bien, y compris l'utilisation de bind(this) dans les deux classes d'objet.

A ce propos (comme on dit quand ça n'a pas grand chose à voir) Smiley cligne y a-t-il une bonne pratique pour l'utilisation de
 function NomDeFonction()

ou de
NomDeFonction = function()

actuellement je n'utilise la deuxième forme qu'à l'intérieur de la définition d'une classe d'objet, mais c'est un héritage des pratiques des années 1990 où j'ai commencé à utiliser le Javascript...
Modifié par PapyJP (01 Sep 2017 - 10:26)
kustolovic a écrit :

Si j'instancie dix objets avec la méthode déclarée dynamiquement, cette méthode sera chargée 10x en mémoire, avec le prototype une seule fois. Ce qui fait aussi (même si je ne le conseille guère) qu'on peut modifier cette méthode par la suite.
C'est pourquoi les attributs sont généralement définis dynamiquement car on souhaite que leurs valeurs diffèrent, mais c'est rarement le cas des méthodes.

Je comprends très bien ce point de vue, effectivement les méthodes sont généralement les mêmes pour toutes les instances d'une classe d'objet.
Je n'avais pas réalisé que cela se traduisait par une recopie de la méthode dans chaque instance.

D'un autre côté, du point de vue écriture du code, définir une méthode à l'intérieur de la définition de l'objet me semble plus naturel: la définition d'une classe est ainsi complète, à l'intérieur d'un bloc de code, et c'est pour cela que j'ai abandonné la définition séparée des méthodes.

Est-ce que quelque chose comme

function MonObjet() {

    this.prototype.bidule = function() {
        ........
    }
}

aurait un sens?
Bonjour.
PapyJP a écrit :

D'un autre côté, du point de vue écriture du code, définir une méthode à l'intérieur de la définition de l'objet me semble plus naturel: la définition d'une classe est ainsi complète, à l'intérieur d'un bloc de code, et c'est pour cela que j'ai abandonné la définition séparée des méthodes.

Ça me parait étrange à moi d'avoir des considérations d'ordre esthétique concernant l'informatique.
D'une façon générale, je reprends du code fait par d'autres dont la valeur a été consacrée par l'usage (surtout si je ne comprends pas tout).
Si souvent ma présentation diffère des autres, je ne change pas une virgule sur les aspects qui pourraient avoir de l'importance.

Mais vous êtes bien aventureux, M. JP. Smiley lol

Smiley smile
Zelena a écrit :
Bonjour.

Ça me parait étrange à moi d'avoir des considérations d'ordre esthétique concernant l'informatique.
D'une façon générale, je reprends du code fait par d'autres dont la valeur a été consacrée par l'usage (surtout si je ne comprends pas tout).
Si souvent ma présentation diffère des autres, je ne change pas une virgule sur les aspects qui pourraient avoir de l'importance.

Mais vous êtes bien aventureux, M. JP. Smiley lol

Smiley smile

Aventureux ??? Smiley biggrin
Je suis surtout un ingénieur informaticien de 75 ans qui a acquis certaines technos et philosophie de son métier.

La présentation n'est pas en soit le problème, ce qui est un problème c'est la maintenabilité du code.

Dans l'industrie, il arrive fréquemment que l'on ait à brule pourpoint à devoir assurer la maintenance (corrective, adaptative et évolutive) d''un programme de plusieurs milliers de lignes de code écrits par quelqu'un qui n'est plus là.
Dans ces circonstances, on apprécie que le code soit maintenable, ce qui veut dire en particulier qu'on en comprenne la logique à la lecture.
C'est là que la "présentation" joue un rôle.

J'ai eu à maintenir de tels programmes à une époque où la "programmation structurée" était une nouveauté. Je suppose que cela n'a pas beaucoup de sens pour les jeunes générations, ne serait-ce que parce que tous les langages de programmation actuels produisent systématiquement du code structuré.
Dans beaucoup de cas, il fallait aller à la pêche dans du code non maintenable pour assurer la maintenance corrective, et pour ce qui est de l'adaptation du code à de nouvelles contraintes et son évolution, il ne restait bien souvent pas d'autre choix que de tout réécrire.

Vous me direz que le code que je fais est maintenu par moi-même et que je n'ai pas à m'en soucier.
D'une part, je ne suis pas éternel, et à 75 ans plus une santé qui laisse à désirer, je dois envisager comme proche le moment où je ne serai plus là, ou plus en état de faire ce boulot.
D'autre part, je dois avoir en activité des dizaines ou même des centaines de milliers de ligne de code. Il n'est pas rare que je me gratte la tête quand je dois faire des modifications dans des programmes auxquels je n'ai pas touché depuis des années. C'est un peu comme si c'était un autre qui les avait écrits.

Comme tout le monde, j'ai fait des copier-coller de codes trouvés sur Internet, sans chercher trop à comprendre leur structure parce que j'étais pressé.
Je sais cependant que tant qu'on n'a pas compris ce que l'on fait, la maintenance devient très compliquée à faire. Il est donc très bien d'adopter le code des voisins (consentants) mais ça ne dispense pas de passer un peu de temps quand on en a pour comprendre comment ça marche...

Bon! j'arrête de radoter comme un vieux c...
Modérateur
Désolé pour la réponse tardive Smiley smile

a écrit :

Est-ce que quelque chose comme

function MonObjet() {
this.prototype.bidule = function() {
........
}
}
aurait un sens?

pas vraiment, car une instance n'a pas de prototype mais une fonction si. Donc si on instancie avec new il faudrait l'écrire ainsi:

function MonObjet() {
    this.constructor.prototype.bidule = function() {
        ........
    };
}

Mais cela pose divers problèmes:
- Si on souhaite faire de l'héritage, le prototype n'est renseigné que lors de la première instanciation
- Le prototype est écrasé par les même méthodes lors de chaque création d'instances.

J'utilise personnellement la notation suivant qui me permet, à défaut de tout grouper, de grouper les méthodes:

var Arbre = function(){
  // constructeur
};
Arbre.prototype = {
  pousser: function(){
    
  },
  couper: function(){
    
  },
};


Mais oui la syntaxe javascript pour la POO est juste dégueulasse. C'est pourquoi ES6 a introduit le sucre syntaxique suivant, utilisable si les navigateurs utilisés sont modernes et connus:


class Rectangle {
  constructor(hauteur, largeur) {
    this.hauteur = hauteur;
    this.largeur = largeur;
  }
 
  get area() {
    return this.calcArea();
  }

  calcArea() {
    return this.largeur * this.hauteur;
  }
}

const carré = new Rectangle(10, 10);

console.log(carré.area);

avec les extends, static, etc. Tout cela utilise bien les prototypes en arrière-plan mais permet de rédiger un code plus lisible.

Voir: https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Classes
Meilleure solution
Whouaou! Voilà une nouvelle qu'elle est bonne!

ENFIN DES VRAIES CLASSES EN JS !
Je ne serai donc pas mort avant que ce soit disponible, c'est génial!

Comme mes développements en cours n'ont pas besoin de tourner sur des navigateurs autres que Chrome et FireFox, je pense que je vais m'y mettre dès que possible.

Merci de cette excellente nouvelle! Smiley biggrin