A practical introduction to JS classes

Classes in JS are awesome. With ease I'm able to create, instantiate and update stuff in the browser using my own modular set of scoped sets of variables and functions. You would almost forget that classes in JS practically doesn't exist. What?! Yes..

So what are classes then?

Classes are a new way to write functional/prototyped objects in JS. A functional object is an object that is being created from a function according to a specific 'blueprint'. It simply returns an object literal. This is what this looks like in code

function Person(name) {
  return {
    name: name,
    hi: function() {
      console.log(this.name);
    }
  }
}

const person = Person('John Doe');
person.hi();

As you can see, this is a function that is returning an object containing variables and functions. The object created is often refered to as an instantiated object or instance. Since these functions are creating new object, I like to call these blueprint- or factory-functions. While we are still a few steps away from a class, in essence this is the same.

Functional syntax

To ease creation of these blueprint functions, the folks at JS have created something easier to write:

function Person(name) {
  this.name = name;
  this.hi = function() {
    console.log(this.name);
  }
}

const person = new Person('John Doe');
person.hi();

Why is this working? We are not returning anything! That's because of the keyword new before calling our function. This does a few interesting things, mainly being:

  • It creates a new (empty) JS object
  • Connects this from your function to this new object
  • Implicitly returns this (new object)

Class syntax

Now lets take a look at the new sugared class syntax

class Person {
  constructor(name) {
    this.name = name;
  }

  hi() {
    console.log(this.name);
  }
}

const person = new Person('John Doe');
person.hi();

So we've dropped the function syntax, but instead had to add a constructor function. This function is automatically called by the new keyword. Additionally we can define more functions right in the class scope. The class syntax adds a lot of clarity and structure in creating (modular) JS.

Let's poke these classes!

I got ya, class syntax, yes. So, thats it?

No, besides creating (modular) scopes with ease, they can also be extended. This means a class can build upon another class. But another class can also build upon that class. Which of the two? It doesn't matter! Consider the following:

  • Thing

    • Rock
    • Living Thing
    • Person

      • Superman
    • Cat

We can actually do some interesting things using classes and extends. Note that this is the full code example, which I'll break down below this section.

class Thing {
  constructor(name = 'unnamed') {
    this.name = name;
  }

  hi() {
    console.log(`The ${this.name} isn't responding`);
  }
}

class Rock extends Thing {
  constructor() {
    super('Rock');
  }
}

class LivingThing extends Thing {
  constructor(name) {
    super(name)
  }

  hi() {
    console.log(`You don't speak the same language as ${this.name}`);
  }
}

class Person extends LivingThing {
  constructor(firstName, lastName) {
    super(firstName);
    this.lastName = lastName;
  }

  hi() {
    super.hi();
    console.log(`Brrnn Aooe ${this.lastName}`);
  }
}

class Superman extends Person {
  constructor() {
    super('Bruce', 'Wayne')
  }

  hi() {
    console.log('Nanananananana');
    setTimeout(() => {
      alert('BATMAN!');
      console.log(`Okay, my name is ${this.name} ${this.lastName}`);
    }, 2000);
  }
}

class Cat extends LivingThing {
  hi() {
    console.log('purr');
  }
}

Before going in, try to fiddle with this code. Create new instances and let them say hi to you!

Super extendable (constructors)

Note the difference in constructors. When extending classes, there's a few things to consider. To actually use the parent class, you will have to call super. It will call the parent constructor, and you will have to provide input for the variables required for that constructor. In the constructor, you cannot use this before the super() call. Note that if you omit the constructor (as done with Cat), it will automatically use the parent/super constructor.

Super extendable (functions)

To make use of functions defined in the parent class, you have to call super.functionName() as done in Person. You don't have to call the super method and you're also free to decide on execution order. The limitations for using this are only for constructor functions.

hi() {
  super.hi();
  console.log(`Brrnn Aooe ${this.lastName}`);
}

Classware

Nice and all, but can't you just tell me how I use it and give me some code examples?

Yes I can! In this section I'll provide some practical code examples

API Calls

Setting up your XMLHttpRequests can be tedious. I use a wrapper like this, usage displayed in next example:

class API extends XMLHttpRequest {
  constructor(url) {
    super();
    this.url = url;
  }

  Call() {
    return new Promise((resolve, reject) => {
      let apiUrl = this.url;
      // This would be a nice place to add options/parse the url etc
      this.open('GET', apiUrl);
      this.addEventListener('load', () => {
        const data = this.Parse(this.response);
        resolve(data);
      });
      this.addEventListener('error', () => {
        reject('Oops!');
      });
      this.send();
    })
  }

  Parse(stringData) {
    let data;
    try {
      data = JSON.parse(stringData);
    } catch(err) {
      return err;
    }
    return data; // This line will never be executed if we already returned an error
  }
}

Form handling

Maybe it's just me, but I'm not a fan of the formdata object. I usually use something like this:

class Form {
  constructor(form) {
    this.DOM = form;
    this.DOM.addEventListener('submit', this.Handle);
  }

  Handle(e) {
    e.preventDefault();
    console.log('Handling!', this);
    // Nice place to refresh/fetch all form data
  }

  // Add a function that refreshes form data
}

class APIForm extends Form {
  constructor(form) {
    super(form);

    this.DOM.APIForm = this; // This way we can access our instance from the event handler
    this.api = new API(); // This is the api from previous example
  }

  Handle(e) {
    super.Handle(e);

    this.APIForm.api.Call()
      .then((data) => {
        console.log(data);
      })
      .catch((error) => {
        console.log(error);
      })
  }
}

Filter module

Note that the pattern applied on the form can also be used for other elements:

class Filter { // Contains all general sort/order/filtering functions
    constructor(container) {
        // Get or create DOM
    // Setup stuff
    }
    AddParam(type, key, label) {
    // This function should contain a switch for type
        this.CreateFilterOptions(key, label);
    }
    CreateFilterOptions(key, label) {
    // In this case, I create HTML with JS
    // Make sure to add an eventlistener form this.Sort()
    }
    Sort(e) {
        // Some way to sort
    }
}
class ProductFilter extends Filter { // Is actually used to link this to existing HTML
    constructor(container) {
        super(container);
      this.AddParam('sort', 'price', 'TEXT_SORT_PRICE');
        this.AddParam('sort', 'size', 'TEXT_SORT_SIZE');
    }
}

That's it?

Nope, this was just a practical introduction. A little list to get you going:

  • Getters/Setters (this.varName actually isn't valid)
  • Prototype (Actual pre ES6 syntax)
  • Static (Who needs instances?)
{
    "article": {
        "html": "<p>Classes in JS are awesome. With ease I'm able to create, instantiate and update stuff in the browser using my own modular set of scoped sets of variables and functions. You would almost forget that classes in JS practically doesn't exist. <strong>What?!</strong> Yes..</p>\n<h2>So what are classes then?</h2>\n<p>Classes are a new way to write functional/prototyped objects in JS. A functional object is an object that is being created from a function according to a specific 'blueprint'. It simply returns an object literal. This is what this looks like in code</p>\n<pre><code class=\"language-JS\">function Person(name) {\n  return {\n    name: name,\n    hi: function() {\n      console.log(this.name);\n    }\n  }\n}\n\nconst person = Person('John Doe');\nperson.hi();\n</code></pre>\n<p>As you can see, this is a <code>function</code> that is returning an object containing variables and functions. The object created is often refered to as an <code>instantiated object</code> or <code>instance</code>. Since these functions are creating new object, I like to call these <code>blueprint-</code> or <code>factory-</code>functions. While we are still a few steps away from a <code>class</code>, in essence this is the same.</p>\n<h3>Functional syntax</h3>\n<p>To ease creation of these blueprint functions, the folks at JS have created something easier to write:</p>\n<pre><code class=\"language-JS\">function Person(name) {\n  this.name = name;\n  this.hi = function() {\n    console.log(this.name);\n  }\n}\n\nconst person = new Person('John Doe');\nperson.hi();\n</code></pre>\n<p>Why is this working? We are not returning anything! That's because of the keyword <code>new</code> before calling our function. This does a <a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new\">few interesting things</a>, mainly being:</p>\n<ul>\n<li>It creates a new (empty) JS object</li>\n<li>Connects <code>this</code> from your function to this new object</li>\n<li>Implicitly returns <code>this</code> (new object)</li>\n</ul>\n<h3>Class syntax</h3>\n<p>Now lets take a look at the new <em>sugared</em> class syntax</p>\n<pre><code class=\"language-JS\">class Person {\n  constructor(name) {\n    this.name = name;\n  }\n\n  hi() {\n    console.log(this.name);\n  }\n}\n\nconst person = new Person('John Doe');\nperson.hi();\n</code></pre>\n<p>So we've dropped the function syntax, but instead had to add a <code>constructor</code> function. This function is automatically called by the <code>new</code> keyword. Additionally we can define more functions right in the class scope. The class syntax adds a lot of clarity and structure in creating (modular) JS.</p>\n<h2>Let's poke these classes!</h2>\n<p>I got ya, class syntax, yes. So, thats it?</p>\n<p>No, besides creating (modular) scopes with ease, they can also be extended. This means a class can build upon another class. But another class can also build upon that class. Which of the two? It doesn't matter! Consider the following:</p>\n<ul>\n<li>\n<p>Thing</p>\n<ul>\n<li>Rock</li>\n<li>Living Thing</li>\n<li>\n<p>Person</p>\n<ul>\n<li>Superman</li>\n</ul>\n</li>\n<li>Cat</li>\n</ul>\n</li>\n</ul>\n<p>We can actually do some interesting things using classes and extends. Note that this is the full code example, which I'll break down below this section.</p>\n<pre><code class=\"language-JS\">class Thing {\n  constructor(name = 'unnamed') {\n    this.name = name;\n  }\n\n  hi() {\n    console.log(`The ${this.name} isn't responding`);\n  }\n}\n\nclass Rock extends Thing {\n  constructor() {\n    super('Rock');\n  }\n}\n\nclass LivingThing extends Thing {\n  constructor(name) {\n    super(name)\n  }\n\n  hi() {\n    console.log(`You don't speak the same language as ${this.name}`);\n  }\n}\n\nclass Person extends LivingThing {\n  constructor(firstName, lastName) {\n    super(firstName);\n    this.lastName = lastName;\n  }\n\n  hi() {\n    super.hi();\n    console.log(`Brrnn Aooe ${this.lastName}`);\n  }\n}\n\nclass Superman extends Person {\n  constructor() {\n    super('Bruce', 'Wayne')\n  }\n\n  hi() {\n    console.log('Nanananananana');\n    setTimeout(() => {\n      alert('BATMAN!');\n      console.log(`Okay, my name is ${this.name} ${this.lastName}`);\n    }, 2000);\n  }\n}\n\nclass Cat extends LivingThing {\n  hi() {\n    console.log('purr');\n  }\n}\n</code></pre>\n<p>Before going in, try to fiddle with this code. Create new instances and let them say hi to you!</p>\n<h3>Super extendable (constructors)</h3>\n<p>Note the difference in constructors. When extending classes, there's a few things to consider. To actually use the parent class, you will have to call <code>super</code>. It will call the parent constructor, and you will have to provide input for the variables required for that constructor. In the constructor, you cannot use <code>this</code> before the <code>super()</code> call. Note that if you omit the constructor (as done with <code>Cat</code>), it will automatically use the parent/super constructor.</p>\n<h3>Super extendable (functions)</h3>\n<p>To make use of functions defined in the parent class, you have to call <code>super.functionName()</code> as done in <code>Person</code>.\nYou don't have to call the super method and you're also free to decide on execution order. The limitations for using <code>this</code> are only for constructor functions.</p>\n<pre><code class=\"language-JS\">hi() {\n  super.hi();\n  console.log(`Brrnn Aooe ${this.lastName}`);\n}\n</code></pre>\n<h2>Classware</h2>\n<p>Nice and all, but can't you just tell me how I use it and give me some code examples?</p>\n<p>Yes I can! In this section I'll provide some practical code examples</p>\n<h3>API Calls</h3>\n<p>Setting up your XMLHttpRequests can be tedious. I use a wrapper like this, usage displayed in next example:</p>\n<pre><code class=\"language-JS\">class API extends XMLHttpRequest {\n  constructor(url) {\n    super();\n    this.url = url;\n  }\n\n  Call() {\n    return new Promise((resolve, reject) => {\n      let apiUrl = this.url;\n      // This would be a nice place to add options/parse the url etc\n      this.open('GET', apiUrl);\n      this.addEventListener('load', () => {\n        const data = this.Parse(this.response);\n        resolve(data);\n      });\n      this.addEventListener('error', () => {\n        reject('Oops!');\n      });\n      this.send();\n    })\n  }\n\n  Parse(stringData) {\n    let data;\n    try {\n      data = JSON.parse(stringData);\n    } catch(err) {\n      return err;\n    }\n    return data; // This line will never be executed if we already returned an error\n  }\n}\n</code></pre>\n<h3>Form handling</h3>\n<p>Maybe it's just me, but I'm not a fan of the formdata object. I usually use something like this:</p>\n<pre><code class=\"language-JS\">class Form {\n  constructor(form) {\n    this.DOM = form;\n    this.DOM.addEventListener('submit', this.Handle);\n  }\n\n  Handle(e) {\n    e.preventDefault();\n    console.log('Handling!', this);\n    // Nice place to refresh/fetch all form data\n  }\n\n  // Add a function that refreshes form data\n}\n\nclass APIForm extends Form {\n  constructor(form) {\n    super(form);\n\n    this.DOM.APIForm = this; // This way we can access our instance from the event handler\n    this.api = new API(); // This is the api from previous example\n  }\n\n  Handle(e) {\n    super.Handle(e);\n\n    this.APIForm.api.Call()\n      .then((data) => {\n        console.log(data);\n      })\n      .catch((error) => {\n        console.log(error);\n      })\n  }\n}\n</code></pre>\n<h3>Filter module</h3>\n<p>Note that the pattern applied on the form can also be used for other elements:</p>\n<pre><code class=\"language-JS\">class Filter { // Contains all general sort/order/filtering functions\n    constructor(container) {\n        // Get or create DOM\n    // Setup stuff\n    }\n    AddParam(type, key, label) {\n    // This function should contain a switch for type\n        this.CreateFilterOptions(key, label);\n    }\n    CreateFilterOptions(key, label) {\n    // In this case, I create HTML with JS\n    // Make sure to add an eventlistener form this.Sort()\n    }\n    Sort(e) {\n        // Some way to sort\n    }\n}\nclass ProductFilter extends Filter { // Is actually used to link this to existing HTML\n    constructor(container) {\n        super(container);\n      this.AddParam('sort', 'price', 'TEXT_SORT_PRICE');\n        this.AddParam('sort', 'size', 'TEXT_SORT_SIZE');\n    }\n}\n</code></pre>\n<h2>That's it?</h2>\n<p>Nope, this was just a practical introduction. A little list to get you going:</p>\n<ul>\n<li>Getters/Setters (<code>this.varName</code> actually isn't valid)</li>\n<li>Prototype (Actual pre ES6 syntax)</li>\n<li>Static (Who needs instances?)</li>\n</ul>",
        "excerpt": "Classes in JS are awesome. With ease I'm able to create, instantiate and update stuff in the browser using my own modular set of scoped sets…",
        "frontmatter": {
            "title": "A practical introduction to JS classes",
            "description": null,
            "slug": "js-intro-classes",
            "date": "2019-03-25T23:56:49.967Z",
            "tags": [
                "js",
                "es6"
            ],
            "categories": [
                "howto"
            ]
        }
    }
}