TIL #23 클로져(Closure) 다시 공부하기 그런데 컨텍스트를 곁들인...

Ellie·2022년 7월 31일
0

TodayILearned

목록 보기
21/24

클로져는 예전에 공부했다고 생각했는데 다시 떠올리려고 보니까 명확하게 생각이 안난다... 또 컨텍스트랑 같이 결합해서 클로져를 다시 공부해보려고 한다. 기록하자... 까먹지 말자!

🔎 클로져란?

클로저는 먼저 예시로 공부해보겠다.

✔️ 값을 1씩 증가시키는 함수 만들기!

  • 값을 저장할 변수,
  • 1씩 증가시킬 함수

를 구현하면 된다.

let saveNumber = 1;

function increment() {
  return saveNumber++;
}

console.log(increment()); // 1
console.log(increment()); // 2

❗️ 이 코드의 문제점

let saveNumber = 1;

function increment() {
  return saveNumber++;
}

console.log(increment()); // 1
saveNumber = 200; // 중간에 값이 변경되었다! 전혀 값이 보호되지 않는다.
console.log(increment()); // 201

1씩 증가하기를 기대했는데 모종의 이유로 중간에 값이 바뀌어버렸을 경우 increment 함수는 saverNumber이라는 변수를 보호할 수 없다. 왜냐하면 saverNumber는 누구에게나 노출되고 접근 가능한 변수이기 때문이다. 그렇기 때문에 값을 보호하고자 하는 경우에는 접근이 불가능하게 만들어야 한다. 그렇다고 아예 함수 안에 변수를 넣는다면 계속 처음부터 고정된 값으로 다시 계산될 것이다.


❓그럼 어떻게 해결할 수 있을까?

값을 저장할 변수의 변경된 값을 기억하면서 외부에서 접근하지 못하게 보호하고 싶다면, 값을 반환하는 게 아니라 함수를 리턴 해주면 된다. 바로 클로져 를 이용하는 것이다.

function increment() {
  let saveNumber = 1;
  
  return function() {
  	return saveNumber++;
  } 
}

const inc = increment(); // 함수가 변수에 저장
inc();

console.log(inc()); // 1
console.log(inc()); // 2

함수 안쪽에서 함수가 만들어질 때 안쪽 함수가 바깥 함수의 변수에 접근하게 되면 그 변수를 클로져라고 하는 공간에 저장해둔다.

그리고 함수가 반환되면 saveNumber가 있던 increment함수의 지역 공간, 그러니까 선언된 변수 saveNumber은 없어지지만 안쪽 함수의 클로져라는 공간에 옮겨져 saveNumber가 유지되고 있다.

그래서 바깥 전체 함수가 호출될 때 변수 saveNumber에서 값을 참조하는 게 아니라 안쪽 함수의 클로져 공간에서 찾아 반환하는 것이다.


✔️ 클로져를 사용해야 하는 경우

보호되어야 하는 값이 필요한 경우에 사용한다.


🔎 컨텍스트 측면에서 클로져 분석하기!

// 글로벌 실행 컨텍스트 생성
function hello() {
  const greeting = "hi";
  
  return function () {
  	return console.log(greeting);
  }
}

const say = hello(); // 함수 실행 컨텍스트 생성됨
say();

지금까지 공부한 바로 위와 같은 경우는 greeting이라는 변수의 값을 보호하고자 한 경우에 사용한 것이다. 이 클로져 예시로 실행 컨텍스트에 대해 공부해보자.

EC.LE (=Execution Context Lexical Environment)

  • Global Execution Context
    • Lexical Environment
      • say: Function
        (hello 함수 안에 있던 이름 없는 함수가 지금 전역 컨텍스트에 say라는 이름으로 저장되었다.)
  • hello() Execution Context

    • Lexical Environment
      • greeting: "안녕하세요"
      • function: Function
        • [[Environment]]: hello() EC.LE
      • outer: Global EC.LE
  • say() Execution Context
    분명히 say()함수도 실행되었다. 이것은 어떻게 작동할까?

    • Lexical Environment
      • outer: hello() EC.LE
        (say함수는 이름없는 함수와 같기 때문에 say함수의 바깥은 hello함수이다. 그래서 hello함수의 변수를 참조할 수 있는 것이다!)

const say = hello(); 처럼 함수를 변수로 저장하는 이유는 함수 실행 컨텍스트는 한번 함수가 실행되고 종료되면 사라지기 때문이다. 그래서 이 함수 실행 컨텍스트가 사라지지 않도록 전역 컨텍스트 환경에 있는 변수에 저장해 메모리에 계속 담아놓는 것이다. (진짜 중간에 계속 왜 변수에 저장을 해야할까 궁금했는데 이 궁금증을 해결했다!)


함수는 자기자신이 선언될 때 현재 lexical environment를 자신의 [[environment]] 속성에 담아놓다가 함수 실행 컨텍스트를 생성할 때 다시 outer로 다시 복사해 넣어놓기 때문이다! 그래서 자신이 속해있던 실행 컨텍스트의 값을 참조할 수 있는 것이다.


추가: 타입스크립트의 경우?!

타입스크립트의 경우 클래스에서 private으로 구현하면 된다.

class MyObj {
  private saveNumber: number;
}
profile
정말로 아는 것인지 항상 의심하기

0개의 댓글