Nested function scope

let a = 10
function outer(){
    let b = 20
    function inner() {
        let c = 30
        console.log(a,b,c);
    }
    inner()
}

outer() //logs 10,20,30

This uses lexical scoping - the ability for a function scope to access variables from the parent scope. The variable lookup starts with the innermost function and moves outwards until it reaches the global scope.

Nested functions have access to variables declared in their own scope as well as variables declared in the outer scope.

Closure

In JS, when we return a function from another function, we are effectively returning a combination of the function definition along with the function’s scope.

This would let the function definition have an associated persistent memory which could hold on to live data between executions.

That combination of the function and its scope chain is what is called a closure.

In other words - A closure is created when a function is returned from another function. In that returned function we have access to the variables in its scope. The inner function has access to variables in the outer function scope even after the outer function has finished execution

//Example 1 - not using closure 
function outer() {
    let counter = 0
    function inner() {
        counter++
        console.log(counter);
    }
    inner()
}

outer() // outputs 1
outer() // outputs 1 again

//Example 2 - using closure
function outer() {
    let counter = 0
    function inner() {
        counter++
        console.log(counter);
    }
    return inner
}

const fn = outer()

fn() // outputs 1
fn() // outputs 2 (this remembers that counter value is now 1)

In Example 2, when we call outer() we don't invoke the inner() function right away, we return the function. In doing so we can assign the result (which would be the inner() function) of invoking the outer() function to a variable fn. When fn() now has access to variables in the inner()functions scope which includes the value of counter.

Currying

Currying is a process in functional programming in which we transform a function with multiple arguments into a sequence of nested functions that take one argument at a time.