• Blog
  • Projects
  • Resume
profile_image

[JavaScript] 클로저

SubjectJavaScript

2021.01.19

정의

  1. 클로저는 함수와 함수가 선언된 어휘적 환경의 조합.
  2. 클로저를 이해하려면 자바스크립트가 어떻게 변수의 유효범위를 지정하는지(Lexical scoping)를 먼저 이해해야.


어휘적 범위 지정(Lexical scoping)

function init() {
  var name = 'Mozilla'; // name은 init에 의해 생성된 지역 변수이다.
  function printName() {
    // printName() 은 내부 함수이며, 클로저다.
    console.log(name); // 부모 함수에서 선언된 변수(name)를 사용한다.
    // + 만약 printName()가 자신만의 name변수를 가지고 있었다면, name대신 this.name을 사용했을 것
  }
  printName();
}
init();
  • 위 코드를 실행하면 printName() 함수 내의 console.log()문이 부모 함수에서 정의한 변수 name의 값을 성공적으로 출력함.
  • 이 예시를 통해 함수가 중첩된 상황에서 파서가 어떻게 변수를 처리하는지 알 수 있다.
    이는 어휘적 범위 지정(lexical scoping)의 한 예다.
  • lexical ?
    어휘적 범위 지정(lexical scoping) 과정에서 변수가 어디에서 사용 가능한지 알기 위해 그 변수가 소스코드 내 어디에서 선언되었는지 고려한다는 것을 의미
  • 중첩된 함수는 외부 범위(scope)에서 선언한 변수에도 접근할 수 있다.


클로저(Closure)

예시 1

function makeFunc() {
  var name = 'Mozilla';
  function printName() {
    console.log(name);
  }
  return printName;
}

var myFunc = makeFunc(); // myFunc변수에 printName 리턴함
// 유효범위의 어휘적 환경을 유지
myFunc(); // 리턴된 printName 함수를 실행 (name 변수에 접근)
  • 몇몇 프로그래밍 언어에서, 함수 안의 지역 변수들은 그 함수가 처리되는 동안에만 존재.
    makeFunc() 실행이 끝나면 (printName 리턴되고 나면)
    name 변수에 더 이상 접근할 수 없게 될 것으로 예상하는 것이 일반적.
  • 자바스크립트의 경우엔 다름.
    1. 자바스크립트는 함수를 리턴하고, 리턴하는 함수가 클로저를 형성하기 때문.
    2. 클로저는 함수와 함수가 선언된 어휘적 환경의 조합이다.
    3. 클로저가 생성된 시점의 유효 범위 내에 있는 모든 지역 변수로 구성된다.

예시 2

function makeAdder(x) {
  var y = 1;
  return function (z) {
    y = 100;
    return x + y + z;
  };
}

var add5 = makeAdder(5);
var add10 = makeAdder(10);
//클로저에 x와 y의 환경이 저장됨
/* 
    ▶ add5, add10 둘 다 리턴 시, makeAdder의 리턴 값은 함수. (return function (z) {})
        - function (z) {} 안까지는 접근한게 아니라 보는게 맞을 듯.
        - 매개변수 x 값은 저장되어 있는 상태겠지 add5, add10은.
*/

console.log(add5(2)); // 107 (x:5 + y:100 + z:2)
console.log(add10(2)); // 112 (x:10 + y:100 + z:2)
//함수 실행 시 클로저에 저장된 x, y값에 접근하여 값을 계산
/* 
    ▶ add5, add10를 실행하려면 function (z) {} 이니까,
        z값을 인자로 보내주면, function (z) {} 안에 있는 리턴 값이 나올 것.        
*/
  • add5와 add10은 둘 다 클로저.
    • 이들은 같은 함수 본문 정의를 공유하지만 서로 다른 맥락(어휘)적 환경을 저장한다.
    • 함수 실행 시 add5의 맥락적 환경에서 클로저 내부의 x는 5 이지만 add10의 맥락적 환경에서 x는 10이다.
      또한 리턴되는 함수에서 초기값이 1로 할당된 y에 접근하여 y값을 100으로 변경한 것을 볼 수 있다. (x값도 동일하게 변경 가능하다.)
    • 이는 클로저가 리턴된 후에도 외부함수의 변수들에 접근 가능하다는 것을 보여주는 포인트이며
      클로저에 단순히 값 형태로 전달되는 것이 아니라는 것을 의미.

이게 맞는 걸까? 혼자 짜보는 코드

  • 이 로직은 어떻게 돌아가는 걸까? (화살표 함수를 이용해서 한번 만들어봄)
// 이 로직은 이렇게 돌아가는 걸까?
const testFunc = (x) => (y) => (z) => x + y + z;
//  			|--1--|
//         		        |--2--|
//                			  |---------3---------|

let testX = testFunc(1); // - 위 1번 구간
console.log('testX: ', testX); // testX:  [Function (anonymous)]

let testY = testX(2); // - 위 2번 구간
console.log('testY: ', testY); // testY:  [Function (anonymous)]

let testZ = testY(3); // - 위 3번 구간
console.log('testZ: ', testZ); // testZ:  6
  • 이렇게 이해한게 맞는건가. 잘모르겠다.


참고 자료

  • 클로저 - MDN