Closures in Javascript
Tags: javascript, typescript, node
Published: 2023-03-19 11:37:46

In JavaScript, a closure is an inner function that is able to access the variables of its outer (enclosing) function. The inner function is provided with three separate scopes: access to its own scope, access to the variables of the outer function, and access to the global variables. What are scopes?

When a function is executed in JavaScript, a list of objects is consulted to locate any identifiers (variables) used within the function - this list of objects is known as the scope chain. The scope chain is organized such that the local scope is checked first, then followed by each other scope in the chain. If a variable is not found in the local scope, it will be searched for in the global scope and any scopes that exist between the two. If the variable is not found in any of the scopes, a ReferenceError will be thrown.

```typescript
function outerFunc() {
  let outerVariable = 10;

  function innerFunc() {
    let innerVariable = 20;
    console.log(outerVariable);
    console.log(innerVariable);
  }

  innerFunc();
}

outerFunc(); // 10, 20
```

The code snippet above demonstrates how a scope chain works in JavaScript. The `outerFunc()` function has a local scope, which contains a variable called outerVariable. The `innerFunc()` function exists within the scope of `outerFunc()`, and it has its own local scope which contains a variable called innerVariable. When `innerFunc()` is called, it has access to the outerVariable declared in `outerFunc()'s` scope, as well as its own innerVariable. This is because of the scope chain - the `innerFunc()` function can access variables declared in the `outerFunc()` scope, as well as its own scope. When the code is executed, the outerVariable is logged first, followed by the innerVariable.

How does this relates to closures? The code snippet above is related to closures because the `innerFunc()` function is a closure. In this example, `innerFunc()` has access to the `outerFunction()'s` scope, as well as its own scope, since they are part of the same scope chain. The `innerFunc()` function can access the `outerFunction()'s` variables, but the outerFunction() cannot access the `innerFunc()'s` variables, since they are in a different scope.

```typescript
function assignApples(owner: string){
  let apples = 0;
  // outer scope, can only use apples
  return function doSomethingWithApple(){
    apples += 1;
    // inner scope, can use str & apples
    let str = `${owner} has ${apples} apples`;
    return str;
  }
}

```

The example above shows how closures can be used to generate private variables. The `assignApples()` function takes in an owner parameter and creates a local variable called apples with an initial value of 0. It then returns a function called `doSomethingWithApple()`, which has access to both the owner parameter (declared in the outer scope) and the apples variable (declared in the inner scope). The `doSomethingWithApple()` function is a closure, as it has access to the outer scope's variables, and can also modify the inner scope's variables. In this case, the `doSomethingWithApple()` function can increase the value of the apples variable by 1 each time it is called. The closure also allows the `doSomethingWithApple()` function to create a string with the owner's name and the number of apples they have, without exposing the apples variable to the outer scope.

```javascript
const sam = assignApples('sam');
const frodo = assignApples('frodo');

sam(); // sam has 1 apples
frodo(); // frodo has 1 apples
sam(); // sam has 2 apples
```

### How can we use closures

Closures can be used to create variables that can only be accessed and modified within a specific scope. This is useful for protecting sensitive data, such as API keys or user passwords, from being accessed or modified outside of the intended area. By using closures, developers can ensure that any data stored in these private variables remains secure.

```javascript
function createPasswordHandler(){
  let password = 'SECRET';
  
  return {
    setPassword: (newPassword: string) => {
      password = newPassword;
    },
    getPassword: () => {
      return password;
    }
  }
}

const passwordHandler = createPasswordHandler();
passwordHandler.getPassword(); // 'SECRET'
passwordHandler.setPassword('NEW SECRET');
passwordHandler.getPassword(); // 'NEW SECRET'
```

Closures can also be used to create variables that can only be accessed and modified within a specific scope. This is useful for protecting sensitive data, such as API keys or user passwords, from being accessed or modified outside of the intended area. By using closures, developers can ensure that any data stored in these private variables remains secure. Consider a function that adds a certain amount of money to a user's account balance:

```typescript
function addMoney(amount: number){
  let balance = 0;
  balance += amount;
  return balance;
}
```

The function above doesn't remember the balance after it is called, so it can't be used to implement undo/redo. However, if we use a closure, we can keep track of the changes that have been made:

```typescript
function addMoney(amount: number){
  let balance = 0;
  return function(){
    balance += amount;
    return balance;
  }
}

const add5 = addMoney(5);
add5(); // 5
add5(); // 10
add5(); // 15
```

Closures can be used to create functions that can be passed around and used in different contexts. This is useful for creating reusable components that can be used across different parts of an application. For example, consider a function that creates a counter:

```typescript
function createCounter(){
  let count = 0;
  return function(){
    count += 1;
    return count;
  }
}
```


The function above can be used to create any number of counters, which can be used in different parts of the application. For example, it can be used to create a counter for tracking the number of times a user has logged in:

```typescript
const loginCounter = createCounter();
loginCounter(); // 1
loginCounter(); // 2
loginCounter(); // 3
```

For example, it can be used to track the number of times a certain feature has been used or the number of times a user has visited a certain page. It can also be used to track the number of items in a user's shopping cart or the number of views a certain video has had. All of these can be tracked using the same counter, which can be easily reused across different parts of the application.


Closures can be used to create private functions that keep the data they contain hidden from the outside world. This is beneficial for creating secure functions that can only be accessed by those who have permission. An example of this is a function that creates a personal data store which is only accessible by certain users.

```typescript
function createUserStore{
  let data = {};
  return function(key: string, value: any){
    if (value) {
      data[key] = value;
    } else {
      return data[key];
    }
  }
}

```

The `createUserStore` function can be used to store data that needs to be kept private from the outside. For example, it can be used to store user passwords or API keys that can only be accessed by certain users. Additionally, it can be used to store confidential data such as financial records or medical records that need to be kept secure. 

```typescript
const userStore = createUserStore();
userStore('userName', 'John Doe');
userStore('password', 's3cr3t');

userStore('userName'); // 'John Doe'
userStore('password'); // 's3cr3t'
```

To cap our findings today, closures are inner functions that have access to the outer (enclosing) function's variables—scope chain. Closures can be used to create private variables that can only be accessed and modified within a certain scope, create stateful functions that remember the values of the variables declared in the outer scope, create functions that can be passed around and used in different contexts, and create data-hiding functions that keep the data they contain private from the outside. Closures can be used to create secure functions that can only be accessed by certain users, create reusable components that can be used across different parts of an application, and implement features such as undo/redo.