JavaScript Conception et chaînage d'objets à chaînes


Exemple

Chaînage et chaînage est une méthodologie de conception utilisée pour concevoir des comportements d'objet de telle sorte que les appels à des fonctions d'objet renvoient des références à self ou à un autre objet, donnant accès à des appels de fonctions supplémentaires. l'objet / s.

Les objets pouvant être chaînés sont considérés comme étant chaînables. Si vous appelez un objet chaînable, vous devez vous assurer que tous les objets / primitives renvoyés sont du type correct. Il ne faut qu'une seule fois à votre objet chaînable pour ne pas renvoyer la référence correcte (facile à oublier pour ajouter return this ) et la personne utilisant votre API perdra confiance et évitera le chaînage. Les objets à chaînes doivent être tout ou rien (pas un objet chaînable même si les pièces sont). Un objet ne devrait pas être appelé chaînable si seulement certaines de ses fonctions le sont.

Objet conçu pour être chaîné

function Vec(x = 0, y = 0){
    this.x = x;
    this.y = y;
    // the new keyword implicitly implies the return type 
    // as this and thus is chainable by default.
}
Vec.prototype = {
    add : function(vec){
        this.x += vec.x;
        this.y += vec.y;
        return this; // return reference to self to allow chaining of function calls
    },
    scale : function(val){
        this.x *= val;
        this.y *= val;
        return this; //  return reference to self to allow chaining of function calls
    },
    log :function(val){
        console.log(this.x + ' : ' + this.y);
        return this;
    },
    clone : function(){
        return new Vec(this.x,this.y);
    }
}

Exemple d'enchaînement

var vec = new Vec();
vec.add({x:10,y:10})
    .add({x:10,y:10})
    .log()             // console output "20 : 20"
    .add({x:10,y:10})
    .scale(1/30)
    .log()             // console output "1 : 1"
    .clone()           // returns a new instance of the object
    .scale(2)          // from which you can continue chaining
    .log()

Ne crée pas d'ambiguïté dans le type de retour

Tous les appels de fonctions ne renvoient pas un type de chaînage utile, ni ne renvoient toujours une référence à self. C’est là que l’utilisation rationnelle du nommage est importante. Dans l'exemple ci-dessus, l'appel de fonction .clone() est sans ambiguïté. D'autres exemples sont .toString() implique qu'une chaîne est retournée.

Un exemple de nom de fonction ambigu dans un objet chaînable.

 // line object represents a line
 line.rotate(1)
    .vec();  // ambiguous you don't need to be looking up docs while writing.

 line.rotate(1)
    .asVec()    // unambiguous implies the return type is the line as a vec (vector)
    .add({x:10,y:10)
 // toVec is just as good as long as the programmer can use the naming 
 // to infer the return type

Convention de syntaxe

Il n'y a pas de syntaxe d'utilisation formelle lors du chaînage. La convention consiste à enchaîner les appels sur une seule ligne si elle est courte ou à enchaîner sur la nouvelle ligne en retrait d'un onglet de l'objet référencé avec le point sur la nouvelle ligne. L'utilisation du point-virgule est facultative mais aide à identifier clairement la fin de la chaîne.

  vec.scale(2).add({x:2,y:2}).log();  // for short chains

  vec.scale(2)     // or alternate syntax
      .add({x:2,y:2})
      .log();  // semicolon makes it clear the chain ends here

  // and sometimes though not necessary
  vec.scale(2)     
      .add({x:2,y:2})
      .clone()    // clone adds a new reference to the chain
           .log(); // indenting to signify the new reference

  // for chains in chains
  vec.scale(2)     
      .add({x:2,y:2})
      .add(vec1.add({x:2,y:2})  // a chain as an argument 
           .add({x:2,y:2})      // is indented
           .scale(2))
      .log();

  // or sometimes 
  vec.scale(2)     
      .add({x:2,y:2})
      .add(vec1.add({x:2,y:2})  // a chain as an argument 
           .add({x:2,y:2})      // is indented
           .scale(2)
      ).log();   // the argument list is closed on the new line

Une mauvaise syntaxe

   vec          // new line before the first function call
      .scale()  // can make it unclear what the intention is
      .log();

   vec.          // the dot on the end of the line
      scale(2).  // is very difficult to see in a mass of code
      scale(1/2); // and will likely frustrate as can easily be missed
                  // when trying to locate bugs

Côté gauche de la mission

Lorsque vous attribuez les résultats d'une chaîne, le dernier appel ou référence d'objet renvoyé est attribué.

 var vec2 = vec.scale(2)
                .add(x:1,y:10)
                .clone();   // the last returned result is assigned
                                // vec2 is a clone of vec after the scale and add

Dans l'exemple ci-dessus, la valeur renvoyée par vec2 provient du dernier appel de la chaîne. Dans ce cas, ce serait une copie de vec après l'échelle et l'ajouter.


Résumé

L'avantage de changer est de rendre le code plus facile à maintenir. Certaines personnes préfèrent cela et feront de la chaîne une exigence lors de la sélection d'une API. Il y a aussi un avantage en termes de performances car cela vous évite d'avoir à créer des variables pour contenir des résultats intermédiaires. Le dernier mot étant que les objets chaînables peuvent également être utilisés de manière conventionnelle, vous ne devez pas forcer le chaînage en rendant un objet chaînable.