Scope and related errors in JS...

TypeError and Reference Error

Let's start...

What is Scope?

The scope is the region where a variable can be accessed by its identifier name.

Let's look into the examples:

Example-1:
    const name = 'Akshara' //Global Scope
    console.log(name)  //Prints Akshara

Example-2:
    const name = 'Akshara'
    function greetings(){
      const greet = `Welcome ${name}` //name is accessible because defined in parent's scope i.e Global scope
      //greet variable is defined in function's(greetings) scope
      console.log(greet) //Prints Welcome Akshara
    }
    greetings()
    console.log(greet) //Reference Error -> variable greetings is not defined in outer scope

The scope is defined as a set of rules by which the engine finds the variable by its identifier name. Scopes are nested in a hierarchy, so that child's scope has access to the parent's scope but not vice-versa.

Types Of Scopes

  1. Global Scope

    Variables declared are not inside the function, block or module and can be accessed from anywhere in the program are defined in Global Scope.

     Example-1: //Consider this to be index.js file
    
     const totalPrice = 3999
     const discountedPrice = 599
     const finalPrice = totalPrice - discountedPrice
    
     //all three variables are declared in Global Scope
    
     Example-2: //Another way to declare global variable
     window.myName = 'Akshara' //Global variable myName
    
  2. Module Scope

    Variables that are declared in a module (file that is exported) are defined in the module scope.

     //order.js file
     export class Order {
        items: [],
        price: {
             total: 0,
             currency: 'INR'
        },
        calculatePrice: () => console.log("Price is")
     }
    
     //Order variable is accessed in order.js file only until it is
     //exported from file and imported in another.
    
     //index.js file
     import { Order } from './order.js'
    
  3. Function Scope

    Variables declared (using let, const, var) inside the function body are defined in Function Scope

     function selectGenres() {
     //All three are function scoped variables 
       var Genre1 = 'Comedy' 
       const Genre2 = 'Horror'
       let Genre3 = 'Action'
    
       console.log(Genre1, Genre2, Genre3) // Prints Comedy Horror Action
     }
    
     selectGenres()
     console.log(Genre1, Genre2, Genre3) // Reference Error, since scope Resolution fails(trying to access function scoped variables in global scope)
    
  4. Block Scope

    Variables declared in a block ( { } curly brackets) are defined in Block Scope.

     //var declared variables are not block-scope.Only let and const
     Example-1: 
         if(true){
           let price = 500
           console.log(price)
         }
         console.log(price) //Reference Error
    
     Example-2:
         if(true) {
           const pi = 3.17
           console.log(pi)
         }
         console.log(pi) //Reference Error
    
     Example-3:
         //VAR is not block-scope
         if(true) {
          var status = 'confirmed'
          console.log(status) //confirmed
         }
         console.log(status) //Prints confirmed
    

Scope Chain

Before diving deep into the scope chain, let's understand the nested scope

Consider this example:

function getGifts(numOfGifts){
 //Bubble-2
  var complementary = numOfGifts + 1
  function getHomeDecorItems(numOfHomeDecorItems){
    //Bubble-3
    console.log(numOfGifts,complementary,numOfHomeDecorItems) //2,3,1
  }
  getHomeDecorItems(1)
}
getGifts(2) //Bubble-1

Consider this as bubbles:

Bubble-1: This bubble encompasses the global scope and has just one identifier i.e getGifts

Bubble-2: This bubble encompasses the scope of getGifts function which includes these identifiers complementary, getHomeDecorItems, and numOfGifts

Bubble-3: This bubble encompasses the scope of getHomeDecorItems function which includes only one identifier numOfHomeDecorItems

Now let's see its execution:

When the engine executes the console.log(numOfGifts,numOfHomeDecorItems, complementary) statement it goes on to find those three variables. Now numOfGifts is not found in Bubble-3, so it goes onto the outer bubble i.e. Bubble-2 finds their numOfGifts. A similar process happens for complementary variable. numOfHomeDecorItems variable is found in Bubble-3 itself.

That is what defines the scope chain

Javascript Engine tries to find the variable in the current scope. If it is not found, it will look into the outer scope and this process continues until it reaches global scope.

Scope look-up is stopped at the first match. If the same identifier name is declared at multiple layers of nested scope, the process is defined as shadowing

Errors due to scope

  1. Reference Error

Reference Error occurs because of scope-resolution failure.

Example:

function doSomething() {
 var saySomething = 'Hello-Australia'
 console.log(saySomething) //Prints Hello-Australia
}

 console.log(saySomething) //Reference Error

Type Error

Type error occurs when a variable value is found i.e. scope-resolution is a success, but you try to perform some impossible operation.

Example-1:
    function convertToUpperCase(){
     var d=1234
     console.log(d.toUpperCase())    
    }
    convertToUpperCase() //Results in type error

Scope Models in JS

Lexical Scope

The lexical or static scope is determined at lexical i.e. compile time.

let numOfIceCreams = 4;
function printNumber() {
  console.log(numOfIceCreams);
}
function log() {
  let numOfIceCreams = 5;
  printNumber();
}
// Prints 4
log();

Here, console.log will always print 4 no matter where it is called from.

Javascript uses the Lexical scope model.

Dynamic Scope

In Dynamic scope, you search the current scope, if not found, you search in the function that called the local function and so on i.e. call stack.

Consider the same example:

let numOfIceCreams = 4;
function printNumber() {
  console.log(numOfIceCreams);
}
function log() {
  let numOfIceCreams = 5;
  printNumber();
}

// Prints 5
log();

numOfIceCreams is not found in printNumber() function scope so it goes into log() scope to find numOfIceCreams and hence prints 5.

Conclusion

  1. The scope is a region where a variable can be accessed by its identifier name.

  2. Variables can be declared in Global, local (module, function, block) scope.

  3. The engine performs a look-up in the outer scope until reaches the global scope to find the variable.

  4. Scope resolution failure results in a reference error.

  5. If scope resolution is a success but an impossible operation is executed it results in a type error.

  6. Javascript uses the Lexical scope model.

Congralutions, you made it to end! I hope you learnt something new today