Monday, July 30, 2018

Javascript ES6 - lexical scope of this

In Javascript every function brings its own 'this' context. The situation becomes difficult to work with when there are many functions inside function which complicates the context of 'this'. The ES6 fat arrow (=>) passes the lexical scope of parent 'this' to all child functions -

Example:
function parentFunction(){
    this.add = 0;
    this.addAnother = function(){
    this.addOne = setTimeout(function(){
        console.log(++this.add);
        }, 1000)
    }
} 

var funcObj = new parentFunction();
funcObj.addAnother(); // NaN  

It outputs to 'NaN' because the 'this' context here 'console.log(++this.add);' is scoped to setTimeout function.

To avoid this, the ES5 solution is to use the classic 'var that = this' -
function parentFunction(){
    this.add = 0;
    var that = this;
    this.addAnother = function(){
    this.addOne = setTimeout(function(){
        console.log(++that.add);
        }, 1000)
    }
} 

var funcObj = new parentFunction();
funcObj.addAnother();  

Now, this works perfectly fine, but the new ES6 fat arrow works even better by passing the lexical scope of parent 'this' to all child functions -
function parentFunction(){
    this.add = 0;
    this.addAnother = () => {
    this.addOne = setTimeout(() => {
        console.log(++this.add);
        }, 1000)
    }
} 

var funcObj = new parentFunction();
funcObj.addAnother();  

The traditional ES5 'this' creates a new scope of 'this' in each function which is why we were getting the 'NaN' because the 'this' in setTimeout function was scoped to the setTimeout function alone.

With the fat arrow, the 'this' in setTimeout refers to the lexical scope of parent 'this'.




Friday, July 27, 2018

Javascript: Var vs Let vs Const

The var, let and const game is all about their scope (the location where they are available to the developers). Put it simply -
  • Var has a functional scope (local and global/window), also it goes all the way up till the window object to find the declaration. Var is also a function scope (they are available inside the function they are declared in)   
  • Let has a block level scope. It stays within the curly braces. Let allows re-assigning its values  
  • Const also has a block level scope. Use const if you do not need to change its value 

Let's go with examples:

Var:
for(var i = 0; i < 10; i++){
    console.log(i);
}
It's normal to consider that the scope of 'var i' would be available inside the curly braces of for loop. However, the 'var i' is also accessible outside of the for loop.
for(var i = 0; i < 10; i++){
    console.log(i);
}
console.log('After loop is over ' +i); //logs 10 

It's completely valid because of the concept of 'Hoisting'. All variables in JavaScript are hoisted on top of the function - something like this:
var i; 
for(i = 0; i < 10; i++){
    console.log(i);
}
console.log('After loop is over ' +i);   

This is because there is only one type of scope in ES5 - that is Function Scope
function func(){ 
    for(var i = 0; i < 10; i++){
        console.log(i);
    }
}
func();
console.log('After the function ' +i); // throws an error, because var i is not accessible     

And if you don't declare the var keyword inside the function, the JS will hoist the var at the window/global level and it will be accessible everywhere  -
var i; // hoists in global scope 
function func(){ 
    for(i = 0; i < 10; i++){ // if you don't mention 'var i'
        console.log(i);
    }
}
func();
console.log('After the function ' +i); // logs 10      

Let:
Let will never have the issues like var, because let has a block level scope which means it doesn't work outside of that block of code, or outside of those curly braces -
 
for(let i = 0; i < 10; i++){
    console.log(i);
}
console.log('After loop is over ' +i); // doesn't work     

But you can re-assign the values of let -
 
let i = 'hi';
i = 55; 
console.log(i); // logs 55 

Const:
Changing const value is not possible -
 
const i = 10;
i = 55; 
console.log(i); // throws typeError  

But, it allows a little flexibility to change the property of const object  -
 
const i = {
    k: 10
}
i.k = 55; 
console.log(i); // logs k:55