카테고리 없음

클로저 관련 정리

surimi🍥 2024. 12. 25. 22:14
반응형

Lexical Scope

변수의 유효 범위를 결정하는 규칙.
코드가 실행될 때가 아니라 작성될 때 결정됨.

Lexical = 문맥
스코프 예시
let globalVar = "global"; // 글로벌 스코프

function outerFunction() {
  let outerVar = "outer"; // outerFunction의 렉시컬 스코프

  function innerFunction() {
    let innerVar = "inner"; // innerFunction의 렉시컬 스코프
    console.log(globalVar); // "global"
    console.log(outerVar);  // "outer"
    console.log(innerVar);  // "inner"
  }

  innerFunction();
}

outerFunction();
렉시컬 스코프 예시
var x = 1;

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

function foo() {
    var x = 10;
    bar();
}

foo(); // 1 -> 함수가 선언된 위치가 전역이기 때문에
bar(); // 1

 

함수를 어디서 호출했는지에 따라 바뀌는걸 dynamic scope라고 하는데, 대부분의 언어는 lexical scope(=static scope)를 따른다.
  • 함수 객체의 내부 슬롯 [[Environment]]에는 상위 스코프의 참조값이 저장된다.
  • 전역에 선언된 함수에는 내부 슬롯 [[Environment]]에 전역 스코프의 참조값이 저장됨.
  • bar 함수의 내부 슬롯 [[Environment]]에는 전역 스코프의 참조가 저장되어 x는 1의 값을 가지고 있음.

Lexical Environment

실행 컨텍스트의 일부분으로, 변수를 저장하고 관리.
각 함수가 호출되거나 블록 진입시(=실행 컨텍스트가 생성될 때마다) 새로운 렉시컬 환경이 생성된다.

렉시컬 환경의 두 가지 주요 부분:

  1. Environment Record: 현재 스코프의 모든 변수와 함수 저장.
  2. Outer Lexical Environment Reference (외부 렉시컬 환경 참조): 상위 스코프 참조.

Execution Context

JS 코드가 실행되는 환경.

실행 컨텍스트를 생성하는 코드

  1. Global
    • 코드가 처음 실행될 떄 생성됨.
    • 전역 객체와 this 포함.
    • JS 프로세스가 종료될 때까지 유지.
  2. Function
    • 함수가 호출될 때마다 실행됨.
    • 함수의 arguments, local variables, this를 포함.
    • 함수 실행이 완료되면 컨텍스트 스택에서 제거됨.
  3. Eval
    • eval 함수가 실행될 때 생성됨.
  4. Module
    • 모듈 내부에 존재하는 코드.
this = 현재 실행 컨텍스트에서 참조하는 객체

실행된 순서대로 실행 컨텍스트 스택에 쌓인다.
맨 밑바닥에는 Global EC가 프로세스 종료될 때까지 깔려있다.

First-class Object (일급 객체) (=First-class Citizen)

  1. 변수에 할당할 수 있다.
  2. 다른 함수의 인자로 전달할 수 있다.
  3. 다른 함수의 반환값으로 사용할 수 있다.
  4. 동적으로 생성할 수 있다.
  • JS에서는 함수가 일급 객체이다.
  • 함수가 변수에 할당될 수 있고, 다른 함수의 인자로 전달되거나 반환값으로 사용될 수 있음.

Closure

"A closure is the combination of a function and the lexical environment within which that function was declared."
클로저는 함수와 그 함수가 선언된 렉시컬 환경과의 조합이다.
  • 여기서 말하는 렉시컬 환경은 함수가 정의된 스코프(=상위 스코프, 실행 컨텍스트의 렉시컬 환경)
  • 모든 함수는 상위 스코프를 기억하므로, 어디서 호출되든 상위 스코프의 식별자(변수, 함수, 클래스 등)를 참조할 수 있다. (lexical scope)
  • 중첩 함수(Inner 함수)이면서 상위 스코프의 식별자를 참조하지 않거나, 참조 하더라도 Outer 함수보다 먼저 소멸되면 클로저라고 하지 않는다.
function foo() {
		const x = 1;
		const y = 2;
	
		// 클로저
		// 중첩 함수 bar는 외부 함수보다 더 오래 유지되며 상위 스코프의 식별자를 참조한다.
		function bar() {
			debugger;
			console.log(x);
		}
		return bar;
	}

	const bar = foo();
	bar();

Closure 활용

  • 정보 은닉
    소멸될 외부 함수에 변수를 선언하고 클로저 함수를 반환하게 해두면 반환된 클로저 함수로만 변수에 접근할 수있다.

예시

const inc = (function () {
  let counter = 0; // 외부에서 접근할 수 없음.

  // Closure
  return function () {
    return ++counter;
  };
})();

console.log(inc()); // 1
console.log(inc()); // 2
console.log(inc()); // 3
// 함수를 반환하는 고차 함수
// 이 함수는 카운트 상태를 유지하기 위한 자유 변수 counter를 기억하는 클로저를 반환한다.
const counter = (function() {
  // 카운트 상태를 유지하기 위한 자유 변수
  let counter = 0;
  // 함수를 인수로 전달받는 클로저를 반환
  return function(aux) {
    // 인수로 전달받은 보조 함수에 상태 변경을 위임한다.
    counter = aux(counter);
    return counter;
  };
}());

// 보조 함수
function increase(n) {
  return ++n;
}

// 보조 함수
function decrease(n) {
  return --n;
}

// 보조 함수를 전달하여 호출
console.log(counter(increase)); // 1
console.log(counter(increase)); // 2
// 자유 변수를 공유한다.
console.log(counter(decrease)); // 1
console.log(counter(decrease)); // 0
  • 외부에서 접근 불가능한 변수의 값을 변경할 함수를 넣어줄 수 있다.
반응형