Simplifying JavaScript Objects Inheritance in QCObjects Framework

Objects Inheritance

Objects Inheritance means that it is possible to inherit attributes and methods from one class to another. In JavaScript, a class is a representation of an object's prototype, and a prototype is a subset of definitions for the properties of an object.

Native Inheritance in JavaScript

Since the ECMAScript 2015 specification, JavaScript introduced a special keyword called "class" (lowercase), which can be used as a syntax sugar to describe a prototype of a function that represents a class or an object's type.

'use strict';

class Polygon {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
}

class Square extends Polygon {
  constructor(sideLength) {
    super(sideLength, sideLength);
  }
  get area() {
    return this.height * this.width;
  }
  set sideLength(newLength) {
    this.height = newLength;
    this.width = newLength;
  }
}

//this sample code was extracted from MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain#with_the_class_keyword

The above code describes a Polygon class and a Square class that can be instantiated by using the new operator like this:

 let polygon = new Polygon( 20, 30 );
 let square = new Square (30);

One class extends from the other, inheriting its methods. The extended class (Polygon) is commonly called parent class or abstract class and the extending class (Square) is commonly called subclass or child-class.

QCObjects Inheritance using the Class function

Now, a similar code but with QCObjects syntax that will produce a quite more advanced results, would look like this:

// QCObjects v2.3 code

Class ("Polygon", {
    height: 0,
    width: 0
})

Class ("Square", Polygon, {
   slideLength: 0,
   get area() {
     return this.height * this.width;
   }
   set sideLength(newLength) {
     this.height = newLength;
     this.width = newLength;
   }
})

// To instantiate this classes
let polygon = New(Polygon, {width: 20, height: 30})
let square = New(Square, {sideLength: 20})

One of the most important differences in the above code is that we don't need to declare a constructor (as we needed in the first native JavaScript code), because the framework is automatically declaring it for us.

Another important thing is that to instantiate the objects polygon and square we don't use the new operator. We need to replace it by a New function (beware of the capital letters in Class and New).

One killer advantage of this syntax is that you are defining the objects as objects, and you're not using any function syntax to declare the class definitions (the QCObjects framework internally does it for you when needed).

As simple as you see the Class and New features, these are the very source of the power of QCObjects, because transforming the way you can declare the objects you use, you can also enrich them in ways that you still can't imagine.

Overriding methods

You can also override methods in native JavaScript. In the next example, we will create a method called whoami that returns a string saying "I am a Polygon of x " or "I am a Square of x " showing the values of with and height inside the message respectively.

class Polygon {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }

  whoami () {
      return `I am a Polygon of ${this.width} x ${this.height}`;
  }

}

class Square extends Polygon {
  constructor(sideLength) {
    super(sideLength, sideLength);
  }
  get area() {
    return this.height * this.width;
  }
  set sideLength(newLength) {
    this.height = newLength;
    this.width = newLength;
  }

 // overridden method 
  whoami () {
      return `I am a Square of ${this.width} x ${this.height}`;
  }

}

let polygon = new Polygon(20, 30);
let square = new Square (20);

console.log ( polygon.whoami() );
// "I am a Polygon of 30 x 20"

console.log ( square.whoami() );
// "I am a Square of 20 x 20"

As you can see, the same method name can be overridden to have a different behaviour depending on where the method is defined (the subclass or the parent class).

Overriding methods in QCObjects

The same code described above can be simplified using QCObjects like this:

// QCObjects v2.3 code

Class ("Polygon", {
    height: 0,
    width: 0, 
    whoami () {
      return `I am a Polygon of ${this.width} x ${this.height}`;
    }
})

Class ("Square", Polygon, {
   slideLength: 0,
   get area() {
     return this.height * this.width;
   }
   set sideLength(newLength) {
     this.height = newLength;
     this.width = newLength;
   },
  whoami () {
     // overridden method 
      return `I am a Square of ${this.width} x ${this.height}`;
  }

})

// To instantiate this classes
let polygon = New(Polygon, {width: 20, height: 30})
let square = New(Square, {sideLength: 20})

And we're gonna get the very same result

console.log ( polygon.whoami() );
// "I am a Polygon of 30 x 20"

console.log ( square.whoami() );
// "I am a Square of 20 x 20"

QCObjects is meant to expand the JavaScript language in runtime to empower you with the features of modern software development without the need of using bundlers nor static transpilers. For that reason you can always mix the syntax extending definitions of the native JavaScript.

One of the cool things of QCObjects is that you can also create a new QCObjects Class Definition extending a traditional JavaScript class definition, like this:


// native language class definition

class Polygon {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }

  whoami () {
      return `I am a Polygon of ${this.width} x ${this.height}`;
  }

}

// QCObjects Class Definition Square can extend a Polygon 

Class ("Square", Polygon, {
   slideLength: 0,
   get area() {
     return this.height * this.width;
   }
   set sideLength(newLength) {
     this.height = newLength;
     this.width = newLength;
   },
  whoami () {
     // overridden method 
      return `I am a Square of ${this.width} x ${this.height}`;
  }

})

let polygon = new Polygon(20, 30);
let square = New(Square, {sideLength: 20})

console.log ( polygon.whoami() );
// "I am a Polygon of 30 x 20"

console.log ( square.whoami() );
// "I am a Square of 20 x 20"

However, while the above code is cool and also powerful sometimes, it is not recommended for common use unless you really need it.

Conclusion

The syntax of QCObjects Class Function is simple and powerful to declare class definitions. You can also extend JavaScript native classes to define new objects. You can override methods and inherit behaviours of a parent class.