How to create class methods that allow function chaining

I was creating a Matrix class so that I can create matrices and do matrix operations.

I know there are libraries out there but I thought it would be interesting and wanted to learn to do it on my own. And, indeed, I came across a very interesting question that I have never thought about despite seeing it many times.

How are class methods created that allow for chaining?

In effect, how does one create a function that takes no arguments except that object it is chained to?

I.e., what I wnt to achieve are class methods that allow the following pattern: classInstance.firstFunction().secondFunction().thirdFunction()

To further illustrate what I mean:

//first I create a type to illustrate that we
//are working with multi-dimensional arrays.
//(Note, circular reference is allowed in TypeScript)
type matrix = Array<number | matrix>; 

//These instances would be
//created by my custom Matrix class:
let xMatrix = new Matrix(x: matrix);
let yMatrix = new Matrix(y: matrix);

//Then I would like my class to have methods that allow me
//to "chain" opertions on my new Matrix instances, like so:
let xTranspose = xMatrix.transpose();
let xTransposeX = xTranspose.multiply(xMatrix);
let xTransposeY = xTranspose.multiply(yMatrix);

Does anyone know how to do this or can point me to the right part of the JavaScript curriculum?

Thank you so much for this amazing forum. Easily the best forum on the web!

Greetings,
Is this what you are looking for?

Thank you @kravmaguy

I actually wasn’t sure it was called “chaining” but perhaps I would have found the same page had I just searched for “function chaining” on Google.

I was able to achieve as below. However, I am not sure how I can move the Matrix.prototype.inverse inside the class definition itself.

type matrix = Array<number | matrix>; 

class Matrix {
  private matrix: matrix;
  constructor(matrix: matrix) {
    this.matrix = matrix;
  }

  public inverse() {
    return null;
  }
}

Matrix.prototype.inverse = function() {
  console.log("Matrix was inversed!");
  return null;
}

let myMatrix = new Matrix([1,2,3]);
myMatrix.inverse();

It’s normally called the builder patter, also called “fluent interface” or “fluent API”. The methods need to return this, whatever this is, that’s how it works.

  1. you wrap the underlying thing you’re working with in an object
  2. the methods work on that thing, but return this, which is the overall object
  3. you provide a method that grabs and returns the underlying thing which a user can use to extract what they want.

In this case the thing I’m working with is an array of arrays, the object I’m using to wrap it has the type Matrix. The method I’m using to grab the unrlying array of arrays is called toArray.

class Matrix {
  #matrix: Matrix;

  constructor (init = [[]]) {
    this.#matrix = init;
  }

  get rows() {
    return this.#matrix.length;
  }

  get cols() {
    return this.#matrix[0].length
  }

  toArray() {
    return this.#matrix;
  }

  invert() {
    // Do the inversion 
    return this;
  }

  add(matrix) {
    // do logic for adding current matrix
    // to another matrix
    return this
  }
}

so like

new Matrix().invert().add(matrix2).toArray()

Note that, although it kinda looks neat, it’s normally a lot more trouble that it’s worth

1 Like

Thank you @DanCouper !

What does the # signify? It is shortcut for private variables introduced by ECMA?

Is the this-keyword always referring to the instance of the object? Basically, if I return this will it return the same instance I am working on?

Note that, although it kinda looks neat, it’s normally a lot more trouble that it’s worth

It looks neat. What is the downside of this pattern?

Class fields are public by default, but private class members can be created by using a hash # prefix.

there have been many resources written on the subject. heres a very comprehensive one:

https://pepa.holla.cz/wp-content/uploads/2016/08/You-Don-t-Know-JS-this-Object-Prototypes.pdf

from the article I mentioned:

this keyword in JavaScript refers to the current object in which it is called. Thus, when a method returns this , it simply returns an instance of the object in which it is returned. Since the returned value is an instance of an object, it is, therefore, possible to call another method of an object to the returned value, which is its instance. And this makes method chaining possible in JavaScript.

and as dan showed as well.

what you wrote isnt javascript but you can also add/override the prototype method outside of the class, you can extend with a subclass and override as well.

It’s not a shortcut, it is the syntax for private class fields as @kravmaguy says

This

private matrix

Is Typescript-specific syntax and doesn’t really create private class fields, produces different output, was added early on to Typescript before JS classes were really a thing, and long before private class fields were added to JS

You’ve got to very carefully write a lot of extra code to make an API that looks “neat”.

It’s called the builder pattern because in that case what you are doing is building an object bit by bit that presumably has many different options. And it may make sense to do that, may make it much easier to use. So as a contrived example:

class UserBuilder() {
  #firstName = undefined;
  #secondName = undefined;
  #age = undefined;

  addFirstName(name) {
    this.#firstName = name
    return this;
  }

  addSecondName(name) {
    this.#secondName = name
    return this;
  }

  addAge(age) {
    this.#age = age
    return this;
  }

  build() {
    return {
      firstName: this.#firstName,
      secondName: this.#secondName,
      age: this.#age,
    }
  }
}
user = new UserBuilder()
  .addFirstName("Riddley")
  .addSecondName("Walker")
  .addAge(15)
  .build()
class Matrix {
  inverse() {
    // Logic for inverting
    return this;
  }
}

Is exactly the same as

class Matrix {}

Matrix.prototype.inverse = function () {
  // Logic for inverting
  return this;
}
1 Like

@DanCouper @kravmaguy

Thank you both, and I apologize for the late reply. Had a serious bout with Covid and spent the past two months in the hospital intensive care unit. Hope to get back to coding as my health eventually recovers.

/javascript-scholar

2 Likes

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.