[JS인터뷰준비] Closure

Bonggus·2021년 10월 31일
0

자바스크립트

목록 보기
20/23
post-thumbnail

클로저

클로저는 자바스크립트 고유의 개념이 아니다. 함수를 일급 객체로 취급하는 함수형 프로그래밍 언어에서 사용되는 중요한 특성이다. MDN에서는 클로저를 다음과 같이 정의한다

클로저는 함수와 그 함수가 선언된 렉시컬 환경과의 조합이다.

const x = 1;
function outerFunc() {
  const x = 10;
  function innerFunc() {
  	console.log(x); // 10
  }
  innerFunc() 
}
outerFunc(); 

outerFunc 함수 내부에서 중첩 함수 innerFunc가 정의되고 호출되었다. 이때 중첩함수 innerFunc의 상위 스코프는 외부함수 outerFunc의 스코프다. 따라서 innerFunc 내부에서 자신을 포함하고 있는 외부함수 outerFunc의 변수에 접근할 수 있다.

const x = 1;
function outerFunc() {
  const x = 10;
  innerFunc() 
}

function innerFunc() {
  console.log(x); // 1
}
outerFunc(); 

innerFunc 함수가 outerFunc 함수 내뷍서 정의된 중첩함수가 아니라면, innerFunc 함수를 outerFunc 함수의 내부에서 호출하더라도 outerFunc 변수에 접근이 불가하다.

이러한 이유는 자바스크립트가 렉시컬 스코프를 따르는 프로그래밍 언어이기 때문이다.

1. 렉시컬 스코프

자바스크립트 엔진은 함수를 어디서 호출했는지가 아니라 함수를 어디에 정의했는지에 따라 상위 스코프를 결정한다. 이를 렉시컬스코프(정적 스코프)라고 한다.

const x = 1;
function foo() {
  const x = 10;
  bar() 
}

function bar() {
  console.log(x); 
}

foo(); // 1
bar(); // 1

foo, bar 모두 전역에서 정의된 함수이다. 함수의 상위 스코프는 어디서 정의되었는지에 따라 결정되므로 두 함수의 상위 스코프는 전역이다. 함수를 어디서 호출하는지는 함수의 상위 스코프 결정에 어떠한 영향도 주지 못한다. 즉, 함수의 상위 스코프는 함수를 정의한 위치에 의해 정적으로 결정되고 변하지 않는다.

렉시컬 환경은 자신의 "외부 렉시컬 환경에 대한 참조"를 통해 상위 렉시컬 환경과 연결된다(스코프체인). 함수의 상위 스코프를 결정한다는 것은 렉시컬 환경의 외부 렉시컬 환경에 대한 참조에 저장할 참조값을 결정한다는 것과 같다. 렉시컬 환경에 대한 참조에 저장할 참조값이 바로 상위 렉시컬 환경에 대한 참조이며, 이것이 바로 상위 스코프이기 때문이다.

렉시컬 환경의 외부 렉시컬 환경에 대한 참조에 저장할 참조값, 즉 상위 스코프에 대한 참조는 함수 정의가 평가되는 시점에 함수가 정의된 위치에 의해 결정된다. 이것이 바로 렉시컬스코프이다.

2. 클로저

자바스크립트에는 함수 스코프가 있다. 함수 내부에서 정의된 변수라면 함수의 어느 부분에서든 접근할 수 있다. 즉, 내부함수에서 자신을 포함하는 외부함수의 스코프에 접근할 수 있다는 이야기이다.

function outerFunc() {
  let a = 10;
  function innerFunc() {
    let b = 5;
    let c = 6;
    a = a + b+ c;
  	console.log(a); // 21
  }
  return innerFunc;
}
const newInner = outerFunc();
newInner()

innerFunc에서는 outerFunc에 접근할 권한을 갖는다. 그러나, 내부함수가 외부함수보다 오래 살아남은 경우에 외부(상위스코프)에 있던 변수들은 어떻게 처리되는 것인가?

function outerFunc() {
  let a = 10;
  function innerFunc() {
    let b = 5;
    let c = 6;
    a = a + b+ c;
  	console.log(a); 
  }
  return innerFunc;
}
const newInner = outerFunc();
newInner() // 21

실행행순서는 newInner() -> outer() -> inner() 순서로 실행된다. 그러나 결과를 보면 outer 함수가 이미 실행되서 메모리에서 해제되어도, inner는 외부함수의 변수에 접근 가능하다. 함수는 자신을 포함하는 함수의 스코프에 접근이 가능하기 때문이다. 조금더 함수가 정의되었을때, 외부 스코프를 참조하게 되기에 접근이 가능한 것이다.

3. 클로저 활용

const person = (function() {
  let age =  15;
  return {
    getAge: function() {
      console.log(age);
      return age;
    },
    setAge: function(val) {
      age = val;
      console.log(age);
    }
  }
})()

person.getAge(); // 15
person.setAge(20); // 20

person.age = 30;
person.getAge(); // 15

person.age = 30;를 통해서는 함수 안의 변수에 접근하지 못한다. 이런식으로 클로저를 활용하면 비공개 데이터를 가진 객체를 만들 수 있다(상태변경, 은닉). 클로저는 상태(변수)를 은닉해서 특정 함수에게만 상태 변경을 허용하여 상태를 안전하게 변경하기 유지하게 해준다.

4. 읽을거리

const useState = function(initalValue = undefined) {
  let _state = initalValue;
  function state () {
    return _state
  }
  function setState(newStateValue) {
    _state = newStateValue;
  }

  return  [state, setState];
};

module.exports = { useState };

정리

  • 자바스크립트 내부 함수에서 자신을 포함하는 외부함수에 접근 가능하다
  • 내부 함수가 살아있는 상태에서 외부 함수가 파괴되면 외부함수의 변수들에 대한 접근 권한은 '내부함수'만 가지게 된다.
  • 이렇게 폐쇄된 공간에 대한 접근 권한을 가진 함수가 클로저이다.

출처

profile
프론트엔드

0개의 댓글