클로저

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

위를 보면 크게 와닿지가 않는다. 그럼 더 설명해보겠다.
클로저를 공부하기 앞서 실행컨텍스트에 대한 지식이 있으면 이해하기가 훨씬 쉽다.

const x = 1;

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

outerFunc();

innerFunc 함수는 outerFunc 함수 내부에서 중첩된 함수이다. 따라서, 외부 함수 outerFunc의 x 변수에 접근이 가능하다. 만약 innerFunc함수가 outerFunc의 함수의 내부에서 정의된 중첩 함수가 아니라면 innerFunc함수를 outerFunc함수의 내부에서 호출한다 하더라도 outerFunc 함수의 변수에 접근할 수 없다. (이해가 가지 않는다면 실행컨텍스트를 공부하고 오자! ! !)

const x = 1;

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

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

outerFunc();

접근할 수 없는 이유는 자바스크립트는 렉시컬 스코프를 따르는 프로그래밍 언어이기 때문이다.
자바스크립트 엔진은 함수를 어디서 호출했는지가 아니라 함수를 어디서 정의했는지에 따라 상위 스코프를 결정한다. 이것이 렉시컬 스코프(정적 스코프)라 한다.

함수 객체의 내부 슬롯 [[Environment]]

  • 함수는 자신의 내부 슬롯 [[Environment]]에 자신이 정의된 환경, 즉 상위 스코프의 참조를 저장한다.
  • [[Environment]]에 저장된 상위 스코프의 참조는 현재 실행 중인 실행 컨텍스트의 렉시컬 환경을 가리킨다.

클로저와 렉시컬 환경

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

위에 실행 결과를 예측해보면 outer함수는 중첩함수 inner를 반환하고 종료되므로 실행 컨텍스트는 실행 컨텍스트 스택에서 제거된다. 이때 outer 함수의 지역 변수 x와 변수 값 10을 저장하고 있던 outer 함수의 실행 컨택스트가 제거되었으므로 outer 함수의 지역 변수 x 또한 생명주기를 마감하기 때문에 위 코드 결과가 1을 출력할거 같지만 실제로 위 코드 결과는 10을 출력한다. 이미 생명 주기가 종료되어 실행 컨텍스트 스택에서 제거된 outer 함수의 지역변수 x가 다시 부활한것처럼 동작한다.

이처럼 외부 함수보다 중첩 함수가 더 오래 유지되는 경우 중첩함수는 이미 생명주기가 종료한 외부 함수의 변수를 참조할 수 있다. 이러한 중첩 함수를 우리는 클로저라고 부른다 !

클로저는 중첩 함수가 상위 스코프의 식별자를 참조하고 있고 중첩 함수가 외부 함수보다 더 오래 유지되는 경우에 한정하는 것이 일반적이다.

자유변수: 클로저에 참조되는 상위 스코프의 변수

클로저의 활용

클로저는 상태를 안전하게 변경하고 유지하기 위해 사용한다. 즉, 상태가 의도치 않게 변경되지 않도록 상태를 안전하게 은닉하고 특정 함수에게만 상태 변경을 허용하기 위해 사용한다.

let num = 0;
const increase = function () {
   return ++num;
};
console.log(increase()); // 1
console.log(increase()); // 2
console.log(increase()); // 3
num == 100;
console.log(increase()); // 101

위 코드 문제점
카운트 변수가 전역 변수를 통해 관리하므로 언제든지 누구나 접근 및 변경이 가능하다는 것이 문제 !

해결 1. 카운트 변수를 전역에서 지역 변수로 변경
--> 함수가 호출될 때마다 초기화되므로 카운트 변수 값이 변경되지 않으므로 해결x
해결 2. 클로저 사용

즉시 실행 함수 사용

const increase = (function () { // 즉시 실행 함수
   let num = 0;
   return function () {
       return ++num; // increase 함수의 변수 참조, 생명 주기 더 김 -> 클로저
   };
}());

console.log(increase()); // 1
console.log(increase()); // 2
console.log(increase()); // 3
const counter = (function () { // 즉시 실행 함수
   let num = 0;
   return { // 객체 리터럴을 반환 --> 객체 리터럴은 스코프 생성 x --> 메서드들의 상위 스코프는 외부 함수
       increase() {
           return ++num;
       },
       decrease() {
           return num > 0 ? --num : 0;
       }
   };
}());
console.log(counter.increase()); // 1
console.log(counter.increase()); // 2
console.log(counter.decrease()); // 1
console.log(counter.decrease()); // 0

캡슐화와 정보 은닉

캡슐화 - 객체의 상태를 나타내는 프로퍼티와 프로퍼티를 참조하고 조작할 수 있는 동작인 메서드를 하나로 묶는 것
정보 은닉- 객체의 특정 프로퍼티나 메서드를 감출 목적으로 사용하는 것

자바스크립트는 접근 제한자를 제공하지 않는다. 자바스크립트의 객체의 모든 프로퍼티와 메서드는 기본적으로 외부에 공개되어 있다. (public)

function Person(name, age) {
   this.name = name; // public
   let _age = age; // private (지역 변수)
   this.sayHi = function () {
       console.log(`Hi! My name is ${this.name}. I am ${_age}.`);
   }; // 객체 생성될 때마다 중복 생성 --> 프로토타입 메서드로 변경하기
}
const me = new Person('Lee', 20);
me.sayHi; // Hi! My name is Lee. I am 20.
console.log(me.name); // Lee
console.log(me._age); // undefined
profile
꺾여도 하는 마음

0개의 댓글