js 클로저

Duboo·2023년 9월 19일
0

자바스크립트

목록 보기
7/7
post-thumbnail

클로저는 독립적인 (자유) 변수를 가리키는 함수이다. 또는, 클로저 안에 정의된 함수는 만들어진 환경을 ‘기억한다’.- 출처: MDN

클로저 (Closure)

바로 본론으로 넘어가자면 클로저는 스코프의 연장 개념으로 볼 수 있는데
클로저(closure)는 내부 함수가 외부 함수의 맥락(context)에 접근할 수 있는 것을 가리킨다고 합니다.
다른 말로 클로즈는 외부 함수의 변수에 접근할 수 있는 내부 함수라고 생각할 수 있습니다.

먼저 우리는 자바스크립트 함수 안에는 또 다른 함수를 선언할 수 있다는 걸 알아야 하는데,
그렇다면 함수 안의 또 다른 함수(내부 함수)와 밖에 함수(외부 함수)라는 게 존재하는 이유를
이해할 수 있을 것이며 여기서 함수 안의 또 다른 함수 내부 함수를 집중해서 살펴봅니다.


내부, 외부 함수를 정리하기 전에 스코프에 대한 개념을 다시 한번 잡고 넘어간다.

밑의 예제를 먼저 보고 innerFn 함수에서 접근할 수 있는 Scope는 총 몇 개인지 알 수 있다면 클로저를 이해할 수 있다.

function outerFn() {
  let outerVar = 'outer';
  console.log(outerVar);

  function innerFn() {
    let innerVar = 'inner';
    console.log(innerVar);
  }
}

let globalVar = 'global';
outerFn();


전역, 외부함수, 자기 자신(내부 함수) 총 3개

다음 밑의 코드의 결과는?

function outerFn() {
  let outerVar = 'outer';
  console.log(outerVar);

  function innerFn() {
    let innerVar = 'inner';
    console.log(innerVar);
  }
  return innerFn;
}

outerFn(); // 결과는??
// outerFn을 실행()했기 때문에 당연히 outerFn 함수안에 있는 console.log(outerVar)가
// 실행이 된다는건 바로 알 수 있다
// 하지만 여기서 한가지의 다른 값이 더 나오는데 바로 밑의 innerFn 함수이다.

outer // <- outerFn() 의 결과

ƒ innerFn() {                 
    let innerVar = 'inner';  // <- outerFn() 의 또 다른 결과
    console.log(innerVar)
  }

innerFn 함수 안의 console.log(innerVar)이 실행이 되는 게 아닌 함수 그 자체가 리턴되는 걸 볼 수 있는데 그렇다면 outerFn()을 한 번 더 실행()하면 어떻게 될까?

function outerFn() {
  let outerVar = 'outer';
  console.log(outerVar);

  function innerFn() {
    let innerVar = 'inner';
    console.log(innerVar);
  }
  retrun innerFn;
}

outerFn()(); // 결과는??

outerFn()을 했을 때 값이 콘솔 창에 outer가 찍히는 것 말고도 innerFn이라는 함수 자체가 나왔으니까 예상을 할 수 있겠지만 값은 outer, inner이다.

즉 outter()의 값이 outer와 innerFn 함수가 나오기 때문에 뒤에 나온 함수를 다시 한번 실행한 것으로 이해할 수 있다 - 리턴 값으로 함수 그 자체를 넣어줄 수 있다는 걸 여기서 이해하자

마지막으로 한 가지 케이스를 더 보고 넘어가자

function outerFn() {
  const outerVar = 'outer';
  console.log(outerVar);

  function innerFn() {
    const innerVar = 'inner';
    console.log(innerVar);
  }
  return innerFn;
}

let innerFn = outerFn();    // outerFn의 실행한 값을 inner라는 변수에 넣어줬다
//이때 outerFn을 실행했기 때문에 콘솔창에 outer가 한번 찍힌다.
innerFn(); // 이때 inner라는 변수를 실행한 결과는 무엇일까??

결과는 innerFn 함수를 실행한 것과 동일한 inner라는 텍스트를 콘솔 창에 찍어내는 걸 확인할 수 있다.

이걸 보고 우리는 스코프와 클로저의 개념이 다르지 않다는 걸 알 수 있는데
이제 다시 한번 클로저가 무엇인지 확인하자면?

내부 함수에서 즉, 클로저에서는 외부 함수의 변수, 전역 변수(글로벌), 지역변수에 모두 접근 가능

위의 내용 자체가 클로저라는 정의라고 생각하면 된다.

  • 내부 함수 - 함수의 안에 들어간 함수(클로저) < - 위에서도 언급했지만 얘가 그냥 클로저
  • 외부 함수 - 안에 다른 함수를 포함하고 있는 함수

클로저의 사용

자바스크립트를 포함한 모든 프로그래밍 언어들의 메서드 및 여러 가지 함수들은 이유 없이 만들어지지 않습니다. 그 말인 즉, 코드를 더 사용하기 편하기 위해서 만들어졌다는 것

우리가 어떤 A라는 함수를 작성하고자 하는데 그 함수의 기능으로 B라는 함수를 사용하고 싶은데 만약 B라는 함수를 함수의 바깥에 즉, 전역에 선언을 하게 된다면 어떻게 될까??

B라는 함수를 전역에 만들어줬기 때문에 응집성이 떨어질 뿐만 아니라 A라는 함수 말고도 여러 가지 방해 요소들이 B라는 함수를 건들 수 도 있지 않을까??

그렇기 때문에 우리는 A라는 함수 안에 B라는 함수를 만들어 두면 B라는 함수는 A함수만이 사용할 수 있는 함수가 된다.


밑의 예제를 통해서 다시 한번 클로저에 대해서 알아보자

function outerFn() {  
  const outerVar = 'outer'; 

  function innerFn() {  
    console.log(outerVar);  
  }
  innerFn();                  
}

outerFn();  // outer

위의 outerFn()의 결과는 'outer'가 나오는 걸 직접 콘솔 창에서 확인할 수 있다.
여기서 우리가 알 수 있는 건 내부 함수에 outerVar이라는 지역 변수가 존재하지 않는다면??
자바스크립트는 inner함수를 포함하고 있는 outerFn라는 바깥의 함수에서 outerVar라는 변수를 찾는다는 것이다.

즉, 내부 함수에서 외부 함수에 접근을 하는 게 가능하다는 것이다. <- 기초이면서도 핵심 개념

한 가지 다른 예를 들어보면 조금 더 이해를 할 수 있을 것이다.

function outerFn() {  //외부 함수
  let outerVar = 'outer'; // 외부 함수의 변수

  return function innerFn() { 
    console.log(outerVar);
  }
}

let someFn = outerFn(); // 이 코드의 내용은 outerFn함수를 실행한 결과를 someFn라는 변수에 담아준 뒤
someFn(); // inner라는 함수를 ()실행한걸로 볼 수 있다.

어떤 값이 나오는지 예상이 힘들 수도 있는 게 outerFn라는 함수는 안에 내부 함수를 '리턴'했다(우리는 리턴을 하게 되면 그 함수가 죽는다는 걸 이미 알고 있다.

그런데 우리가 someFn라는 변수에 담긴 outerFn라는 함수를 실행한 결과는 console.log(outerVar)가 된다. // outer

즉, 외부 함수는 이미 죽었음에도 불구하고 외부 함수가 가지고 있는 외부 함수의 변수에 내부 함수가 접근이 가능하다는 것이다.

    1. 내부 함수를 포함하고 있는 외부 함수의 접근할 수 있을 뿐만 아니라 외부 함수가 종료된 이후에도 외부 함수에 접근할 수 있다.
    1. 자신을 포함하고 있는 외부 함수의 인자, 지역 변수 등을 함수가 종료된 후에도 사용할 수 있는 변수를 자유 변수(free variable)라고 부른다고 합니다.

분명 스코프라는 이름을 만들어서 사용하는 이유가 있을 것이다. 그 이유를 알아보자

const closureTest = name => {
  const say = 'Hi';
  const test = `${say} ${name}`;
  return function() {
    console.log(test);
  };
}

const hyun = closureTest('hyun');
const hyun2 = closureTest('hyun');
const yoon = closureTest('yoon');

hyun(); // Hi hyun
hyun2(); // Hi hyun
yoon(); // Hi yoon

console.log(hyun === hyun2); // false 같은 값이 나오지만 같지 않다. 즉, 다른 주소이다?

closureTest 함수에 각각 다른 인자를 넣어서 변수에 넣은 뒤 각 변수들을 실행해봤다.
결과는 보다싶이 name 변수가 동적으로 바뀌어서 변수에 담기고 있다.

클로저를 사용하여 Private variable를 만들 수 있다고 한다.
추가로 정리한 뒤 내용 수정 필요

profile
둡둡

0개의 댓글