[클로저] - 클로저

Donggu(oo)·2022년 11월 7일
0

JavaScript

목록 보기
24/49
post-thumbnail

클로저(closure)


클로저(closure)란?

  • 함수와 함수가 선언된 어휘적(코드가 어디서 실행되며 주변에 어떤 코드가 있는지, lexical 환경) 환경의 조합을 말한다. 이 환경은 클로저가 생성된 시점의 유효 범위 내에 있는 모든 지역 변수로 구성된다.
    1. 중첩함수(함수가 함수를 리턴하는 형태)이고 2. 리턴된 함수가 외부함수의 변수를 참조하면 클로저이다.
  • 렉시컬 환경 === 렉시컬 스코프

1. 클로저의 개념


  • 클로저는 외부 변수를 기억하고 이 외부 변수에 접근할 수 있는 함수를 의미한다.
  • 함수 실행이 끝나고 나면 함수 내부의 변수를 사용할 수 없는 일반적인 함수와는 달리, 외부 함수의 실행이 끝나더라도 외부 함수 내 변수가 메모리 상에 저장된다.(어휘적 환경을 메모리에 저장하기 때문)
  • 자신을 포함하고 있는 외부함수보다 내부함수가 더 오래 유지되는 경우, 외부 함수 밖에서 내부함수가 호출되더라고 외부함수의 지역 변수에 접근할 수 있는데 이러한 함수를 클로저라고 부른다.
  • 함수 outerFunc는 내부함수 innerFunc를 반환하고 생을 마감했다. 즉, 함수 outerFunc는 실행된 이후 콜스택(실행 컨텍스트 스택)에서 제거되었으므로 함수 outerFunc의 변수 x 또한 더이상 유효하지 않게 되어 변수 x에 접근할 수 있는 방법은 달리 없어 보인다. 그러나 위 코드의 실행 결과는 변수 x의 값인 10이다. 이미 life-cycle이 종료되어 실행 컨텍스트 스택에서 제거된 함수 outerFunc의 지역변수 x가 다시 부활이라도 한 듯이 동작하고 있다.
  • 아래 예제에서는 실행이 종료된 outer 함수 속 지역변수 x를 참조하고 있는 innerFunc 함수가 클로저가 된다.
const x = 1;
function outerFunc() {
  const x = 10;  // 지역변수 x 선언
  const innerFunc = function() {  // innerFunc함수 정의
    console.log(x);
  }
  return innerFunc;  // innerFunc함수를 그대로 리턴
}  // outer의 역할은 여기서 끝
const innerFuce = outerFunc();
innerFunc();  // outerFunc함수 내부에 있는 innerFunc함수를 실행한다.

2. 클로저의 활용


1) 데이터를 보존하는 함수

  • 클로저는 함수 실행이 끝나고 나면 함수 내부의 변수를 사용할 수 없는 일반적인 함수와는 달리, 외부 함수의 실행이 끝나더라도 외부 함수 내 변수가 메모리 상에 저장된다.(어휘적 환경을 메모리에 저장하기 때문)
  • 변수 add5 에는 클로저를 통해 리턴한 함수가 담겨 있다. add5는 외부 함수의 실행이 끝났음에도 adder함수에서 인자로 넘긴 5라는 값을 x 변수에 계속 담은 채로 남아있다.

2) 정보의 접근 제한(캡슐화)

  • 스코프로 value 값을 감싸지 않았다면, value 값은 전역 변수여야 한다. 그러나 makeCounter라는 함수가 value 값을 보존하고 있기 때문에 전역 변수 만들 필요가 없는 것이다.

side effect

  • side effect란 전역 변수에 의해 다른 함수 혹은 로직 등에 의해 의도하지 않은 변경을 초래하는 것을 말한다.
    -> 클로저를 통해 불필요한 전역 변수 사용을 줄이고, 스코프를 이용해 값을 보다 안전하게 다룰 수 있다.

3) 모듈화(모듈 패턴)

  • 모듈화란 함수 재사용성을 극대화하여, 함수 하나를 완전히 독립적인 부품 형태로 분리하는 것을 말한다.
  • 클로저를 통해 데이터와 메서드를 같이 묶어서 다룰 수 있다.
const makeCounter = () => {
  let value = 0;
  
  return {
    increase: () => {
      value = value + 1
    },
    decrease: () => {
      value = value - 1
    },
    getValue: () => value
  }
}

const counter1 = makeCounter();
counter1.increase();  // 1
counter1.increase();  // 2
counter1.decrease();  // 1
// getValue 메서드는 외부 함수에 선언된 value 값을 리턴하는 함수
counter1.getValue();  // 1

const counter2 = makeCounter();
counter2.decrease();  // -1
counter2.decrease();  // -2
counter2.dencrease();  // -3
// getValue 메서드는 외부 함수에 선언된 value 값을 리턴하는 함수
counter2.getValue();  // -3

3. 클로저 예제


1) 다음 코드를 실행했을 때 total의 값은 무언인지 고르세요.

let add = function(x) { // x = 1
  let sum = function(y) { // y = 6
    return x + y;
  }
  return sum;
}

let foo = add(1);  // foo === sum
foo(3);  // foo(3) -> add함수 내부에서 sum(3)
let total = foo(6);  // 7
  • 함수 add는 x를 매개변수로 하며, 함수 sum을 반환한다. add는 매개변수 x를 통해 전달받은 값을 내부 함수 sum에게 내려주는데, 여기서 sum은 외부 함수인 add의 변수 x에 접근할 수 있으므로 클로저이다.
  • total의 값인 foo(6), 은 add(1)(6)과 같으므로 total은 7이다.
  • 여기서 foo(3)은 실행되면 4가 반환되지만, 이 값은 어떠한 변수에도 할당이 되지 않았으므로 total에 아무런 영향을 미치지 않는다.

2) A, B, C, D 각각에 출력 될 내용으로 틀린 것을 고르세요.

var a = 0;
function foo() {
    var b = 0;
    return function() {
        console.log(++a, ++b);
    };
}

var f1 = foo();
var f2 = foo();

f1(); // --> A : 1 1
f1(); // --> B : 2 2 
f2(); // --> C : 3 1 
f2(); // --> D : 4 2 

// 보기
// A. 1 1
// B. 2 2 
// C. 1 1
// D. 4 2
  • 변수 a는 전역 변수, 변수 b는 지역 변수로 볼 수 있다. 변수 b는 함수 안에 선언되어 있기 때문에 f1()을 실행할 때, f2()를 실행할 때 각각 카운트가 올라가는 것이고, 변수 a는 함수 밖에 선언되어 있기 때문에 f1(), f2() 중 어떤 것을 실행해도 카운트가 올라갑니다.

0개의 댓글