Scope and Closures in JavaScript
Tags: javascript
Published: 2023-06-01 14:29:33

[Photo](https://www.pexels.com/photo/fishing-nets-floating-in-the-air-9945260/) by Jan van der Wolf
<br>


Scope and closures are essential concepts that are applicable to every JavaScript code base. While most developers may have some familiarity with these concepts, they are fundamental topics that deserve dedicated attention. 
Understanding scope and closures is crucial for writing efficient and maintainable JavaScript code. In this article we will be covering the following topics:

1. The Scope
2. Global Scope
3. Function Scope
4. Block Scope
5. Closures


## 1. **The Scope** 
Scope as a defined by a quick google search is as follows, [“the extent of the area or subject matter that something deals with or to which it is relevant.”](https://www.lawinsider.com/dictionary/scope#:~:text=Scope%C2%A0means%20the%20extent%20of%20the%20area%20or%20subject%20matter%20that%20something%20deals%20with%20or%20to%20which%20it%20is%20relevant.)

In the context of JavaScript, scope refers to the extent of a variable's accessibility within our code. JavaScript encompasses three types of scope: Global, Function, and Block.

## 2. **Global Scope** 

A  variable with global scope is accessible globally throughout our code base. Any variable declared outside of a function with either the `var`, `let` or `const` keywords is said to have global scope. 

```jsx
let a = 1
const b = {someProperty: '2'};
var c = 'hello world';

function outer(){
	console.log(a);
	
	function inner(){
		console.log(b);   
	}
	inner();
}

console.log(c);

outer(); 

/*
Output:

1
{someProperty: '2'}
hello world

*/
```

## 3. **Function Scope**

Every function has it’s own scope, therefore, a variable declared in a function is only accessible within that function scope therefore the variable has function scope. This applies to any variable declared with `var`, `let` and `const`. 

```jsx
function outer(){
	let a = 1
	const b = {someProperty: '2'};
	var c = 'hello world';

	function inner(){
		console.log(b);   
	}

	console.log(a);
	console.log(b);
	console.log(c);
	inner();
}

outer(); 
/*
Output:

1
{someProperty: '2'}
hello world
{someProperty: '2'}
*/

console.log(a);
/*
Output:

ReferenceError: a is not defined
*/
```

As you can see in the final output, accessing the `a` variable outside of it’s functional scope is not possible and will result in a reference error. 

Another interesting thing you can see here is that `b` in the outer function is accessible inside the nested inner function. This ability, where an inner function has access to the parent’s surrounding scope is known as the lexical or static scope.

We can nest functions as much as we like and each inner function will have access to the scope of all it’s outer functions.

Let’s look at one more example.

```jsx
function outer(){
	const a = 1

	function inner(){
		const a = 'hello world'
		console.log(a);   
	}

	inner();
	console.log(a);
}

outer(); 
/*
Output:

hello world
1
*/

```

In the above function we can see that although the inner function has access to the outer function’s scope, the variable `a` in it’s own scope with the same name takes precedence when accessing the variable of that name. This concept is known as variable shadowing or shadowing.

## 4. **Block Scope** 

The block scope was introduced with the release of ES6 in 2015 and only applies to the two new variable types that were also introduced, `const` and `let`. 

A block is considered as group of statements enclosed within curly braces. The statements typically consist of control flow statements e.g. if, else, switch, variable declarations and function calls.

Any variables declared with the variable type `var` will simply ignore the block scope and apply either function or global scope depending on the given lexical environment.

Let’s checkout an example demonstrating block scope.

```jsx
const a = true;

if (a) {
  let b = 1;
  {
    const c = 2;
    var d = 3;
    console.log(b);
    console.log(c);
  }
  console.log(b);
}

console.log(d);

/*
Output:

1
2
1
3
*/
```

As you can see block scope behaves identically to function scope except that it only applies to `const` and `let` variables, where the `d` variable declared with the `var` type simply ignores the block scope.

Additionally the block scope also adheres to the same shadowing and lexical scope behaviours we explored in function scopes.

## 5. Closures 

MDN web docs defines a closure as [“the combination of a function and the lexical environment within which that function was declared”](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures#:~:text=the%20combination%20of%20a%20function%20and%20the%20lexical%20environment%20within%20which%20that%20function%20was%20declared.). 

Let’s take a look at an example which should provide a more intuitive understanding of this definition.

```jsx

function outerFunction() {
  const outerVariable = 'I am the outer variable';

  function innerFunction() {
    console.log(outerVariable);
  }

  return innerFunction;
}

var closure = outerFunction();
closure();  

/*
Output:

I am the outer variable
*/
```

The closure function in this example is the `innerFunction()`, it captures the scope of it’s parent function, which is the variable `outerVariable`, within it’s own scope. 

The `innerFunction()` can then access the `outerVariable` at any time during execution even if not during execution of the parent function.

Putting it more simply, a closure is a function that simply remembers the lexical scope from the place where it was defined regardless of where or when it is executed later.

Let’s take a look at some real world use cases of closures.

### 5.1 Event handling

Here is an example of how keep we can keep track of the number of times a button is pressed.

```jsx
// Assume we have an html button with id #button

function buttonSetup() {
	const buttonRef = document.getElementById("#button");
	let count = 0;

	buttonRef.addEventListener("click", function () {
	  count++;
	  console.log(count);
	});
}

buttonSetup();

/*
Button clicked:
1

Button clicked again:
2

Button clicked again:
3

*/
```

As you can see here the anonymous event listener callback function captures the `count` variable in the `buttonSetup()` function in a closure.

Every time the button is clicked the `count`  variable is simply incremented and its value logged to the console.

### 5.2 Private variables and encapsulation

In this example let’s model a toggle switch and hide access to it’s private internal state from the outside world.

```jsx
function createToggleSwitch() {
  let toggleSwitch = "off";

  return {
    flickSwitch: function () {
      toggleSwitch = toggleSwitch === "off" ? "on" : "off";
    },

    logState: function () {
      console.log("state: ", toggleSwitch);
    }
  };
}

const toggle = createToggleSwitch();
toggle.logState();
toggle.flickSwitch();
toggle.logState();
toggle.flickSwitch();
toggle.flickSwitch();
toggle.logState();

/*
Output:

state:  off
state:  on
state:  on
*/
```

As you can see the internal `toggleSwitch` variable is completely inaccessible to anything outside of the `createToggleSwitch`. However we can still manipulate the toggle switch state through accessor functions that have closures capturing the lexical scope of their parent function `createToggleSwitch()`.

### 5.3 Currying

Currying is a closure pattern that transforms a function that takes multiple arguments into a sequence of functions, each taking a single argument. Each function captures the lexical scope of it’s parent.

This pattern allows us chain and compose multiple functions whilst increasing code reuse and improving readability.

In this example we want to use the process of currying to apply different discounts to a customer depending on the customer type.

```jsx
const discountRules = {
  staff: 10,
  manager: 20,
  customer: 0
};

function calcDiscount(percentDiscount) {
  return function (purchasePrice) {
    return (purchasePrice * (100 - percentDiscount)) / 100;
  };
}

function createDiscount(customerType) {
  const discountAmount = discountRules[customerType];
  return calcDiscount(discountAmount);
}

const applyManagerDiscount = createDiscount("manager");
const applyStaffDiscount = createDiscount("staff");
const applyCustomerDiscount = createDiscount("customer");

console.log("manager final price: ", applyManagerDiscount(45));
console.log("staff final price: ", applyStaffDiscount(45));
console.log("customer final price: ", applyCustomerDiscount(45));

/*
Output:

manager final price:  36
staff final price:  40.5
customer final price:  45
*/
```

In the above example there are two curried functions, `createDiscount()` and `calcDiscount()`. Both have distinct functionality where `createDiscount()` selects the percentage discount depending on the customer type and `calcDiscount()` applies the actual price discount transformation to the purchase price. By chaining these functions together we then create the specialised discount functions, `applyManagerDiscount()`, `applyStaffDiscount()` and `applyCustomerDiscount()`.

It then becomes very intuitive to use and apply these specific discounts to the different customer types.

Another commonly confused but related concept to currying is ‘partial applications’. A partial application is the process of fixing or providing some arguments to a function, resulting in a new function with fewer parameters. 

It is actually a more general concept where currying is a specialised type of partial applications, which takes  multiple arguments and applies them to a sequence of functions each taking a single argument. 

If you're interested in learning more about partial applications, a quick Google search will yield plenty of excellent content to explore.

## 6. Conclusion

There are 3 types of scope in JavaScript, Global, Function and Block. 

Global scope means that a given variable is accessible throughout the code base. Function scope means that a given variable is only accessible within a function. Block scope, which only applies to const and let variables, means that a given variable can be accessed within a block i.e. a group of statements enclosed within curly braces.

The type of scope that applies to a given variable is based on the location or position of a given variable in  the source code, this mechanism is known as the Lexical scope.

A closure allow us to capture a variable in a given lexical scope and reuse it in a different place from where it was defined regardless of where or when it is executed later.

There are many useful design patterns that employ closures this includes but is not limited to ‘event handling’, ‘encapsulation’ and ‘currying’.