Scope in JavaScript

Let's dive into an important concept in JavaScript: scope. Scope is all about where your variables and functions can be accessed in your code.

Without understanding scope you might have trouble figuring out why some variables are available in one part of your code but not another.

In JavaScript, there are a few types of scope: global scope, local scope, and block scope.

Global Scope

Variables declared outside of any function or block have a global scope and are accessible from anywhere in the code. This means, inside your functions you can reach out and refer to variables that are not available inside your function:

const globalVariable = 'I am global';

function displayGlobalVariable() {
  console.log(globalVariable); // Outputs: I am global
}

displayGlobalVariable();
console.log(globalVariable); // Outputs: I am global

In this example, globalVariable is accessible both inside the displayGlobalVariable function and outside it.

Local Scope

Variables declared inside a function are in the local scope of that function. They can only be accessed within that function. Any code outside of your function will not be able to access those variables:

function displayLocalVariable() {
  let localVariable = 'I am local';
  console.log(localVariable); // Outputs: I am local
}

displayLocalVariable();
console.log(localVariable); // Error: localVariable is not defined

In this example, localVariable is only accessible inside the displayLocalVariable function. Trying to access it outside the function results in an error.

Block Scope

Variables declared with let or const inside a block (a pair of curly braces {}) are only accessible within that block. Here's an example:

{
  let blockScopedVariable = 'I am block scoped';
  console.log(blockScopedVariable); // Outputs: I am block scoped
}

console.log(blockScopedVariable); // Error: blockScopedVariable is not defined

In this example, blockScopedVariable is only accessible inside the {} block. This might not feel super useful right now, but later as we learn more concepts it'll be a useful thing to know.

Function Scope vs. Block Scope with var

So far, we have declared all variables with either const or let. These are newer features of JavaScript. Before the introduction of let and const, JavaScript only had function scope for variables declared with var. Variables declared with var are function-scoped and not block-scoped, which means they are accessible throughout the entire function they are declared in, even if they are inside a block like an if statement or a loop.

function displayVarScope() {
  {
    var functionScopedVariable = 'I am function scoped';
  }
  console.log(functionScopedVariable); // Outputs: I am function scoped
}

displayVarScope();

In this example, functionScopedVariable is accessible throughout the entire displayVarScope function, even though it was declared inside an {} block.

Why Avoid var?

I've intentionally avoided using var because I couldn't think of a scenario other than reading older codebases that you might find helpful. The var keyword has some quirks that can lead to unexpected behavior. Let's look at a few examples to understand why let and const are generally preferred.

Function Scope vs. Block Scope

Example with var:

function displayVarScope() {
  if (true) {
    var functionScopedVariable = 'I am function scoped';
  }
  console.log(functionScopedVariable); // Outputs: I am function scoped
}

displayVarScope();

In this example, functionScopedVariable is accessible throughout the entire displayVarScope function, even though it was declared inside an if block. This is because var is function-scoped, not block-scoped.

Example with let:

function displayLetScope() {
  if (true) {
    let blockScopedVariable = 'I am block scoped';
  }
  console.log(blockScopedVariable); // Error: blockScopedVariable is not defined
}

displayLetScope();

In this example, blockScopedVariable is not accessible outside the if block where it was declared. This is because let is block-scoped, making the code more predictable and easier to manage.

Hoisting

Example with var:

function hoistingExample() {
  console.log(hoistedVariable); // Outputs: undefined
  var hoistedVariable = 'I am hoisted';
}

hoistingExample();

In this example, hoistedVariable is hoisted to the top of the function scope. This means the variable declaration is moved to the top of the function, but the initialization stays in place. The result is that the console.log statement does not throw an error, but it outputs undefined because the variable is not yet initialized.

Example with let:

function hoistingWithLetExample() {
  console.log(notHoistedVariable); // Error: Cannot access 'notHoistedVariable' before initialization
  let notHoistedVariable = 'I am not hoisted';
}

hoistingWithLetExample();

In this example, trying to access notHoistedVariable before it is declared results in an error. This is because let does not hoist in the same way var does, which helps prevent the confusion and bugs associated with hoisting.

By using let and const, you get block-scoped variables and avoid the pitfalls of hoisting, making your code cleaner and less prone to errors. Most modern JavaScript developers prefer using let and const over var.

Skills to Practice

  1. Global Scope: Declare a variable in the global scope and access it inside and outside a function.

    • Example:
    let globalVar = 'Global';
    
    function showGlobalVar() {
      console.log(globalVar); // Should print 'Global'
    }
    
    showGlobalVar();
    console.log(globalVar); // Should also print 'Global'
    
  2. Local Scope: Declare a variable inside a function and try to access it outside the function.

    • Example:
    function localScopeExample() {
      let localVar = 'Local';
      console.log(localVar); // Should print 'Local'
    }
    
    localScopeExample();
    console.log(localVar); // Should give an error
    
  3. Block Scope: Use let or const inside a block and try to access the variable outside the block.

    • Example:
    if (true) {
      let blockVar = 'Block';
      console.log(blockVar); // Should print 'Block'
    }
    
    console.log(blockVar); // Should give an error
    
  4. Function Scope vs. Block Scope with var: Declare a variable with var inside a block and see where it is accessible.

    • Example:
    function varScopeExample() {
      if (true) {
        var varScoped = 'Function Scope';
      }
      console.log(varScoped); // Should print 'Function Scope'
    }
    
    varScopeExample();
    
  5. Nested Scope: Create nested functions and access variables from outer and inner scopes.

    • Example:
    let globalOuter = 'Global Outer';
    
    function outerFunction() {
      let localOuter = 'Local Outer';
    
      function innerFunction() {
        let localInner = 'Local Inner';
        console.log(globalOuter); // Should print 'Global Outer'
        console.log(localOuter); // Should print 'Local Outer'
        console.log(localInner); // Should print 'Local Inner'
      }
    
      innerFunction();
    }
    
    outerFunction();
    

Understanding scope is crucial for managing variable accessibility and avoiding conflicts in your code. Practice these concepts to become more proficient in handling scope in JavaScript.

BeginnerJavaScript
Avatar for Niall Maher

Written by Niall Maher

Founder of Codú - The web developer community! I've worked in nearly every corner of technology businesses: Lead Developer, Software Architect, Product Manager, CTO, and now happily a Founder.

Loading

Fetching comments

Hey! 👋

Got something to say?

or to leave a comment.