ES6 classes: The hidden truths

ES6 classes: The hidden truths

For many developers, especially those coming from traditional languages such as Java, C#, PHP, etc. and those who have a great passion for OOP paradigm, ES6 may be a huge win as the most wanted feature in Javascript finally has been out: class. But are there only roses ?

Old wine in a new bolttle

We declare a class in ES6 like below:

class A {
}

When we inspect type of A

typeof A

It will print out function. If we type above code in Chrome’s console, it will print

function class A {
}

A is just a special function in that it isn’t callable (until this point in time). Trying to invoke A() will generate ReferenceError:

Uncaught ReferenceError: A is not defined

Apparently, nothing new is shipped with ES6.

Just convenient syntax

Let’s see what the Javascript engine will do when it deals with class

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

    getArea() {
        return this.height* this.width;
    }
    toString() {
        return `Rectangle: width(${this.width}), height(${this.height})`;
    }
    static create(height, width) {
        return new Rectangle(height, width);
    }
}

class Square extends Rectangle {
    constructor(height) {
        super(height, height);
    }
    toString() {
        return `Square: length(${this.height})`;
    }
}

const s1 = new Square(10);

Below is what the javascript engine will see:

Object diagrams

Apparently, nothing new with ES6 class.

It’s just syntactic sugar on top of prototype inheritance.

An apple vs. an orange

Let’s see what are the differences between ES6 class and OOP language class.

Concept

In traditional OOP languages, class is a blueprint / template from which instances are created.

In Javascript, class is just a constructor function.

Behavior

In traditional languages, when creating instances, methods, properties, etc. are copied down from parent classes to child classes and then from the class to new instances.
OOP behavior

While in Javascript, there is no such a copy from classes to classes and from classes to instances. There are just links between objects.
JS behavior

Features

class is at heart of OOP languages. It’s no strange that OOP languages support a lot of features such as: class variable scopes, multiple inheritances, static block, nested class..

In Javascript, class is just syntactic sugar on top of prototype inheritance. Features support in class is limited. Currently, only following are supported:

  • constructor
  • instance method
  • static method

Note that class property is not supported.

So, comparing Javascript class to OOP language class is like comparing an apple to an orange.

Advertisements

The hidden powers of NODE_ENV

The hidden powers of NODE_ENV

NODE_ENV is an environment variable popularized by the Express framework. It specifies the environment in which an application is running such as development, staging, production, testing, etc.

By default, our application will run in development environment. And we can change the environment by changing process.env.NODE_ENV. Let’s see how frameworks, libraries will behave in different environments.

development:

  • More logs come out
  • No views are cached
  • More verbose error messages are generated
  • Front end stuff like javascript, css, etc. aren’t minized and cached

production:

Below are common, regardless of frameworks:

  • Middleware and other dependencies switch to use efficient code path
  • Only packages in dependencies are installed. Dependencies in devDependencies and peerDependencies are ignored.
express.js
  • View templates are cached
  • Less verbose messages are generated
  • CSS files are cached
sailsjs
  • CSRF is enabled
  • Response may be compressed (see https://www.npmjs.com/package/compression)
  • Models’ migration settings are forced to migrate: ‘safe’. This is a failsafe to protect against inadvertently damaging our production data during deployment
  • Error messages and stack traces from res.serverError() will still be logged, but will not be sent in the response
mongoose

There’re more that I don’t list here. As you can see, setting NODE_ENV to production gives best performance. As this variable is so important and has become globally adopted in Node.js world, burn this “always setting NODE_ENV to production” into your mind.

Making Promises For a Better World

Do you think that we can make our world better by making and keeping our promises? If you agree, keep reading! Just kidding. Actually, in this post, we’re going to see how we can build better software by making promises in Node.js.

Node is built with callback design pattern in mind. Large part of its APIs accepts a callback function as a second parameter. Callbacks are very easy to grasp. They have become the default way of handling async data flows in JavaScript. But its simplicity comes at a price. While callback function is fine for simple cases, it may causes a lot of issues in complex cases:

  • Callback hell
  • Inconsistent behavior ( releasing Zalgo) )
  • Misbehaving callbacks
    • Callbacks may be called many times
    • Callbacks may never be called
    • Callbacks may be called too early

And, Promises came to save us from callbacks. But, in Node, how can we make Promises from the callback world? By promisifying!

Almost all Promise libraries support an extra API allowing us to make a Promise version from callback version:

What “making a promise” functions do is to take an asynchronous operation that conforms to error-first style (or Node style) and wrap that into a Promise. Note that “making a promise” functions can’t convert a synchronous operation into a Promise based operation.

In following examples, we’re going to use bluebird to make Promises. By default, bluebird append the suffix “Async” to the callback based operation name when creating a Promise based version: method1 –> method1Async

before

const fs = require('fs')

fs.readFile('./data.zip', (err, content) => {
  if (err) {
      return errorHandler(err)
  }
  console.log(content)
})

after

const Promise = require("bluebird")
const fs = Promise.promisifyAll(require("fs"))

fs.readFileAsync("./data.zip")
  .then(content => console.log(content))
  .catch(errorHandler)

before

import mysql from "mysql";
const pool = mysql.createPool({..});

module.exports.execute = function(statement, params) {
    pool.getConnection(function(e, connection) {
        if(e) {
            return errorHandler(e);
        } else {            
            connection.query(statement, (e, results, fields) =>{
                connection.release();
                if(e) {
                    return errorHandler(e);
                } else {
                    console.log(results);
                }
            });
        }
    });
}

after

import mysql from "mysql";
import Promise from "bluebird";

Promise.promisifyAll(require("mysql/lib/Connection").prototype);
Promise.promisifyAll(require("mysql/lib/Pool").prototype);

const pool = mysql.createPool(config.dbConnectionPool);

/**
 * API to execute a sql statement against the db.
**/
export function execute (sql, params) {
    return Promise.using(getConnection(), (conn)=>{
        return conn.queryAsync(sql, params);
    });
}

function getConnection(){
    return pool.getConnectionAsync().disposer((conn)=>{      
        conn.release();
    });
}

With Promises, our program is more clear by being closer to synchronous code, reducing nesting blocks and keeping track of less state.