JavaScript


Scope All Versions

1
2
3
E4X
5
5.1
6
7
8

This draft deletes the entire topic.

Introduction

Introduction

expand all collapse all

Examples

  • 13

    When a function is declared, variables in the context of its declaration are captured in its scope. For example, in the code below, the variable x is bound to a value in the outer scope, and then the reference to x is captured in the context of bar:

    var x = 4; // declaration in outer scope
    
    function bar() {
        console.log(x); // outer scope is captured on declaration
    }
    
    bar(); // prints 4 to console
    

    Sample output: 4

    This concept of "capturing" scope is interesting because we can use and modify variables from an outer scope even after the outer scope exits. For example, consider the following:

    function foo() {
        var x = 4; // declaration in outer scope
    
        function bar() {
            console.log(x); // outer scope is captured on declaration
        }
    
        return bar;
        
        // x goes out of scope after foo returns
    }
    
    var barWithX = foo();
    barWithX(); // we can still access x
    

    Sample output: 4

    In the above example, when foo is called, its context is captured in the function bar. So even after it returns, bar can still access and modify the variable x. The function foo, whose context is captured in another function, is said to be a closure.

    Private data

    This lets us do some interesting things, such as defining "private" variables that are visible only to a specific function or set of functions. A contrived (but popular) example:

    function makeCounter() {
        var counter = 0;
    
        return {
            value: function () {
                return counter;
            },
            increment: function () {
                counter++;
            }
        };
    }
    
    var a = makeCounter();
    var b = makeCounter();
    
    a.increment();
    
    console.log(a.value());
    console.log(b.value());
    

    Sample output:

    1
    0
    

    When makeCounter() is called, a snapshot of the context of that function is saved. All code inside makeCounter() will use that snapshot in their execution. Two calls of makeCounter() will thus create two different snapshots, with their own copy of counter.

    Immediately-invoked function expressions (IIFE)

    Closures are also used to prevent global namespace pollution, often through the use of immediately-invoked function expressions.

    Immediately-invoked function expressions (or, perhaps more intuitively, self-executing anonymous functions) are essentially closures that are called right after declaration. The general idea with IIFE's is to invoke the side-effect of creating a separate context that is accessible only to the code within the IIFE.

    Suppose we want to be able to reference jQuery with $. Consider the naive method, without using an IIFE:

    var $ = jQuery;
    // we've just polluted the global namespace by assigning window.$ to jQuery
    

    In the following example, an IIFE is used to ensure that the $ is bound to jQuery only in the context created by the closure:

    (function ($) {
        // $ is assigned to jQuery here
    })(jQuery);
    // but window.$ binding doesn't exist, so no pollution
    

    See the canonical answer on Stackoverflow for more information on closures.

  • 11

    Hoisting is a mechanism which moves all variable and function declarations to the top of their scope. However, variable assignments still happen where they originally were.

    For example, consider the following code:

        console.log(foo);
        // - > undefined
        var foo = 42;
        console.log(foo);
        // - > 42
    

    The above code is the same as:

        var foo; // Hoisted variable declaration
        console.log(foo);
        // - > undefined
        foo = 42; // variable assignment remains in the same place
        console.log(foo);
        // - > 42
    

    Note that due to hoisting the above undefined is not the same as the not defined resulting from running:

        console.log(foo);
        // - > foo is not defined 
    

    A similar principle applies to functions. When functions are assigned to a variable (i.e. a function expression), the variable declaration is hoisted while the assignment remains in the same place. The following two code snippets are equivalent.

        console.log(foo(2, 3));
        // - > foo is not a function
        var foo = function(a, b) {
            return a * b;
        }
    
        var foo;
        console.log(foo(2, 3));
        // - > foo is not a function
        foo = function(a, b) {
            return a * b;
        }
    

    When declaring function statements, a different scenario occurs. Unlike function statements, function declarations are hoisted to the top of their scope. Consider the following code:

        console.log(foo(2, 3));
        // - > 6
        function foo(a, b) {
            return a * b;
        }
    

    The above code is the same as the next code snippet due to hoisting:

        function foo(a, b) {
            return a * b;
        }
    console.log(foo(2, 3));
        // - > 6
    

    Here are some examples of what is and what isn't hoisting:

    // (A) valid code:
    foo();
    
    function foo() {}
    
    
    // (B) **invalid**:
    bar(); TypeError: bar is not a function
    var bar = function () {};
    
    
    // (C) valid:
    foo();
    function foo() {
        bar();
    }
    function bar() {}
    
    
    // (D) **invalid**:
    foo();
    function foo() {
        bar(); // TypeError: bar is not a function
    }
    var bar = function () {};
    
    
    // (E) valid:
    function foo() {
        bar();
    }
    var bar = function(){};
    foo();
    

    Limitations of Hoisting

    Initializing a variable can not be Hoisted or In simple JavaScript Hoists declarations not initialization.

    For Example:

    The below scripts will give different outputs.

    <script>
    var x = 2; 
    var y = 4; 
    alert(x+y);
    </script>
    

    This will give you an output of 6.

    And

    <script>
    var x = 2; 
    alert(x+y);
    var y = 4; 
    </script>
    

    This will give you an output of NaN. Since we are initializing the value of y, the JavaScript Hoisting is not happening, so the y value will be undefined. The JavaScript will consider that y is not yet declared.

    So the second example is same as of below.

    <script>
    var x = 2; 
    var y;
    alert(x+y);
    y = 4; 
    </script>
    

    This will give you an output of NaN.

    enter image description here

  • 8

    (Note: All examples using let are also valid for const)

    var is available in all versions of JavaScript, while let and const are part of ECMAScript 6 and only available in some newer browsers.

    var is scoped to the containing function or the global space, depending when it is declared:

    var x = 4; // global scope
    
    function DoThings() {
        var x = 7; // function scope
        console.log(x);
    }
    
    console.log(x); // >> 4
    DoThings();     // >> 7
    console.log(x); // >> 4
    

    That means it "escapes" if statements and all similar block constructs:

    var x = 4;
    if (true) {
        var x = 7;
    }
    console.log(x); // >> 7
    
    for (var i = 0; i < 4; i++) {
        var j = 10;
    }
    console.log(i); // >> 4
    console.log(j); // >> 10
    

    By comparison, let is block scoped:

    let x = 4;
    
    if (true) {
        let x = 7;
        console.log(x); // >> 7
    }
    
    console.log(x); // >> 4
    
    for (let i = 0; i < 4; i++) {
        let j = 10;
    }
    console.log(i); // >> "ReferenceError: i is not defined"
    console.log(j); // >> "ReferenceError: j is not defined"
    

    Note that i and j are only declared in the for loop and are therefore undeclared outside of it.

    There are several other crucial differences:

    Global variable declaration

    In the top scope (outside any functions and blocks), var declarations put an element in the global object. let does not:

    var x = 4;
    let y = 7;
    
    console.log(this.x); // >> 4
    console.log(this.y); // >> undefined
    

    Re-declaration

    Declaring a variable twice using var doesn't produce an error (even though it's equivalent to declaring it once):

    var x = 4;
    var x = 7;
    

    With let, this produces an error:

    let x = 4;
    let x = 7;
    

    TypeError: Identifier x has already been declared

    The same is true when y is declared with var:

    var y = 4;
    let y = 7;
    

    TypeError: Identifier y has already been declared

    However variables declared with let can be reused (not re-declared) in a nested block

    let i = 5;    
    {
       let i = 6;
       console.log(i); // >> 6
    }
    console.log(i); // >> 5
    

    Within the block the outer i can be accessed, but if the within block has a let declaration for i, the outer i can not be accessed and will throw a ReferenceError if used before the second is declared.

    let i = 5;
    {
        i = 6;  // outer i is unavailable within the Temporal Dead Zone
        let i;
    }
    

    ReferenceError: i is not defined

    Hoisting

    See the main topic on Hoisting for further details.

    var can be referenced before assignment, let cannot:

    console.log(x); // >> undefined
    console.log(y); // >> "ReferenceError: `y` is not defined"
    var x = 4;
    let y = 7;
    

    The area between the start of a block and a let or const declaration is known as the Temporal Dead Zone, and any references to the variable in this area will cause a ReferenceError.

Please consider making a request to improve this example.

Syntax

Syntax

Parameters

Parameters

Remarks

Scope is the context in which variables live and can be accessed by other code in the same scope. Because JavaScript can largely be used as a functional programming language, knowing the scope of variables and functions is important as it helps to prevent bugs and unexpected behavior at runtime.

Still have a question about Scope? Ask Question

Topic Outline