Just JavaScript - ES2015 Class
Written by Ian Elliot   
Monday, 12 February 2018
Article Index
Just JavaScript - ES2015 Class
Extends & Super

One of the biggest criticisms of JavaScript by programmers more familiar with other languages is that it does not have classes. For the JavaScript enthusiast, on the other hand, this is one of its big attractions. So what do we make of the introduction of class in ES2015?

The good news, if you think that JavaScript is one of the more innovative languages in common use today, is that the class keyword hasn't added or changed anything. It is all pure syntactic sugar and once you know how it corresponds to ES5 or classic JavaScript's use of constructors and prototypes it all seems so much easier to understand.

This is an extract from the book Just JavaScript by Ian Elliot.

Buy Now: from your local Amazon

Just JavaScript 
An Idiomatic Approach

JustJavaScriptSmall

A Radical Look At JavaScript

 

Most books on JavaScript either compare it to the better known class based languages such as Java or C++ and even go on to show you how to make it look like the one of these.

Just JavaScript is an experiment in telling JavaScript's story "just as it is" without trying to apologise for its lack of class or some other feature. The broad features of the story are very clear but some of the small details may need working out along the way - hence the use of the term "experiment". Read on, but don't assume that you are just reading an account of Java, C++ or C# translated to JavaScript - you need to think about things in a new way. 

Just JavaScript is a radical look at the language without apologies.

Contents

  1. JavaScript – Essentially Different
  2. In The Beginning Was The Object
  3. Real World Objects 
  4. The Function Object
          Extract - The Function Object
          Extract - Function Object Self Reference
  5. The Object Expression
  6. Function Scope, Lifetime & Closure
    Extract Scope, Lifetime & Closure
    Extract Execution Context ***NEW!
  7. Parameters, Returns and Destructuring
         Extract - Parameters, and Destructuring
  8. How Functions Become Methods
  9. Object Construction
         Extract: - Object Factories
  10. The Prototype
         Extract - ES2015 Class and Extends
  11. Inheritance and Type
  12. The Search For Type
  13. Property Checking

Buy Now: from your local Amazon

Also by Ian Elliot 
JavaScript Async: Events, Callbacks, Promises and Async Await
Just jQuery: The Core UI 
Just jQuery: Events, Async & AJAX  

<ASIN:1871962579>

<ASIN:1871962560>

<ASIN:1871962501>

<ASIN:1871962528>

Class In ES2015

ES2015 introduced some additional syntax which makes JavaScript’s combination of constructor and prototype look a lot like class based inheritance. It is important to realize that these changes are syntactic and make use of the same mechanisms we have been looking at in earlier chapters.

In other words, ES2015 may have a class statement, but it still doesn’t have classes.

Let's have a look at the new syntax and see how it relates to the constructor and the prototype.

You can declare a class using:

class Point {
  constructor(x, y) {
   this.x = x;
   this.y = y;
   this.setxy = function (x, y) {
                  this.x = x;
                  this.y = y;
                };
   }
}

which can be regarded as a shorthand for:

function Point(x, y) {
  this.x = x;
  this.y = y;
  this.setxy = function (x, y) {
                  this.x = x;
                  this.y = y;
               };
}

The important differences are that the class statement is not hoisted and so has to occur before the first use of the constructor and you have to call the class Point function using new - it isn't optional. Also the body of the class statement is executed in strict mode. If you don’t provide a constructor then a default function is created, which returns the empty object {}.

As well as class declarations there are also class expressions:

var Point=class{
                constructor(x, y) { …

The only difference is that class expressions allow a class to be redefined where a class statement throws an exception if you try to redefine it.

Prototype Methods

Notice that each object constructed using Point has its own x, y and setxy function. Unless Point is going to be a singleton it doesn’t make good sense to define a new setxy function for every instance. Any functions which are defined in the class body but outside of the constructor are added to the prototype object and this is a convenient way of sharing code between all of the instances.

For example, if we move setxy outside of the constructor:

class Point {
             constructor(x, y) {
               this.x = x;
               this.y = y;
             };
             setxy(x, y) {
                          this.x = x;
                          this.y = y;
             };
}

then it is added to the prototype object. In this case the class definition is equivalent to :

function Point(x, y) {
                      this.x = x;
                      this.y = y;
                     }
Point.prototype.setxy = function (x, y) {
                            this.x = x;                                  this.y = y;
                        };

Methods defined in the constructor are own methods and those defined in the class body are prototype methods.

You can only define methods and not data properties as it is considered bad practice to place data into the prototype.

If you want to add data to the prototype then you have to return to the original way of doing it:

Point.prototype.z=0;

You can do this because the class creates a constructor function called Point which has a prototype property just like all Function objects.

The class syntax makes it so easy to define prototype methods that it is a standard way of defining methods even for a singleton.

The standard pattern is to always define methods in the class body and data in the constructor.

A common pattern is to use get and set in the class to allow access to data properties in the constructor:

class Point {
             constructor(x, y) {
               this.x = x;
               this.y = y;
             }
            get x() {
               return this.x;
            }
            set x(value) {
               this.x = value;
            }
}

In this case x and y are instance variables i.e. each instance gets its own x and y but the get and set functions are shared by all the instances via the prototype object.

Static Methods

You can also define static methods which are methods of the constructor. That is, a static method is a method of the Function Point and not of any object constructed by Point. Static methods don’t have access to the properties of the instances and they are generally called so that this is set to the constructor.

For example:

class Point {
             constructor(x, y) {
               this.x = x;
               this.y = y;
            }
            static display(x) {
               alert(x);
            }
}

This adds the function display to the Point Function object.

To use it you would write:

Point.display(10);

As display is a method of Point it has no access to x and y as defined in the constructor and realized in any of its instances. When display is called this is set to Point and in principle it could have access to any data properties defined on Point but there is no way to define such properties using static. You can however create properties on the Point Function object in the traditional way:

Point.z=10;

and then you could write:

class Point {
             constructor(x, y) {
                this.x = x;
                this.y = y;
             }
             static display() {
                alert(this.z);
             }
}

You need to be very clear that z is a property of Point and not of any instances that Point creates.

The main use of static methods are to provide utility function that work with the instances of the class. The standard example is a compare function:

class Point {
             constructor(x, y) {
                this.x = x;
                this.y = y;
            }
            static equal(p1,p2) {
                return p1.x==p2.x && p1.y==p2.y;
            }
}

which could be used as:

Point.equal(point1,point2);

where point1 and point2 are instances. Notice that there is no special relationship between the static method and what is passed to it e.g. p1 and p2 could be anything not just instances of Point.

 



Last Updated ( Tuesday, 27 February 2018 )