ES6 — Let’s divide our phones into Classes

Prototype inheritance for a smartphone from a cellphone shouldn’t be complicated to write, should it? 👯

JavaScript is strictly a prototype-based language and treats everything under one form: Object.
OK. So what why do we need Classes in ES6? Are they similar to the Classes in other language like Java, C#, etc? Does it change JavaScript’s core from prototype-based to class-based?

Well, let us find out.


First of all, what is “class-based” and “prototype-based” language?

Class-based VS Prototype-based

Class-based language

  • Objects are built based on class, which acts as blueprint/template — definition and description of an object.
  • All objects are created on run-time using syntax new as instances of certain classes, and classes need to be pre-defined explicitly.
  • Inheritance here is performed between classes, aka class inherits directly from another class(es).

Prototype-based language (JavaScript)

  • In JavaScript, there is only one thing matters: objects — which means:
“What matters about an object is what it can do, not what it is descended from.”

Hence JavaScript doesn’t do ‘casting’, aka there is no template as ‘class’ and object is not instantiate from a class. Class is not needed, and objects can inherit directly from other objects.

  • In addition, each object automatically has a private/hidden property [[Prototype]] — reference to null or to another object instance which is called a prototype. On newly created object, its prototype is initialized as object which is { constructor: this }
  • JavaScript uses “differential inheritance” model, which is performed with the help of prototype chain — child object will not need to duplicate all properties and methods of its parent, instead it will have “hidden link” referred back to its parent object as a prototype. For example:

Any access request to inherited properties will be delegated to its parent’s properties respectively, which means Object.hasOwnProperty() for inherited properties on the child object will return false. In the example above, we can call dog.beCute() but the property beCute doesn’t not exist in dog, only in its prototype — aka its parent.

Note: __proto__ is an accessor property (a getter function and a setter function) that exposes the internal [[Prototype]]. It is strongly not recommended to directly access/modify this.

Got the general idea? Great. Now let’s understand how we “define” and “implement” class-like object types in JavaScript before and after ES6.

Before ES6 with Function

Declaring an object type

In general, “mimic”-ing traditional Object Oriented class-based coding style like in Java, C#, etc… can be achieved in JavaScript by taking advantage of “functional object”.

For instance, below is how you declare an object type named CellPhone that can be initialized with basic functionalities: charge, call and sms.

As you can see, all methods are declared in CellPhone.prototype but not to CellPhone itself to make sure that any object instance created/inherited from CellPhone will have direct access to these methods (without using the word prototype) .

Any method declared directly to CellPhone will be considered as “static” method, which means it exists as local property of CellPhone only and won’t be available to its instances.

For example, if we add static method showTime directly to CellPhone

Then

var myPhone = new CellPhone();
myPhone.charge();
myPhone.call({name: 'Maya', phoneNum: 12345}); //"Calling Maya"
myPhone.showTime(); //TypeError: myPhone.showTime is not a function
CellPhone.charge(); //TypeError: CellPhone.charge is not a function
CellPhone.showTime(); //Display the correct time.

Inheritance

As i mentioned earlier, JavaScript is prototype-based language and each prototype is actually an independent object, hence in order to create proper inheritance, we will need the help of Object.create().

Explanation: Object.create() is meant to create new object with specified prototype and properties.

And therefore:

let iPhone =  new SmartPhone('iPhone');
iPhone.charge();
console.log(iPhone.battery); //1
iPhone.call({name: 'Pikachu', phone: 007}); //Calling Pikachu

Note: However, this coding style is definitely NOT recommended, for various reasons, one of them is performance — CellPhone is actually an object itself and created during run-time, unlike in other languages — another is code readability. We will discuss more about this in different article 😄.

So how does the chaining look like? Here is a glimpse of objects created in our example:

Since we know this coding style is not idealistic, what’s about Classes? Can they help us write better code? Let’s see…

After ES6 with Classes

What are Classes?

First, in JavaScript, Classes are no different from Functions. They are ‘special’ functions, by definition. Hence:

class CellPhone{}
console.log(typeof CellPhone === "function"); //true

As a result, Classes have similar components like Functions, which are:

Classes declaration

Back to our example — CellPhone, syntax for declaring object type now is similar but simpler than before:

//class <Name of Object> {}
class CellPhone{}

And CellPhone can be declared in the following form:

As shown, there are several small improvements here:

  • No need for comma keyword , in-between properties or methods like in Object declaration syntax.
  • More organized code, with a proper constructor to indicate how CellPhone should be created and initialized.
  • No more CellPhone.prototype. syntax to define its prototype properties and methods. Less typing, less typo mistakes 😄!
  • Direct properties of class can only be created/initialized inside constructor. Constructor acts as the class definition and handles all the dirty work of setting up the prototype chain properly.
class CellPhone{
this.battery = 0; //SyntaxError
}
function CellPhone(){
this.battery = 0; //OK
}

What about static methods?

Good news, ES6 actually provides (finally) the keyword static to enhance readability (thank God!) for us developers. Thereby we can define static methods directly in class declaration just like other prototype methods following the syntax:

Clearer, isn’t it? Now you don’t need to try to remember which method is static, and which is not. It’s finally getting simpler 💃!

Classes expression

Similar to Function expression, class expression can be named or unnamed. And the name given stays local to the class’s body, which means:

let cellPhone = class{
constructor(){}
},
cellPhone2 = class randomPhone{
constructor(){ this.battery = 0; }
};
console.log(cellPhone.name); //cellPhone
console.log(cellPhone2.name); //randomPhone

In case class name is not given, the callprototype.name will retrieve the name of the instance/variable assigned to that unnamed class. Otherwise, it will always take given class name.

So far so good? Awesome.

Next, how do Classes handle inheritance, will Classes help us write better code?

Inheritance

“Extends” keyword

Familiar with Java? If so, you probably are familiar with the keyword extends used for inheritance there. Similarly, ES6 brings us extends as more proper and readable way to implement inheritance, instead of using Object.create()

How do we use it? By the syntax:

class <sub-class name> extends <class name>{}

In our example, to create sub-class SmartPhone inherits from CellPhone will be done as followed:

class SmartPhone extends CellPhone{}

How about accessing parent class? For that we have

Super-class

In addition, ES6 introduces another keyword super which referred to parent class (super-class) and is used to call corresponding methods of parent class inside subclass declaration.

By definition:

The prototype of a subclass is the super-class.

Important note:

  • this is originally uninitialized in subclass constructor. Therefore, if constructor presents in subclass, before accessing any local property for that sub-class via this , constructor of super-class super() MUST be called to initialize this.
  • super() can only be called ONCE, ReferenceError will be thrown otherwise.
  • If the constructor explicitly returns an object, it will be used as the result instance, and it doesn’t matter whether this is initialized anymore.

Hence the following code is OK:

class SmartPhone extends CellPhone{
constructor(brand){
super(); //Call CellPhone's constructor
this.brand = brand;
}
...
}

But this is not OK

class SmartPhone extends CellPhone{
constructor(brand){
this.brand = brand;
}
...
}
//ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor

Certainly any parent’s method can be accessed using super.<method> syntax:

class SmartPhone extends CellPhone{
constructor(brand){..}
call(person){
if (person.hasVideo){
super.call(person); //Trigger call() of super-class
}
else{
this.callVideo(person);
}
}
}

Here we override the call method inherited from super-class CellPhone and call the original call method of CellPhone inside SmartPhone ‘s new call method.

Now the code can be re-written as:

So far so good? Indeed.

Nevertheless, here come the next questions: What are the benefits of Classes? Is there any limitation for using Classes over Functions? Does the appearance of Classes change the core concept of JavaScript from prototype-based to class-based?

Benefits

Classes clearly bring us quite a few benefits, which includes

  • Easier way for new developer or class-based language developers to keep up with the code base.
  • Built-in constructors are subclassable.
  • External library for inheritance is not needed anymore and code becomes more portable and easier to maintain between frameworks.
  • Creating a foundation for advanced features in the future such as mixins, immutable instances, etc.
  • Code system organizing and readability (very important for me 😅!)

Certainly, nothing comes with only benefits, let’s look at Classes’ limitation, shall we?

Warnings / Limitations

  • As stated before, Classes are just “special functions”, thus basically a class is type of Function regardless. And Function in JavaScript is Object. Therefore, it did not and will not change the core concept of JavaScript: JavaScript remains strictly prototype-based language.
  • Classes, as same as many other cool features of ES6, are just another syntactical sugar for prototypes. It aims to help developers to write lesser, cleaner and more organized code, thereby improving code readability (especially for Java developers who have to dig into JavaScript code 😆). After all, who would want to repeat typing “prototype” for more than 3 times (certainly not me)?
  • There is no “hoisting” applied for Classes, unlike Functions, hence:
let phone = new CellPhone();
class CellPhone(){...} //ReferenceError: CellPhone is not defined
let  phone2 = new CellPhone2();
function CellPhone2(){...} //OK
  • Class can only be instantiated by using keyword new
let samplePhone = CellPhone();//TypeError: Class constructor CellPhone cannot be invoked without 'new'

while with function call it is just simply initialize undefined unless function call returns an object.

function CellPhone(){this.battery = 0;}
let samplePhone = CellPhone(); //No error
console.log(samplePhone); //undefined

Conclusion

In general, I strongly recommend using Classes over Function in case you want to apply class-based object oriented code style in your system. After all, both of them are the same at core, why not choose new feature that obviously gives you a better control over coding standards?

But again (as usual), if you don’t have to, don’t overuse Classes. Being too organized when not needed is bad. Think about each class you define will actually be instantiated as an object in JavaScript, not staying as a template like in other language.

Keep in mind, JavaScript is not class-based but prototype-based, hence KISS always rules 😘!

How about you, what do you think about Classes? I’d love to hear your thought in the comments below 😺.


More on ES6:

More on Data Structures:

If you like this post, don’t forget to give me a 👏 below ⏬️ . It will surely motivate me a lot 😊

If you love to read more from me, feel free to check out my articles.

If you’d like to catch up with me sometimes, follow me on Twitter | Facebook or simply visit my portfolio website.