ReasonJun

Javascript : Closure 본문

Frontend/Javasciprt

Javascript : Closure

ReasonJun 2023. 6. 8. 03:10
728x90

A closure is a fundamental concept in JavaScript that allows a function to retain access to variables from its outer (enclosing) lexical scope even after the outer function has finished executing. In simpler terms, a closure is a combination of a function and the lexical environment within which that function was declared.

 

To understand closures, it's important to grasp the concept of lexical scoping. In JavaScript, each function creates its own scope, and it can access variables and functions defined in its own scope, as well as variables and functions defined in its parent (outer) scopes. This nesting of scopes creates a chain of nested lexical environments.

When a function is defined within another function, the inner function forms a closure, capturing references to variables and functions from its outer scope. This allows the inner function to access and manipulate those variables even after the outer function has completed its execution.

 

Here's an example that demonstrates how closures work:

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

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

  return innerFunction;
}

const closure = outerFunction();
closure();
// Output: I am from the outer function

const text = 'hello';
function func() {
  console.log(text);
}
func(); // hello

function outer() {
  const x = 0;
  function inner() {
    console.log(`inside inner: ${x}`);
  }
  return inner;
}
const func1 = outer();
func1(); // inside inner: 0



//
const h1El = document.querySelector('h1');
const h2El = document.querySelector('h2');

let h1IsRed = false;
let h2IsRed = false;

h1El.addEventListener('click', (event) => {
  h1IsRed = !h1IsRed;
  h1El.style.color = h1IsRed ? 'red' : 'black';
});

h2El.addEventListener('click', (event) => {
  h2IsRed = !h2IsRed;
  h1El.style.color = h2IsRed ? 'red' : 'black';
});

// closure 
// State can be managed as one.
const createToggleHandler = () => {
  let isRed = false;
  return (event) => {
    isRed = !isRed;
    event.target.style.color = isRed ? 'red' : 'black';
  };
};

h1El.addEventListener('click', createToggleHandler());
h2El.addEventListener('click', createToggleHandler());

In this example, the outerFunction defines a variable outerVariable and declares an inner function innerFunction. The innerFunction is returned from the outerFunction. When we invoke outerFunction and assign the result to the closure variable, it captures the reference to the outerVariable within its lexical environment.

 

Later, when we call closure(), it still has access to the outerVariable even though the outerFunction has already finished executing. This is because the closure maintains a reference to the variables it needs, preserving their values even outside their original scope.

 

Closures are powerful in JavaScript as they enable various programming patterns, such as encapsulation, data privacy, and the creation of functions with persistent state. They are commonly used in scenarios like creating private variables, implementing function factories, and handling asynchronous operations.

 

It's important to note that closures have memory implications, as the variables captured within the closure's scope are not immediately garbage collected, even if they are no longer needed. This can lead to potential memory leaks if closures are not handled carefully, especially in long-lived applications.

 

// Hiding internal information and manipulating data through public functions (public, external)
// Encapsulation and Information Hiding
// Equivalent to the effect of using a class private field or method!
function makeCounter() {
  let count = 0; // Internal properties that cannot be accessed externally
  function increase() { // Internal function not accessible from outside
    count++;
    console.log(count);
  }
  return increase; // Set the accessible path.
}
const increase = makeCounter();
increase(); // 1
increase(); // 2
increase(); // 3

const counter1 = function() {
  const privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }
  return { // Set how functions and properties defined in counter1 can be used
    increment: () => {
      changeBy(1);
    },
    decrement: () => {
      changeBy(-2);
    },
    value: () => {
      return privateCounter;
    }
    
  };
};

class Counter {
  #count = 0; 
  increase() {
    this.#count++;
    console.log(this.#count);
  }
}
const counter = new Counter();
counter.increase(); // 1
counter.increase(); // 2
counter.increase(); // 3
728x90
Comments