Course Content
Data types and Values
0/1
Object-oriented programming in JavaScript
0/1
Error handling and debugging in JavaScript
0/1
JavaScript functions for string and array manipulation
0/1
JavaScript Libraries and Frameworks
0/1
JavaScript
About Lesson

JavaScript Scope

The term “scope” refers to the context in which variables, functions, and objects are accessible or visible. It defines where a particular identifier (variable or function name) can be accessed and manipulated within your code. Primarily JavaScript has three types of scopes (global scope, function scope, and block scope). Additionally, JavaScript also has a concept called “lexical scope,” which is not a separate type of scope but rather a characteristic of how scope is determined based on the physical placement of code within the source file.

Global Scope

Variables declared outside of any function or block have global scope. They can be accessed from anywhere in your code, including within functions and blocks.


var globalVariable = 10;

function someFunction() {
    console.log(globalVariable); // Can access globalVariable here
}

Function Scope

Variables declared within a function are only accessible within that function. They are said to have function scope. They cannot be accessed from outside the function.


function myFunction() {
    var localVar = 5; // localVar has function scope
    console.log(localVar);
}

console.log(localVar); // This would result in an error

Block Scope (ES6 and later)

Introduced with ES6 (ECMAScript 2015), block scope allows variables to be scoped to individual code blocks (loops and conditional statements) using let and const declarations. Variables declared with var do not have block scope.

Variables declared with let and const within blocks (loops, conditional statements, and other block structures) are only accessible within that block. This introduces the concept of block scope, where variables are confined to the nearest containing block.

It’s worth noting that variables declared with the var keyword do not have block scope; instead, they have function scope or global scope, depending on where they are declared. This behavior can sometimes lead to unexpected results, which is one of the reasons why let and const were introduced to provide more predictable block-scoped variables. This makes people avoid var keyword strongly.


if (true) {
    let blockVar = 20; // blockVar has block scope
    const constVar = 30; // constVar also has block scope
    var oldVar = 40; // oldVar doesn't have block scope
}

console.log(blockVar); // This would result in an error
console.log(constVar); // This would result in an error
console.log(oldVar);   // oldVar is accessible, showing 40

Lexical scope

In addition to these 3 basic types of scope, JavaScript also has a concept called “lexical scope.” This means that the scope of a variable is determined by its position in the source code. A function can access variables from its containing (enclosing) scope, even after the containing function has finished executing. This behavior is the basis for closures in JavaScript.


function outerFunction() {
    var outerVar = 'I am from the outer function';

    function innerFunction() {
        var innerVar = 'I am from the inner function';
        console.log(innerVar); // This will print 'I am from the inner function'
        console.log(outerVar); // This will print 'I am from the outer function'
    }

    return innerFunction;
}

var closure = outerFunction(); // 'closure' now holds a reference to 'innerFunction'
closure(); // This will execute 'innerFunction' and print the values as described above.

Hoisting

When you declare a variable using var within a scope, it’s as if that declaration is lifted to the top of the scope. This behavior is called hoisting, making the variable accessible throughout the scope.

var myVar = 7;
foo();                                  // works because `foo()`, 
                                        // declaration is "hoisted"

function foo() {
    myVar = 5;
    console.log( myVar );              // 5
    var myVar;                         // declaration is "hoisted"
                                       // to the top of `foo()`
}
console.log( myVar );                  // 2

Nested scope

When you declare a variable, it is available anywhere in that scope,
as well as any lower/inner scopes. For example:

function foo() {
   var a = 5;
   function bar() {
      var b = 6;
      function baz() {
         var c = 7;
         console.log( a, b, c ); // 5 6 7
      }
      baz();
      console.log( a, b ); // 5 6
   }
   bar();
   console.log( a ); // 5
}
foo();

Note that `c` isn't accessible within `bar()` because it's declared only in the inner `baz()` scope. Similarly, `b` isn't available to `foo()` for the same reason. If you attempt to access a variable's value in a scope where it's not defined, you'll encounter a thrown ReferenceError.

Understanding scope is crucial for avoiding bugs related to variable visibility and for writing clean, maintainable code. It's important to be aware of which variables are accessible from where to prevent unintended behavior and variable name clashes.