JS - Justified Statements JavaScript ๐Ÿ‘€

The core of JavaScript

ยท

10 min read

Introduction

Hello Developers, we are back here with some serious stuff in JavaScript. As we discussed some basics on JavaScript in our previous article. This article is all about the core of JavaScript. Let's get into the content.

๐Ÿ’ก
To understand the topics discussed in this article, I highly recommend seeing the previous article https://hari-js.hashnode.dev/js-just-some-javascript

The core of JavaScript ๐Ÿงจ

Below is the core concept of JavaScript which you should understand from the beginning of your learning process topics to become a pro in the language.

  1. The call stack and how functions execute

  2. Scope types: function scope, block scope and lexical scope

  3. Expressions vs statements

  4. The event loop and asynchronous execution

  5. Closures and higher-order functions

  6. The differences between == and ===

Ok, Now let's explore each topic briefly with some practical examples. I would suggest working on examples simultaneously on a code editor of your preference.

1.The call stack and how functions execute โš“

The call stack is a mechanism the JavaScript engine uses to keep track of function calls. It follows the LIFO (Last In, First Out) principle, where the last function that gets pushed to the stack is the first to be popped out. This allows JavaScript to execute code sequentially and keep track of nested function calls. Here is a detailed explanation of how the call stack works in JavaScript:

When a script starts executing, the JavaScript engine first creates a global execution context and pushes it to the call stack. This global context remains at the bottom of the stack throughout the life of the script.

When a function is called, the JavaScript engine creates a new execution context for that function and pushes it to the top of the call stack. The engine then starts executing the function.

If the function calls another function, a new execution context is created and pushed to the top of the stack. This continues for each nested function call.

When a function returns, its execution context is popped out of the stack and the engine resumes executing the function that called it. This continues until the stack is empty and the script finishes executing.

For example:

  function greet() {
    sayHi();
  }

  function sayHi() {
    console.log('Hi!'); 
  }

  greet();

The call stack would look like this:

  1. The global context is pushed to the stack

  2. The greet() context is pushed and greet() is executed

  3. The sayHi() context is pushed and sayHi() is executed

  4. 'Hi!' is printed and sayHi() returns, popping its context

  5. greet() continues executing and returns, popping its context

  6. The stack is empty and script execution ends.

If the stack exceeds its maximum size (due to deeply nested function calls), a Stack Overflow error will occur.

Functions in asynchronous tasks do not block the call stack. The event loop handles async tasks separately and pushes their results to the stack once completed.

2.Scope types in JavaScript ๐Ÿšฆ

JavaScript has three types of scope: function scope, block scope, and lexical scope. Scope determines the accessibility (visibility) of variables.

i.Function Scope

Function scope is the most basic type of scope in JavaScript. Variables declared within a function are only accessible within that function. This is known as function scope.

  function addNumbers() {
    let num1 = 5;  
    let num2 = 10;
    return num1 + num2; 
  }

  console.log(num1); // Throws a reference error

Here, num1 and num2 are function scoped variables - they are only accessible within the addNumbers() function.

ii.Block Scope

Block scope refers to the scope of variables declared within code blocks - {} curly braces. In JavaScript, variables declared with let and const have block scope.

  if (condition) {
    let variable = "foo";
  }

  console.log(variable);  // Throws a reference error

Here, variable is block scoped to the if block - it is not accessible outside those curly braces.

iii.Lexical Scope

Lexical scope means that inner functions have access to the variables and parameters of their outer functions. This is known as the scope chain.

  function outer() {
    let outerVariable = "Hello";

    function inner() {
      let innerVariable = "World!";
      console.log(outerVariable + innerVariable);  
    }

    inner();   
  }

  outer(); // Prints Hello World!

Here, the inner() function has access to the outer variable defined in its parent outer() function - this is lexical scope.

In summary, variables in JavaScript have either function scope or block scope. Lexical scope refers to how inner functions can access variables defined in their outer functions.

3.Expressions and statements ๐Ÿฑโ€๐Ÿ

Expressions and statements are the basic building blocks of any JavaScript program. Expressions evaluate a value, while statements perform an action. Understanding the difference between the two is essential for writing clean and maintainable JavaScript code.

An expression is any valid unit of code that produces a value. This could be a literal value like a number or string, a variable reference, or an operation that evaluates to a value.

For example:

  1;          // Numeric literal
  "hello";    // String literal
  x;          // Variable reference
  x + y;      // Addition operation 
  a && b;     // Logical AND operation

All expressions can be used as function arguments or assigned to variables. They produce a value that can be used somewhere else in the code.

A statement, on the other hand, performs an action like assigning a value to a variable, calling a function, or controlling the flow of execution.

For example:

  let x = 1;      // Variable declaration 
  x++;            // Increment operation
  if (x > 0) { }  // if statement  
  while (x < 10) { } // while loop
  function sum() {}  // Function declaration

Statements are executed for their side effects - modifying variables, calling functions, etc. They don't produce a value by themselves.

๐Ÿ’ก
One important thing to note is that all expressions can be used as statements by placing a semicolon after them. This is called an expression statement.

For example:

  1 + 2;   // Expression statement
  x++;     // Expression statement

But not all statements can be used as expressions. Only expressions can be passed as function arguments or assigned to variables.

In summary, the key differences between expressions and statements are:

  1. Expressions evaluate to a value, statements perform an action

  2. Expressions can be used as function arguments or assigned to variables

  3. Statements are executed for their side effects

  4. All expressions can be used as statements, but not vice versa

4.The event loop and asynchronous execution ๐Ÿ”ƒ

The JavaScript event loop is a mechanism that executes code and processes events asynchronously. It allows JavaScript to handle asynchronous programming by queuing functions and callbacks and executing them when the stack is clear.

๐Ÿ’ก
JavaScript uses a single-threaded event loop model. While a long-running task is executing, the event loop continues to process other events in the queue. This allows JavaScript to appear asynchronous.

Some key concepts involved in the event loop are:

  • The call stack: As we disscussed earlier call stack stores the execution context of executing functions. When a function is called, it is pushed to the stack. When it returns, it is popped off the stack.

  • The heap: It stores all the live objects in memory. Variables and function arguments refer to objects in the heap.

  • The message queue: It stores functions waiting to be executed. When an asynchronous event occurs, a callback is pushed into the message queue.

  • The event loop: It constantly checks the call stack and the message queue. If the stack is empty, it takes the first callback from the queue and pushes it to the stack, executing it.

Asynchronous functions like setTimeout() and promises resolve asynchronously. When they are called, they are pushed to the stack and immediately return. Their callbacks are then pushed to the message queue.

For example:

  setTimeout(() => {
    console.log("Timeout callback");
  }, 0);

  console.log("Hello");

  // Prints 
  // Hello
  // Timeout callback

Here, the setTimeout() call is pushed to the stack and immediately returns. Its callback is then pushed to the message queue. The console.log("Hello") call is pushed to the stack and executed. Then the stack is empty, so the event loop takes the callback from the queue and pushes it to the stack, executing it.

This allows JavaScript to be asynchronous and non-blocking while maintaining a single thread of execution. Functions are executed one by one from the stack while asynchronous tasks queue their callbacks.

4.Closure and higher order functions {}

A closure in JavaScript is a function that has access to its outer function's scope even after that outer function has returned. In other words, a closure gives a function access to all the variables in the scope that the function was created, even after that scope has gone away.

Example:

  function makeCounter() {
    let count = 0;

    function increment() {
      count++;  
    }  

    return increment;
  }

  const counter = makeCounter();
  counter();
  counter();
  console.log(count); // 2

This is a closure because the inner increment() function has access to the outer count variable even after makeCounter() has returned. This is possible due to the lexical scoping of JavaScript functions.

Higher-order function map() example:

  const numbers = [1, 2, 3];

  const doubleNumbers = numbers.map(function(number) {
    return number * 2;
  });

  console.log(doubleNumbers); 
  // [2, 4, 6]

map() is a higher-order function because it takes a callback function as an argument and applies it to each element of the array.

Higher-order function filter() example:

  const numbers = [1, 2, 3, 4, 5];

  const oddNumbers = numbers.filter(function(number) {
    return number % 2 !== 0; 
  });

  console.log(oddNumbers);
  // [1, 3, 5]

filter() is also a higher-order function. It takes a callback and returns a new array with elements that pass the test in the callback.

๐Ÿ’ก
We will discuss more about Higher order functions in future articles.

6.The differences between == and === ๐Ÿค”

The == and === operators are used to compare values in JavaScript. The main difference between them is:

  • \== performs type coercion before comparing values while === performs a strict comparison without type coercion.

  • \== checks for equality after converting the operands to a common type.

  • \=== checks for equality without converting the operands to a common type.

Some examples for "==" :

  "5" == 5 // Returns true 
  // Because "5" is coerced to an integer 5 before comparing

  5 == true // Returns true
  // Because true is coerced to 1 before comparing

  null == undefined // Returns true
  // Both null and undefined are coerced to their common type before comparing

Some examples for "===" :

  "5" === 5 // Returns false
  // Because string "5" is not equal to number 5

  5 === true // Returns false
  // Because number 5 is not equal to boolean true 

  null === undefined // Returns false
  // null and undefined are two different types so not equal

In general, it is considered best practice to use === over ==. Some reasons are:

  • It avoids unintended type coercion which can lead to bugs.

  • The code is more explicit and self-documenting since the types are checked.

  • It performs slightly faster since no type coercion is needed.

So in summary:

  • Use == when you want to compare values after type coercion.

  • Use === when you want to compare both values and type strictly without coercion.

  • Prefer === over == for more robust and performant code.

The above-discussed topics are just an intro about what is their work in JavaScript. We will see each topic in detail with how it is practically used in real-world problems in future articles

What Next ? -- JavaScript Libraries ๐Ÿงจ

Here is just why we use libraries in JavaScript:-

  1. They extend the capabilities of JavaScript

  2. They make complex tasks easier

  3. They improve performance

  4. They have a large ecosystem

  5. They have a learning curve

And that's it let's see more in the next article.

Happy Coding.....๐Ÿ˜Š

ย