JavaScript Quick Revision – 2

Topics Covered:

  • Closure
  • SetTimeout
  • All About functions:
    • Function Statement
    • Expression & Declaration
    • Anonymous Functions
    • Named function expression
    • Parameters & Arguments
    • First class functions
    • Arrow functions
  • Callback Functions
  • Event Listeners
  • Event Loop

Closure

Closures are functions bundled with (along with) its lexical scope.

When Functions are returned, they still maintain their Lexical scope.

function x() {
  var a = 10; //same if let
  return function y() {
    var a = 100;
    console.log(a);
  };

  //return y
}
var z = x();
z();
  • In case of a closure, the variables are not garbage collected after the function is out of scope
  • Here are some examples where closure is used:
    • Module Design Pattern
    • Currying
    • Memoize
    • functions like “once”
    • setTimeout
    • Iterators
    • Data Hiding and encapsulation
  • Disadvantages of Closures:
    • Over-consumption of memory while forming closures.
    • Closures are not garbage collected and may lead to memory leaks

Note: However, modern JS engines/browsers like V8/Chrome have smart Garbage collection wherein, unused variables in a closure are automatically identified and garbage collected.

Constructor function with closure

function Counter() {
  var counter = 0;
  this.incrementCounter = function () {
    counter++;
    console.log(counter);
  };
  this.decrementCounter = function () {
    counter--;
    console.log(counter);
  };
}

var counter1 = new Counter();
counter1.incrementCounter();
counter1.incrementCounter();
counter1.decrementCounter();

SetTimeout

Here is a program that prints 1 to 5, with an interval of 1 through 5. i.e. 1 after 1sec, 2 after 2secs and so on. We cannot simply use var in the for loop because the value of i is overwritten by the time the setTimeout waits for its turn to execute.

function x() {
  for (let i = 0; i <= 5; i++) {
    setTimeout(function () {
      console.log(i);
    }, i * 1000);
  }
  console.log("JS Closure");
}
x();

The above works with let because let has a blocked scope. Value of i is different for each iteration. The same can also be done using var itself but by using a closure:

function x() {
  for (var i = 0; i <= 5; i++) {
    function y(x) {
      setTimeout(function () {
        console.log(x);
      }, x * 1000);
    }
    y(i);
  }
  console.log("JS Closure");
}
x();

All About Functions

  • Function Statement: This is the normal way of declaring functions. It can be hoisted
function a() { 
    console.log("hello")
}
  • Function Declaration: Is same as Function Statement. It is another name for Function Statement.
  • Function Expression: Cannot be hoisted (See Execution context section in Part 1)
var b = function() {
    console.log("hi")
}
  • Anonymous Functions: They are functions that do not have a name. They are used in places where functions are used as a value (ex. In a function expression)
function () {}
  • Named Function Expression: A function expression (shown above) having a name. However, they cannot be called outside the scope of the function.
var b = function xyz() {
  console.log(xyz);
};
b();
// xyz() This cannot be done as it is not in global scope.
  • Parameters & Arguments: Parameters are the identifiers that receive the values in a function. Arguments are the ones which are passed to a function when it is called.
function a(param1,param2){...}   //Function parameters
a(1,2); //Function Arguments.
  • First class functions: It is the ability to use functions like values, that makes functions in JS First class functions or First class citizens.
    • functions can be passed as arguments to other functions
    • functions can be returned from functions.
    • functions can be assigned to variables.

Callback Functions

Functions passed into another functions are called callback functions. They can be called-back sometime in the future by the calling function without having to block the main thread. You can perform asynchronous operations with the help of callbacks. This is what gives JS, which is a single threaded synchronous language, the power of async.

It is a good practice to use a callback and not block the main thread for time taking operations. The callback function will be handled at a later point and the rest of the code will continue to be executed. Executing asynchronously gives a better performance instead of making the main thread wait for the time taking operation to complete.

setTimeout(function () {
  console.log("timer completed after 5sec");
}, 5000);
//main thread
function x(y) {
  console.log("in function x");
  y();
}
function y() {
  console.log("in function y");
}
x(y);

// output:
// in function x
// in function y
// timer completed after 5sec

You can see the Call stack for the above program. It is populated with function x, then y and the call stack stays empty for 5 seconds before the setTimeOut reappears in the stack.

Event Listener

Here is an example of an event listener on a button that counts the number of times it is clicked. It is written using a closure. Closure is used so that count is not modified by the code elsewhere.

In the debugger: You can see the button event in the call stack. In the Scope, you can see the closure having access to the count variable.

function buttonEvent() {
  let count = 0;
  document.getElementById("clickHere").addEventListener("click", function () {
    console.log("button clicked", ++count);
  });
}
buttonEvent();

Event listeners need to be removed after use, as they are heavy and take up memory as it always forms a closure. The program cannot free up memory when a closure exists. Many event listeners can make the program slow. Once event listeners are removed, the variables can be garbage collected.

Event Loop

The cycle of execution:

  • At the Start of the execution: Global execution context(GEC) is created and pushed into the call stack.
  • When a callback is encountered: The Callback function is registered in the Web API’s Environment and attaches an event (in case of an event listener)
  • After reaching the EOL (End of Line): GEC is popped out of the call stack
  • It waits for timer (if setTimeOut is used) to expire. Waits for an event (in case of an event listener)
  • Adds callback function to Callback Queue
  • Event loop keeps checking the call back queue. As soon as it finds the call stack empty and encounters an element in the queue, event loop puts it onto the call stack and the call back function completes execution and is popped out.

More about Event loop:

  • Event loop continuously keeps checking the call stack as well as the Callback queue.
  • Microtask queue is similar to Callback queue. Microtask queue has higher priority over the callback queue. If the stack is empty, the microtask queue tasks are first executed and only when it is empty, the tasks from callback queue are picked and pushed into the stack.
    • All promises, when resolved, go into the microtask queue (callbacks in .then())
    • Mutation observer – is a built in object which observes a DOM element and fires a callback when a change in the DOM tree is detected. This callback also goes into the microtask queue.
  • Starvation caused by the Microtask queue: Promises can create more promises internally which keep getting added to the microtask queue leading to an “infinite asynchronous loop”. This can starve the tasks in the callback queue.

Previous Blog: Javascript Quick Revision – 1

Author: Shilpa

https://kriyavikalpa.com/

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s