JavaScript Lexical Scoping & Closures

JavaScript Lexical Scoping & Closures

Understand what a lexical scoping is and what closures are.

·

5 min read

Lexical Scope

  • Before getting to closures, you have to be familiar with lexical scopes. If you know what this is, you might skip this paragraph.

  • Lexical scoping is also called static scoping in other programming languages. It is a concept that defines in which scope (range of functionality) is a certain variable accessed.

  • Example I

// this is the global scope of our code.
// 'a' is a variable that's defined in the global scope
let a = 123;
// 'multen' is a function that multiplies a number by 10
function multen (x) {
    // 'n' is a local variable that's only defined and accessed inside the 'multen' function
    let n = 10;
    console.log("n = ", n); // output is: "n = 10"
    return x * n;
}
// Lets try to console.log 'n' again
console.log("n = ", n); // ReferenceError: n is not defined
  • The issue is that n is only accessed inside the multen function, because we've declared inside the scope of that function.
  • Beware ! in JavaScript arrow functions don't define their inner scope like the ones created with the function my_function_name... manner.
  • We cannot access variables defined in an inner scope from an outer scope. But we can access things defined in an outer scope, inside an inner one.

  • Example II

let a = 42; // a variable defined in our global scope (the most outer one).
// a function that multiplies 'a' by a given number
function multiply_by(n) {
    let result = a * n;
    return result;
}
multiply_by(2); // result is: 84
multiply_by(10) // result is: 420
  • In Example II when we used the variable a in the multiply_by function, it didn't throw a ReferenceError and that's because functions have always access to their outer scope.

Closure

  • A closure is a function (we'll call it inner_func) that is defined inside another function (we'll call it outer_func) and can remember (still reference to) the local variables of the outer_func, after outer_func has been called.

  • In other words, a closure is an inner function that keeps track of the local variables of an outer function in which it (the inner function) has been defined. Because like we've seen in Example I above, when we wanted to use the n variable of the multen function, it threw us a ReferenceError because it's outside of its scope.

  • Even with closures we won't be able to exactly call a variable that's been defined inside a function's scope, from an outer scope, but we'll get referenced to it and we can keep track of it through the closure (the inner function).

Why and When do you need closures ?

  • In JavaScript, we use closures mainly to reinforce data encapsulation because data restriction and private methods don't exist natively in JS. So, we mimic those concepts with closures.

  • To better understand that, lets take a simple example: you have a website and you want to know how many viewers visited a specific page. For that, you decide to create a function that counts the number of the visits to this page. In its simplest way, you'll probably do something like:

let number_of_visits = 0;

function update_number_of_visits () {
    number_of_visits += 1;
}

update_number_of_visits() // the function would be called whenever someone visits the page.
  • Even though the code is correct and works without errors, you can still update or overwrite the variable containing the actual number of visits from anywhere, which is not secured. For that, you'll have to make it more secure and isolated, the first solution you'd think of would be probably defining the variable inside of the function, like so:
function update_number_of_visits () {
    let number_of_visits = 0;
    number_of_visits += 1;
    return number_of_visits;
}

// the code is gonna work without errors but we'll still gonna have a problem
update_number_of_visits() // 1
update_number_of_visits() // 1
update_number_of_visits() // 1
  • After 3 calls of the update_number_of_visits function, instead of getting 3, we're still getting 1 and that's because the function re-initiate the number_of_visits variable, each time its called.

Writing A closure

  • To avoid the last issue that we've encountered, we need to use a closure.
function update_number_of_visits () {
    let number_of_visits = 0;
    // let's create our inner function (closure)
    function update_nb () {
        number_of_visits += 1; // update the number_of_visits
        return number_of_visits; // return the new updated value
    }
    return update_nb;
}

// We define a "visits" variable that take a value
// the return value of the "update_number_of_visits" function,
// which is a function.
// But we do as follow, to have our "number_of_visits" variable
// initialized at 0 and from now we have it ready of the inner
// function to use and update.
let visits = update_number_of_visits();

// Lets check "visits"
console.log(visits);
/*
the output of the console above is:
ƒ update_nb () {
    number_of_visits += 1; // update the number_of_visits
    return number_of_visits; // return the new updated value
}
which means that "visits" is a function.
*/

// Now, whenever we call the visits function, we get the last updated
// value of the "number_of_visits" variable.

visits() // 1
visits() // 2
visits() // 3
  • If you still find Lexical scoping and Closures confusing, you might find the following resources helpful:
  • MDN Closure
  • W3Schools