Javascript Essentials

Javascript Essentials

Most of us are familiar with javascript but there are a few interesting fundamentals that we have to know to use javascript to its full potential. This blog is a good place to start if you have basic knowledge of JavaScript or even if you are learning JavaScript programming language. Let's explore the essentials now.

Let vs Const vs Var

Javascript provides us 3 different ways to declare a variable - var, let and const. Var is an older way of declaring variables whereas let and const keywords were introduced in the ES6 update.

Syntax :

  • var

      var name;
      name = "John";
    
  • let

      let name;
      name = "John";
    
  • const

      const name = "John";
    

Scope :

  • variables declared using var keyword are function-scoped. They can be used even outside the block that they are declared in, but only inside the same function.

      function check(x){
          {
              var x = 2;
              console.log(x);   // 2
          }
          console.log(x);  // 2
      };
    
  • let and const are block-scoped. They can only be used inside the block where they are declared.

      function check(x){
          {
              let x = 2;
              console.log(x);   // 2
          }
          console.log(x);  
          // Uncaught ReferenceError: x is not defined
      };
    
  • The reason behind this is hoisting in JavaScript.

Hoisting :

  • Hoisting is a JavaScript mechanism where variables and function declarations are moved to the top of their scope(to the top of the current script or the current function based on scope) before code execution.

  • Whenever a variable is hoisted only the declaration is hoisted but not the initialisation.

  • Let's look at an example. Consider the following code.

      console.log(age); // Output: undefined
    
      var age = 24;
    

    We expect that JavaScript throws an error saying "ReferenceError: age is not defined", but it prints undefined.

    This is how the code looks after hoisting :

      var age;
      console.log(age); // Output: undefined
    
      age = 24;
    

    We can see that the declaration of the age variable got hoisted, hence age has a default initial value of undefined. Hence no error is thrown.

  • The variables declared with var keyword are hoisted with default initial value i.e., undefined. Hence in the example above javascript didn't throw an error even when we used age before its declaration.

  • We can access variables using var keyword before declaration without any errors but we cannot use let/const variables before declaration.

  • This is because let/const variables are hoisted but not initialised with any default value.

  • So accessing them before the line they were declared throws the following error.

      console.log(num)  
      //ReferenceError:Cannot access 'num' before initialization.
      let num = 5
    
  • The const/let variable stays in “temporal dead zone” from the start of the block until it is declared. This is the period of execution where these variables are hoisted but not accessible.

Reassign a value :

  • var and let variables can be reassigned but const is used for constants and re-assigning is not allowed.

Redeclaring a variable :

  • Only in case of var variables redeclaring is allowed in the same scope.

      function check(x){
          var name = "John";
          var name = "Cena";     //allowed
          let age = 15;
          let age = 20;         //throws an error
          const PI = 3.14;
          const PI = 3.142;     //throws an error
      };
    

The this keyword

Javascript's this keyword always holds the reference to the current execution context. There are 2 kinds of execution context: global and functional.

  • When used outside a function the this points to the global execution context.

  • When used inside a function, the value of this changes depending on how the function is invoked.

Implicit binding :

  • When we call a function as a method of an object then the this keyword refers to the calling object. This is called implicit binding.

      let myObj = {
          a : 10,
          b : "Hello",
          print : function() {
              console.log(this.a, this.b);
          }
      }
      myObj.print();  //10 Hello
    

Explicit binding :

  • When we explicitly bind the this keyword using any of call(), bind(), or apply() methods then this keyword's default reference is changed to the object called using the above-specified methods. For example incase of call(), the this keyword's reference is changed to the 1st argument of the call() function.

      function checkEven(x){
          if(this.x%2==0){
              return true;
          }
          return false;
      };
      checkEven.call({ x : 20 }) // true
      checkEven.call({ x : 17 }) // false
    

Default binding :

  • When this keyword is used in the global scope it refers to the global object.

      const name = "Ross"
      function check(){
          return this.name;
      };
      console.log(check())  //undefined
    
  • This is because there is no variable “name” in global window object. It works when we define it like we did in the following example.

      this.name = "Ross"
      function check(){
          return this.name;
      };
      console.log(check())  //Ross
    

Regular functions vs Arrow functions

Javascript provides us 2 ways to declare a function - regular function and arrow function. Regular function is an old way of declaring functions in javascript where as arrow function was introduced in ES6 update.

Syntax :

  • Regular function

      function sum(x,y) {
          return x+y;
      }
      sum(2,3)  // 5
    
  • Arrow function

      const sum = (x,y) => {
          return x+y;
      }
      sum(2,3)  // 5
    
  • Whenever there is only one statement in an arrow function we can omit curly braces and semicolon, and the result of the statement/expression will implicitly be returned.

      const sum = (x,y) => x+y
      sum(2,3)  // 5
    

Arguments object :

  • In regular function an argument object is available which gives us access to the arguments sent. But in arrow functions no such object will be available.

      function check() {
          console.log(arguments);
      }
      check(1,"John",10);
      // {
      //   '0': 1,
      //   '1': 'John',
      //   '2': 10
      // }
    
      const check = () => {
          console.log(arguments);
      }
      check(1,"John",10);
      // Uncaught ReferenceError: arguments is not defined
    

new Keyword :

  • Regular functions are both callable and constructible, hence they can be called using the “new” keyword. Arrow functions are callable but not constructible.

      let x = function(a,b){
          console.log(a+b);
      };
      var y= new x(3,7); //10
    
      let x = (a,b) => {
          console.log(a+b);
      };
      var y= new x(3,7); // TypeError: x is not a constructor
    

Methods :

Methods in JavaScript behave differently when they are sent as a callback function. The usage of this keyword doesn't work as expected in case of regular functions.

class Student {
    constructor(name, age) {
      this.name = name;
      this.age = age
    }

    logDetails() {
      console.log("name :", this.name, "age:", this.age);
    }

    logDetailsArrowFn = () => {
      console.log("name :", this.name, "age:", this.age);
    }
  }

const student1 = new Student('John', 23);
setTimeout(student1.logDetails, 1000); 
//name : undefined age: undefined
setTimeout(student1.logDetailsArrowFn, 1000); 
//name : John age: 23

This is because a regular function when sent as a callback function the method will get separated from the object and this keyword refers to global execution context rather than the student1 object. But incase of arrow functions the this lexically gets bind to the student1 object.

For regular functions to work in this scenario, we need to use bind on logDetails function by sending the student1 object.

setTimeout(student1.logDetails.bind(student1), 1000);

Or we can also bind all the regular functions in constructor at once.

constructor(name, age) {
      this.name = name;
      this.age = age
      this.logDetails = this.logDetails.bind(this);
  }

For those who are familiar with react JS, class components are generally avoided because of such confusion using this keyword.

for loop vs forEach

For loop is one of the old ways in javascript to iterate through arrays. forEach is a newer and easier way to iterate over elements in the array. Also forEach is more readable than for loop.

Syntax :

  • for loop

      for (let i = 1; i <= 5; i++) 
      { 
          console.log(i); 
      }
    
  • forEach

      let arr = [1,2,3,4,5]
      arr.forEach((value)=>{
          console.log(value); 
      }) ;
    

forEach method on arrays takes a callback function whose arguments are :

  • value (required) - value of the current array element

  • index (optional) - index of the current array element

  • array (optional) - the array on which forEach is invoked

Performance :

  • In general, for loop is faster than forEach method.

  • The obvious reason is the overhead of creating and destroying execution context when the callback function is invoked for each element of the array incase of forEach.

  • But most of the modern browsers use some optimisation techniques for forEach which makes the performance difference minimal and sometimes even better than traditional for loop.

Break and Continue:

  • break and continue statements can be used in for loop but they are not supported in forEach.

  • an empty return in forEach callback function can be equivalent to continue statement.

Await :

  • Traditional for loop works fine with await keyword.

  • forEach doesn't work properly with await keyword and leads to incorrect output.

Thank you for reading!!

Please leave your feedback in comment section 😇.