es6-classes

Understanding ES6 classes

by

Diego Barahona

Today during a conversation with a friend about what exactly ES6 classes are, I found out that topic can be commonly misunderstood.

First of all, if JavaScript is your first language and you have no prior experience with more truly object-oriented languages with classes, instances, inheritance etc, there are numerous sites you can visit to dig into these topics. But, wait! Don’t do anything yet, it’s better if you know nothing about OOP classes…. for the moment.

If you are like me and have experience working with languages that commonly use classes and modules for code organization, then ES6 classes might not be what you think they are. Why? Because under the hood, Harmony classes are just a cleaner way of writing code with prototypal functionality, EcmaScript code. So, all those hacks you probably did when you tried to work with classes in JavaScript are essentially what you are doing when using the new class keyword in ES6.

Is this bad?

No, not at all. You just need to know how this works, how to use it, and the most important thing, that you’re not working with Java classes.

It is also good to reinforce, especially for all those haters that are saying that people messed JS up by including ‘useless’ classes, that these are just syntactic sugar for prototype features JS already has. Therefore, those negative ‘observations’ MAKE NO SENSE!

Let’s get back a little and forget we have this new class keyword, in order to review how prototype works.

Disclaimer: I used the Spanish words “casa” (house), casita (little house), perro (dog), perrito (puppy) for test variables instead of the boring foo/bar hello world. Don’t get confused by them. 🙂

So, let’s suppose we have the function Perro, and we want to attach some methods and properties to it such as number of legs and actions like walking and barking. We have three ways to do it:

Static Properties

A static property/method will only be available for the creating function and will not be included in the instance. This can work as a helper, a useful method for the function you’re creating, or properties that are not related with the instance at all.

function Perro () {};
Perro.definition = "The dog is man's best friend";

In this case, the definition will only be accessible by calling Perro.definition. No matter if you instantiate this function, it will not be transmitted to the instance. Therefore this is a good place to put methods and properties related to the factory function, but not the children.

Instance Properties

An instance property/method is the one that will be copied to all the instances of the function. They will be independent of the definition and other instances; this is your private area where you do what you exclusively want for your instance.
It can be defined in 2 ways, from inside an instance method using the this keyword, or from the instance name.

function Perro (props) {
  this.color = props.color;
}

var max = new Perro({color: 'brown'});
max.name = "Max";

In this example, “max” has its unique properties like color and name, and even when you create new instances of this function, max will still be brown. Easy right?

Prototype Properties

Prototypes are what make JavaScript fun and different…. Well, it’s just one among many things actually. The following example represents, in a nut shell, how we resolved the “lack-of-classes” situation and how we get inheritance to work.

function Perro () {};
Perro.prototype.bark = function () {};
Perro.prototype.walk = function () {};

// Instance the variables
var max = new Perro();
var toby = new Perro();

// Compare functions
max.bark === toby.bark; // true
max.walk === toby.walk; // true

max.hasOwnProperty("walk"); // false

max.walk = function () {};
max.walk === toby.walk; // false, max.walk is an instance property now
Object.getPrototypeOf(max).walk === toby.walk; // true
max.__proto__.walk === toby.walk; // true, but be careful

// Define properties in future, still work on instances
Perro.prototype.eat = function () {};
max.eat === toby.eat; // true

As you can see, prototype is very flexible. It’s not copied to the instances of the function; it’s just a reference. If you try to ask via the hasOwnProperty method about the existence of any prototypal property (or method) inside that instance, it will return false, because that property is not part of the instance, is part of its prototype.

You can, however, create instance properties with the same name of the prototyped property. This will not erase the prototypal property, but it will not be accessible in the same direct way. The instance property will be the direct access, nevertheless, you can still access the prototype using __proto__. Please notice I used Object.getPrototypeOf, this is the secure method in ES5 because the use of proto was normalized just until ES6, so you can get weird behaviors; see MDN –proto.

You can also define prototypal properties in the future, after creating the instances; since it’s just a reference to the __proto__, it will be accessible in all the instances already created from this class.

ES6 Classes

Ok, now going back to ES6 Harmony classes, we have a deeper understanding of how prototype works. You will be surprised of the lack of magic in Harmony classes since they follow the same prototypal behavior we just saw in the previous examples.

class Perro {
  constructor (props) {
    this.color = props.color
  }

  walk () {

  }

  static definition () {
    return "The dog is man's best friend";
  }
}

var max = new Perro({color: "brown"});
max.name = "Max";

var toby = new Perro({color: "white"});

max.walk === toby.walk; // true
Perro.definition(); // "The dog is man's best friend"

I expect, at this point, you are able to have a clearer view of how this works under the hood. Nevertheless, I have some extra info to provide.

Unfortunately, the class structure can only accept methods, not properties, so as you can see, I replaced the definition with a method because you can have property definition in the prototype in the class structure; take a look at this question.

Why not continue using prototype instead?

You can still do it, but using the class structure has some benefits. When using prototype if you want to do inheritance from another function, you’ll probably start doing hacks to clone the prototype of one function into another one, and that’s a complete mess, I don’t recommend it.

class Perro {
  constructor (props) {
    this.color = props.color;
  }

  bark () {
    alert("woof");
  }
}

class Perrito extends Perro {
  constructor (props) {
    super(props);
  }

  bark () {
    super();
    alert("wouf");
  }
}

var puppy = new Perrito({color: "brown"});
puppy.bark();

We have a nice feature that we used as a hack. super() will call the inherited method (constructor or any other polymorphic method).

I hope this brief explanation helps you create classes in Harmony with a better understanding of what you are doing. And hey! Remember never to use ES6 classes as Java classes; they are not the same, JavaScript is a completely different language that uses different design patterns. All these helpers are just that, helpers, not a must when coding/programming.

Comments

comments

Powered by Facebook Comments