[코어자바스크립트 스터디] - 5

Empu·2024년 8월 25일

javaScript

목록 보기
4/5

05. 클로저

클로저란?

  • 클로저는 여러 함수형 프로그래밍 언어에서 등장하는 보편적인 특징.
  • 서적들에서 클로저
    • 자신을 내포하는 함수의 컨텍스트에 접근할 수 있는 함수
    • 함수가 특정 스코프에 접근할 수 있도록 의도적으로 그 스코프에서 정의하는 것
    • 함수를 선언할 때 만들어지는 유효범위가 사라진 후에도 호출할 수 있는 함수
    • 이미 생명 주기상 끝난 외부 함수의 변수를 참조하는 함수
    • 자유변수가 있는 함수와 자유변수를 알 수 있는 환경의 결합
    • 로컬 변수를 참조하고 있는 함수 내의 함수
    • 자신이 생성될 때의 스코프에서 알 수 있었던 변수들 중 언젠가 자신이 실행될 때 사용할 변수만을 기억하여 유지시키는 함수
  • MDN - 함수와 그 함수가 선언될 당시의 lexical environment의 상호관계에 따른 현상
  • 외부 함수보다 중첩 함수가 더 오래 유지되는 경우 중첩 함수는 이미 생명 주기가 종료한 외부 함수의 변수를 참조할 수 있다 → 중첩 함수를 클로저라고 한다
    • 상위 함수보다 하위 함수가 더 오래 살아 있는 경우
const x = 1;

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

const innerFunc = outer(); // 3
innerFunc() // 4

// 답 -> 10

// 3 outer 함수호출하면 중첩함수 inner 함수를 반환하며 생명주기 마감
// 지역변수와 값은 제거 되어서 접근 할 수 없을 것 같은데 10이 부활한듯 동작하네?
// 위에서 말한 외부함수보다 중첩함수가 더 오래 유지되는 함수가 생명주기가 끝난 외부 함수의 변수를 참조하네
// 이거시 클로저
// inner 함수는 전역변수 innerFunc에 의해 참조가 되고 있기에 가비지 컬렉션의 대상 X

//메모
// outer environment ref 는 실행될때 [[environment]]를 참조한다. 
  • 함수 객체의 내부 슬롯 [[ Environment ]] → 자신이 정의된 환경, 즉 상위 스코프의 참조를 저장한다. →(함수가 정의된 환경과 호출된 환경이 다를수 있기에 정의된 환경 즉 상위 스코프 기억)
    1. outer 함수가 평가되어 함수를 생성할때 현재 실행 컨텍스트의 렉시컬 환경, 즉 전역 렉시컬 환경을 outer 함수 객체의 [[ Environment ]] 내부 슬롯에 상위 스코프로서 저장한다.
    2. outer 함수를 호출하면 outer의 렉시컬 환경이 생성되고 앞서 [[ Environment ]]에 저장된 전역 렉시컬 환경을 outer 함수의 oer에 할당한다. 그리고 중첩함수 inner이 평가된다. 이때 inner는 [[ Environment ]] 에 실행중인 컨텍스트의 렉시컬 환경, outer 함수의 렉시컬환경을 상위 스코프로서 저장
    3. outer실행이 종료되면 inner함수를 반환하면서 생명 주기 종료. 실행컨텍스트 스택에서는 제거 되지만 outer 함수의 렉시컬 환경까지 소멸하는 것은 아니다 → outer 함수의 렉시컬 환경은 inner 함수의 [[ Environment ]] 내부 슬롯에 의해 참조되고 있고 inner함수는 전역 변수 innerFunc에 의해 참조 되고 있으므로 GC의 대상이 되지 않기 때문이다.
    4. outer 함수가 반환한 inner 함수를 호출하면 inner 함수의 실행 컨텍스트가 생성된다
      1. 중첩함수 inner가 외부함수 outer보다 더 오래 생존. 중첩함수 inner의 내부에서는 상위 스코프를 참조 할 수 있으므로 식별자 참조 가능.

클로저의 활용

  • 클로저는 상태를 안전하게 변경하고 유지하기 위해 사용한다 → 상태를 안전하게 은닉하고 특정 함수에게만 상태 변경을 허용하기 위해 사용
    • 만약 자바스크립트에 클로저라는 기능이 없다면 상태를 유지하기 위해 전역 변수를 사용할 수 밖에 없다. 전역 변수는 언제든지 누구나 접근할 수 있고 변경할 수 있기 때문에 많은 부작용을 유발해 오류의 원인이 되므로 사용을 억제해야 한다.
  • 데이터 캐싱(메모이제이션)
function cacheFunction(newNumber){
	// 매우매우 복잡한 계산. 사실 진짜 엄청나게 오래걸림 
	var number = 10 * 10
	
	return number * newNumber
}

console.log(cacheFunction(10));
console.log(cacheFunction(20)); ...
//만약 계산을 여러번 해야 한다? -> 반복적인 작업으로 인해 리소스를 많이 소비해야 한다
//아래의 방법으로 바꾸면? (with 클로저)

function cacheFunction(newNumber){
	// 매우매우 복잡한 계산. 사실 진짜 엄청나게 오래걸림 
	var number = 10 * 10
	
	function innerCacheFunc(newNumber){
		return newNumber * number
	}
	
	return innerCacheFunc;
}

const storedNum = cacheFunction() //여기서 number의 계산을 한번만 진행 
console.log(storedNum(10))
console.log(storedNum(20)) ... 

//number라는 값을 클로저가 기억. 
  • 반복적으로 특정값환을 해야 할때 (이것도 뭐 캐싱이긴함)
function cacheFunction2(){
	var number = 99;
	function increment(){
		number++
		return number
	}
	return increment;
}

const storedNum2 = cacheFunction2();
console.log(storedNum2()) // --> 100
console.log(storedNum2()) // --> 101

//increment는 number 값을 기억하고 있음
  • 정보 은닉, 캡슐화
    • closure라는 영어 단어는 사전적으로 ‘닫혀있음, 폐쇠성’ 정도의 의미를 가진다
    • 캡슐화는 객체의 상태를 나타내는 프로퍼티와 프로퍼티를 참조하고 조작할 수 있는 동작인 메서드를 하나로 묶는 것
    • 캡슐화는 객체의 특정 프로퍼티나 메서드를 감출 목적으로 사용하기도 하는데 이를 정보 은닉이라 한다

%클로저 주의점

  • 메모리 누수(memory leak)를 일으킬 수 있으므로 주의해야 한다. 클로저는 렉시컬 환경을 유지하므로, 클로저가 존재하는 한 그 환경의 변수들은 가비지 컬렉션의 대상이 되지 않는다. 따라서, 사용하지 않는 클로저는 적절하게 제거해야 한다.

부분 적용 함수

  • n개의 인자를 받는 함수에 미리 m개의 인자만 넘겨 기억시켰다가, 나중에 (n-m)개의 인자를 넘기면 비로소 원래 함수의 실행 결과를 얻을 수 있게끔 하는 함수

커링 함수

  • 커링 함수란 여러 개의 인자를 받는 함수를 하나의 인자만 받는 함수로 나눠서 순차적으로 호출 될 수 있게 체인 형태로 구성한 것을 말한다. 부분 적용 함수와 기본적인 맥락은 일치하지만 몇 가지 차이가 있다.

부분적용함수 - 여러 개의 인자를 전달 가능실행결과 재 실행시, 원본 함수 무조건 실행
커링함수 - 한 번에 하나의 인자만 전달하는 것이 원칙중간 과정상의 함수를 실행한 결과는 그 다음 인자를 받기 위해 대기마지막 인자가 전달되기 전까지는 원본 함수가 실행되지 않음

profile
Life is a risk.

0개의 댓글