Tree shaking — How to write code and reduce the bundle size in Webpack 4+

 

Tree shaking has become more popular during recent days and hence now its becomes a vital responsibility for a bundler to support tree shaking.

Okay why it is much more important?. The answer is below,

No user will wait for more than 3 seconds for a page to load.

So its important to keep the bundle size as small as possible for a web page to load fast. Webpack and Rollup are the two popular bundlers which supports tree shaking.

In this article, I am going to speak about some guidelines to write code that support tree shaking and also how to reduce bundle size in Webpack.

Write or compile code to ES6

Since tree shaking works by marking the export and import statements as unused, it is better to write or compile your code to ES6 format. Unused imports will be removed during minification process.

If your browsers does not support ES6 codes then there are many transpiler available to do the conversion (For example: Babel).

Use Feature Module (Loose coupling)

Segregate features into separate modules. Know your customer before planning your web site or library.

All users are not going to use all of your library features, so its always a good practice to segregate the features into separate modules and allowing the user to inject when needed.

The feature modules should be loosely coupled with main module and also ensure to avoid explicit usage of the feature module classes or functions inside main module. Create a pub-sub pattern for your website/library to perform communication between modules.

Learn to use functional programming paradigm

Well, writing code using functional programming paradigm allows you to easily maintain and deliver a bug free product. Below are some of the things, you need to know when using function programming.

  • Write pure class/functions.
  • Use array methods such as filter, some, map etc.
  • Avoid using shared state.
  • Avoid mutating state.

You can find a complete explanation about Function Programming in the Master the JavaScript Interview: What is Functional Programming? drafted by Eric Elliott.

Set sideEffects — Reduce bundle size in Webpack

 

When you have followed the above sections in your code then you are halfway away from enabling proper tree shaking capability for your code.

The last thing, I need to share is the use of settings sideEffects keyword as false in your package.json file. Settings this will inform the webpack that your code/project is safe to exclude the re-exported classes/functions.

Please go through the webpack official documentation for more information on marking your project with sideEffects key.

Conclusion

In this article, we have seen how to use write code that can be tree shaken properly and setting sideEffects keyword in package.json to mark the project as safe to exclude the re-exported codes.

Happy coding.. 🙂

Advertisements

Parameter handling in ES6

ECMA Script 6 provides the following extension support for the parameters of JavaScript methods.

  1. Default parameter values
  2. Rest parameter
  3. Spread operator

Default parameter values:

Example with ES5 & ES6


        //[ES5]

        function sum(a, b) {
            if (!b)
                b = 2;
            return a + b;
        }
        var s = sum(5);
        console.log(s);//Result will be 7 as undefined variable 'b' assigned with value 2

In general we may pass lesser number of parameters instead of total capacity of parameters to the JavaScript methods. In this case the remaining parameters will be undefined.

This “undefined” scenario can be avoided in ES6 by initializing default values for function parameters. Have a look at below code snippets.


        //[ES6]

        function sum(a, b = 10) {
            return a + b;
        }
        var s1 = sum(5);
        console.log(s1); //Result will be 15 due to default value
        var s2 = sum(5, 15);
        console.log(s2); //Result will be 20 due to passed value (Override default value of  “b”)

Note: The default values of function parameter will be overridden when we pass values to those parameters.

Rest parameter:

In ECMA Script 5, while we are calling the JavaScript functions with number of parameters that is greater than the actual parameters count of that method, it will ignore the “Rest” parameters and receive the actual parameters alone. So in this case we will use the “arguments” scope to get those “Rest” parameter values.


        //[ES5]

        function sum(a, b) {
            return a + b + arguments[2] + arguments[3];
        }
        var s = sum(10, 30, 40, 20);
        console.log(s); //Result is 100

But in ES6, there is “Rest Parameter” (…parameterName) support for receiving those remaining parameter values. Have a look at the below code snippets.


        //[ES6]

        function sum(a, b, ...arg) {
            return a + b + arg[0] + arg[1];
        }
        var s = sum(10, 30, 40, 20, 50, 60);
        console.log(s); //Result is 100

Note: Rest parameter must be last formal parameter

Spread operator:

The Spread operator support (…arrayName) in ES6 is enabled to iterate over the array values or string values and also to generate array function parameters.

Examples: (ES 5 vs ES 6)

        //ES5

        var arr1 = [4, 5, 6], arr2 = [1, 2, 3].concat(arr1);
        console.log(arr2); // [1, 2, 3, 4, 5, 6]

        var str = "HTML".split("");
        console.log(str); // ["H", "T", "M", "L"]

        //ES6

        var arr1 = [4,5,6], arr2 = [1,2,3, ...arr1];
        console.log(arr2); // [1, 2, 3, 4, 5, 6]

        var str = "HTML";
        console.log([...str]); // ["H", "T", "M", "L"]

Spread operator with function parameter:

In ES 5 array values can be spread to each parameter of the function with the help of “.apply()” predefined method like below code snippets.

Example:

        function sum(a, b, c, d) {
            return a + b + c + d;
        }
        var s = sum.apply("", [5, 10, 15, 20]);
        console.log(s); // Result is 50

But in ES6 Spread operator can be used for spreading the parameter values to the functions. We can pass the parameter values in the following ways.

Example 1:

        function sum(a, b, c, d) {
            return a + b + c + d;
        }
        var arr = [5, 10, 15, 20],
          s = sum(...arr);
        console.log(s); // Result is 50
Example 2:

        function sum(a, ...arg) {
            return a + arg[0] + arg[1] + arg[2];
        }
        var arr = [10, 15, 20],
          s = sum(5, ...arr);
        console.log(s); // Result is 50

Note: Any type of parameter value collection can be passed to the “Rest parameters”.

Arrow function (Lambda expression) in Brief

What is Arrow function?

Arrow function expression defines the succinct syntax for writing the function expressions in JavaScript, which has been introduced in the ES6. It will not bind its own this, while using it within the functions and it has in-built return type attribute.

Syntax:

(parameters) => {stuffs}

Example:

Following is the simple demonstration about the usage of Arrow functions to make the code more compact.

var marks = [68, 83, 72, 56, 88];
var sum1 = marks.reduce(function (total, mark){
    return total + mark;
});
console.log(sum1);// Output: 367

var sum2 = marks.reduce((total, mark) => {
    return total + mark;
});
console.log(sum2);// Output: 367

var sum3 = marks.reduce((total, mark) => total + mark);
console.log(sum3);// Output: 367

Fiddle Demo

Advantage of Arrow function:

Example with JavaScript object constructor

In the following example the student () constructor has setTimeout () method for updating the “totalMark” attribute of “student ()”. Here the “this” defined by setTimeout () method is refers to the Window object. Hence it will not match with the “this” defined by the student () constructor. So the “totalMark” attribute of student () constructor cannot be changed in setTimeout () method as expected and it results in TypeError.

var s = new student();
function student() {
    this.student1 = { "name": "Smith", "totalMark": 375 };
    //To change the "Total Mark"
    setTimeout(function () {
        var newTotal = 395;
        if (this.student1.name == "Smith")//Uncaught TypeError: Cannot read property 'name' of undefined
            this.student1.totalMark = newTotal;
    }, 500);
}

To overcome this issue in ES6, Arrow function expression can be used with that setTimeout () method as stated in the following code snippets.

var s = new student();
function student() {
    this.student1 = {"name":"Smith", "totalMark":375};
    //To change the "Total Mark"
    setTimeout(() => {
        var newTotal = 395;
        if (this.student1.name == "Smith")
            this.student1.totalMark = newTotal;
        console.log(this.student1);//{name:"Smith",totalMark:395}
    }, 500);
}

 

Disadvantage of Arrow function:

In the following example, while using the Arrow function inside the marks () function prototype for total () method, it will change the context of “this” to Window object. Hence it leads to unexpected output or error.

var marks = function() {
    this.mark1 = 80;
    this.mark2 = 60;
};

marks.prototype = {
    total: () => this.mark1 + this.mark2 //Here "this" refers to Window object. It does not represents the "this" of marks()
}
var m = new marks();
console.log(m.total()); //NaN

So in this case, writing the function expression in normal way is preferable as like as following code snippets. Here in total () method, “this” will be referred to marks () object constructor and it works as usual without exception.

var marks = function () {
    this.mark1 = 80;
    this.mark2 = 60;
};

marks.prototype = {
    total: function () {
        return this.mark1 + this.mark2;
    }
}
var m = new marks();
console.log(m.total()); // 140;

 

Note:

For Arrow function line break is not acceptable as shown below. It will throw Syntax error.

var sum = ()
=> {return true}; //As arrow symbol at starting position

 

Replace () as call back function

We know that the replace() method will return a new string based on the pattern and its corresponding replacement argument.

Here the pattern can be either general string or regular expression that we normally use. And for replacement argument in most cases we use string as replacement. But there is an extensible facility to use this replacement as call back function.

Here we go with a small example code snippets.

Code Snippets:


var numbers = '34 55 27 45 72',
type1 = numbers.replace(/\d+/g, '*'),
type2 = numbers.replace(/\d+/g, function (item) {
return +item < 50 ? '*' : item;
});
console.log("simple replace() : ", type1);
console.log("replace() with call back function : ", type2);

Fiddle Demo

Note:

The call back function will be invoked for the each matched set of an item, when the first parameter regular expression is declared as global.

Why new keyword should be used while instantiating JavaScript class

The new keyword is used to instantiate prototypical class or other objects. But why we need new keyword to initialize it, when you to try to initialize it without new will not throw any error at all then why should I use new before my class to instantiate it.

The answer is simple new keyword will set the ‘this’ to the new object created using function instead of global scope”.

If you didn’t provided new keyword then the context will be set to window(global scope).

You can see the difference in the below code example.

var fn = function (){
       this.a = 2;
       return this;
}

Instantiating fn without new keyword.

 var f = fn();  //`this` refers window here.

Instantiating fn with new keyword.

 var f = new fn();  //`this` refer object created by `fn` here.

Even though using new keyword is the best practice to instantiate class, most of the time, the users will skip them as it doesn’t provide any error at all.

We can use the below simple workaround to enforce the instantiating process using new keyword. Then the above fn will become as below.

 var fn = function (){
       if(!(this instanceof fn)) //workaround to resolve this problem.
          return new fn();
       this.a = 2;
       return this;
}

Now instantiating without new will also set this to the current object.

 var f = fn();  //this refer to fn here.

Instantiating class/function without new keyword in ES6 will throw TypeError.

Happy Scripting… 🙂

Deserialize JSON to instance of JavaScript class

Prototypical class is used to create classes in JavaScript like other programming languages.
The object return from these classes will have corresponding class information. When you to try to serialize and de-serialize this object, the class information associated with the corresponding object will be removed.

In this post, we are going to see how to retrieve this class information and make the object as the instanceof corresponding class.

The process involves two steps. They are

1. Parse using JSON.parse method.
2. Assign the constructor of the actual class to the __proto__ property of the parse JSON.

Let us see this in details.

If we have a JavaScript class like below.

 var myClass = function(){
this.a = 1;
return this.a;
};

var d = new myClass(); //result: &amp;quot;{a:1}&amp;quot;;

d instanceof myClass //result: true;

In the above all works fine. The problem will arise if we try to check instanceof on the object from deserialization like shown below.

var str = JSON.stringify(d);

var d = JSON.parse(str); //result: &amp;quot;{a:1}
d instanceof myClass //result: false;

So now d lost its class information and hence it failed. Now we can use the below utility function to retrieve the class information.


var toCalss = function(obj, proto) {
obj.__proto__ = proto;
return obj;
}

var res = toClass(d, myClass.prototype);

res instanceof myCalss //result: true;

Now the object will have class information which makes the instanceof to work as expected.
Happy scripting..

 

Monkey patching

Monkey patching is a technique that uses the flexibility of the JavaScript to modify any function/method behavior.

Performing monkey patching is simple and it can be done by the following steps.

  1. Store the original function in a variable
  2. Now modify the function behavior
  3. Call the original function using apply.

This can be useful when you want to change the argument values of the function. For example, consider we have the following class.

 


        var math = function () {
            this.a = 1;
            this.b = 2;
        };

        math.prototype = {
            sum: function (a, b) {
                return a + b;
            }
        }

Now we can modify the method sum using the monkey patching technique as follows.


        var original = math.prototype.sum;   Step 1

        math.prototype.sum = function (a, b) {
            a = a * 2, b = b * 2;    Step 2

            return original.apply(this, [a, b]);    Step 3
        }

On executing the above method, the result will as follows.


       var obj = new math();

        obj.sum(1, 2); //return 6;
        //Expected: 3, Actual : 6 due to monkey patch

Happy Scripting… 🙂