Nothing is Impossible – Making the Switch from ES5 to ES6

Nothing is Impossible – Making the Switch from ES5 to ES6

I have been writing javascript for about a decade now. When I first saw ES6, it felt like JavaScript had been empowered with so many new functionalities, I just had to rewrite everything to ES6. Once I actually begun that process it was tricky in the existing code base and not possible to rewrite everything using pure ES6 functionalities. I took a step back and decided to start with a smaller subset of ES6 features – class, let/const, lexical scoping, and object. Here’s what I learned along the way:

I have been writing javascript for about a decade now. When I first saw ES6, it felt like JavaScript had been empowered with so many new functionalities, I just had to rewrite everything to ES6. Once I actually begun that process it was tricky in the existing code base and not possible to rewrite everything using pure ES6 functionalities. I took a step back and decided to start with a smaller subset of ES6 features – class, let/const, lexical scoping, and object. Here’s what I learned along the way:

Before getting started you need to prepare the development environment with tools to help write code is ES6, I used gulp (Grunt is also a good choice) to build files. Here’s some Gulp plugins you can install to get started with compiling ES6 to ES5 compatibility versions.

Here is a simple gulp file to help get started:

    
    var gulp = require('gulp');
    var babel = require('gulp-babel');
    var concat = require('gulp-concat');
    var eslint = require('gulp-eslint');
    //Register compilation task
    gulp.task('compile', function() {
        // place code for your default task here
        return gulp.src([
            'src/libs/file1.js',
            'src/libs/file2.js',
            'src/xxx.js',
            'src/yyyy.js'
        ])
        // eslint() attaches the lint output to the eslint property
        // of the file object so it can be used by other modules.
        .pipe(eslint({
            "useEslintrc": true
        }))
        //eslint.format() outputs the lint results to the console.
        // Alternatively use eslint.formatEach() (seeDocs).
        .pipe(eslint.format())
        .pipe(babel({
            "sourceMaps": false
        }))
        .pipe(concat('yourfilename-concat.js'))
        .pipe(gulp.dest('dist'));
    });
    //Register default task
    gulp.task('default', ['compile']);
    

Here are some of the things I learned during my rewriting process:

1. Class

Class was really easy to get started, constructor methods really stands out and it offers a much nicer, clearer syntax for creating objects and dealing with inheritance.

Here is a simple class example:

    
    class Meter {
        constructor(config) {
            this.count = config.count;
        }
        getCount() {
            return this.count;
        }
        static reload() {
            window.location.reload();
        }
    }
    

Interesting things for developers from ES5 world to note:

  1. We don’t have to write the function key word when starting new function
  2. No comma needed for adding a new function
  3. We can’t stick default properties to a class like we do with prototypical inheritance, you have to use this.xyz = ‘abc’ from the function to add a property to the instance.
  4. Static functions are not tied to an instance and you can not call it calling this.reload() instead you have to use a constructor to use this method Meter.reload().
  5. Class declarations are not hoisted

It takes some time to forget the practices that we have been following for years, but once you get hold of it, you will never want to go back again.

2. Variable declaration

Let and const are two new ways to declare variables and both of them are block scope.

2.1 Let

Let is similar to var but it is available to the current code block.

    
    function get() {
        if (condition) {
            let value = "xyz";
            return value;
        } else {
            //value does not exist here
            return null;
        }
    }
    

2.2 Const

    
    if (condition) {
        const MAX_COUNT = 10;
        //more code which uses variable
    }
    //MAX_COUNT is not accessible here
    

In the code above MAX_COUNT is declared within the if statement and MAX_COUNT is only available within the block. As soon as the block statement is finished executing MAX_COUNT is destroyed.

Some important facts to notice here:

  1. We must provide a value when we declare constant.
  2. If we try to assign a value to MAX_COUNT again, the line will throw error.

Interesting things for developers from ES5 world to note:

  1. Let and const are not hoisted to functions or block scope – they are not even hoisted within their own block scope.
  2. We must provide a value to the const variable and if you try to reassign a value to a const variable then it will throw an error.

3. Arrow function

This is one of my favorite feature of ES6, functions defined with a new syntax that uses an arrow(=>) and it works as below:

  • Lexical this binding – The value of this inside of the function is determined by where the arrow function is defined not where it is going to be used.
  • Can not be used with new keyword – it does not provide a new keyword
  • Scope remains constant – this value can not be changed inside the function, it maintains the same value throughout the life cycle of the function.
  • An arguments object is not available – you must pass named arguments.

Examples:

    
    var getName = () => "Hetal";
    // will be transpiled to
    var getName = function () {
        return "Hetal";
    };
    

Lexical this binding

This is the most common use case where we had to come up with storing scope in ES5 world and it has been made easy in ES6 with arrow function.

ES5 you would probably write some function like as below:

    
    var Badge = {
        userName: "Hetal",
        getBadge: function () {
            return "<div class='badge'>" + this. userName + "</div>";
        },
        init: function (type) {
            var self = this;
            //notice how difficult it is maintain scope here
            $(document).on("click", function () {
                self.getBadge.call(self, e);
            });
        }
    };
    

ES6 will make same code easier to write

    
    var Badge = {
        userName: "Hetal",
        getBadge: function () {
            return "<div class=‘badge’>" + this. userName + "</div>";
        },
        init: function (type) {
            //using lexical scope,
            //we can write same code in one line as below
            $(document).on(“click”, (e) => { this.getBadge(e)});
        }
    };
    

This is great way to reduce code lines and write clean code.

 

File Size comparison:

The transpiling process adds some extra code to make it compatible with all browsers which increases the size of the final output file. For instance the file (minified and concatenated versions) that I rewrote is 25KB in ES5 javascript but the ES6 version comes out to be 32KB.

To conclude, Initially I thought that it would be really difficult to convert my project into ES6 but once I started taking baby steps by picking some ES6 features, configuring workflow process using gulp, it all eventually fell in place. I really enjoyed rewriting my ES5 javascript project files into ES6 – which also helped me better organize and structure my codebase and opened my mind to a whole new world. I hope what I learned will help someone who has just started converting projects into ES6. Happy coding!

References:
https://leanpub.com/understandinges6/read

Written by Hetal Thakkar


Leave a Reply

Your email address will not be published. Required fields are marked *