JavaScript Design e catena concatenati dell'oggetto


Esempio

Chaining and Chainable è una metodologia di progettazione utilizzata per progettare i comportamenti degli oggetti in modo che le chiamate alle funzioni oggetto restituiscano riferimenti a self o a un altro oggetto, fornendo accesso a ulteriori chiamate di funzione che consentono all'istruzione chiamante di concatenare più chiamate senza la necessità di fare riferimento alla variabile che tiene l'oggetto / i

Si dice che gli oggetti che possono essere incatenati siano concatenabili. Se si chiama un oggetto chainable, è necessario assicurarsi che tutti gli oggetti / primitive restituiti siano del tipo corretto. Ci vuole solo una volta affinché il tuo oggetto concatenabile non restituisca il riferimento corretto (è facile dimenticare di aggiungere il return this ) e la persona che utilizza la tua API perderà fiducia ed eviterà di incatenare. Gli oggetti concatenabili dovrebbero essere tutto o niente (non un oggetto concatenabile anche se le parti lo sono). Un oggetto non dovrebbe essere chiamato concatenabile se solo alcune delle sue funzioni sono.

Oggetto progettato per essere concatenabile

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);
    }
}

Esempio di concatenamento

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()

Non creare ambiguità nel tipo di reso

Non tutte le chiamate di funzione restituiscono un tipo concatenabile utile, né restituiscono sempre un riferimento a se stessi. È qui che l'uso del buon senso del naming è importante. Nell'esempio sopra la chiamata di funzione .clone() non è ambigua. Altri esempi sono .toString() implica che viene restituita una stringa.

Un esempio di nome di una funzione ambigua in un oggetto concatenabile.

 // 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

Convenzione di sintassi

Non esiste una sintassi di utilizzo formale durante il concatenamento. La convenzione è quella di concatenare le chiamate su una singola riga se breve o concatenare sulla nuova riga rientrata di una scheda dall'oggetto referenziato con il punto sulla nuova riga. L'uso del punto e virgola è facoltativo, ma aiuta indicando chiaramente la fine della catena.

  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

Una cattiva sintassi

   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

Lato sinistro del compito

Quando si assegnano i risultati di una catena, viene assegnata l'ultima chiamata di ritorno o il riferimento a un oggetto.

 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

Nell'esempio sopra vec2 viene assegnato il valore restituito dall'ultima chiamata nella catena. In questo caso, sarebbe una copia di vec dopo la scala e aggiungere.


Sommario

Il vantaggio di cambiare è più chiaro codice più gestibile. Alcune persone lo preferiscono e rendono concatenabile un requisito quando selezionano un'API. Vi è anche un vantaggio in termini di prestazioni poiché consente di evitare di dover creare variabili per conservare risultati intermedi. Con l'ultima parola, gli oggetti concatenabili possono essere usati in modo convenzionale così da non forzare il concatenamento rendendo un oggetto concatenabile.