[TIL] 클로저

ㅜㅜ·2022년 9월 12일
1

Today I learn

목록 보기
15/77
post-thumbnail

클로저

  • 함수와 함수가 선언된 어휘적 환경(변수 및 함수 선언의 형태)의 조합. 이 환경은 클로저가 생성된 시점의 유효 범위 내에 있는 모든 지역 변수로 구성됨.
  • 클로저 함수 : 외부 함수의 컨텍스트에 접근할 수 있는 내부 함수를 뜻함. 외부 함수의 실행이 종료된 후에도 클로저 함수는 외부 함수의 스코프, 즉, 함수가 선언된 어휘적 환경에 접근할 수 있음.
  • 리턴하는 함수에 의해 스코프(변수의 접근 범위)가 구분됨.
    const adder = function (x) {
    	return fucntion (y) {
    		return x + y;
    	}
    }
    
    // 외부를 감싸고 있는 '외부 함수'는 y에 접근 할 수 없다.
    // 내부에 있는 '내부 함수'는 x에 접근 할 수 있다.
  • 내부 함수는 외부 함수에 선언된 변수에 접근 가능.




클로저 함수의 활용

  • 클로저가 가장 유용하게 사용되는 상황은 현재 상태를 기억하고, 변경된 최신 상태를 유지해야할 때.

  • 데이터를 보존하는 함수 : 외부 함수의 실행이 끝나더라도 외부 함수 내의 변수를 사용할 수 있음.

    이는 외부 함수 내 변수가 메모리 상에 저장되기 때문인데, ‘어휘적인 환경’을 메모리에 저장하기 때문에 가능한 것!

    const adder = function (x) {
    	return function (y) {
    		return x + y;
    	}
    }
    
    const add5 = adder(5);
    add5 (7) // 12
    add5 (10) // 15
    
    // add5라는 변수에는 adder 함수에 5를 인자로 전달한 값이 그대로 유지되고 있음. 
    // 그래서 add5 함수에 다른 값을 넣으면 내부에 있는 함수에 인자가 전달이 되어 처리됨. 

    실용적으로 사용하기

    const tagMaker = tag => content => `<${tag}>${content}</${tag}>`
    
    const divMaker = tagMaker('div');
    divMaker('hello')// <div>hello</div>
    divMaker('codestates')// <div>codestates</div>
    
    const anchorMaker = tagMaker('a')
    anchorMaker('go') // <a>go</a>
    anchorMaker('urclass') //<a>urclass</a>
    

  • 정보의 접근 제한 (캡슐화) : 클로저를 사용해 불필요한 전역 변수 사용을 줄이고, 스코프를 이용해 값을 보다 안전하게 다룰 수 있음.
    • 클로저 모듈 패턴 : 클로저 이용해 내부 함수를 단 하나만 리턴하는 것에 그치지 않고, 객체에 담아 여러 개의 내부 함수를 리턴하도록 만듦.
      const makeCounter = () => {
      	let value = 0;
      
      	return {
      		increase: () => {
      			value = value + 1
      		},
      		decrease: () => {
      			value = value - 1
      		},
      		getValue: () => value
      	}
      }
      
      const counter1 = makeCounter();
      counter1 // 객체인 {increase: f, decrease: f, fetValue: f }를 리턴.
      
      //makeCounter 함수를 바꾸지 않고서 value 값을 새롭게 할당할 수 없지만 리턴하는 객체가 제공하는 매서드를 통해 value 값을 간접적으로 조작할 수 있음.
      //아래 모듈화 예시를 보면 value를 어떻게 간접적으로 수정할 수 있는지 알 수 있음.
      //value 값을 전역 변수로 만들지 않아 side effect 방지 가능. 

  • 모듈화 : 함수 재사용성을 극대화 하여 함수 하나를 완전히 독립적인 부품 형태로 분리하는 것. 클로저를 통해 데이터와 매서드를 같이 묶어서 다룰 수 있으므로 클로저는 모듈화에 유리.
    const counter1 = makeCounter();
    counter1.increase();//1
    counter1.increase();//2
    counter1.decrease();//1
    counter1.getValue();//1을 리턴
    
    const counter2 = makeCounter()
    counter2.decrease();//-1
    counter2.decrease();//-2
    counter2.decrease();//-3
    counter2.getValue();//-3을 리턴
    
    //makeCounter에 의해 리턴된 객체는 makeCounter를 실행할 때 선언되는 value 값을 각자 독립적으로 가짐.
    //counter1과 counter2에서의 value는 서로에게 영향 끼치지 않고 각각 값 보존 가능. 
    

  • 클로저의 단점: 일반 함수였다면 함수 실행 종료 후 가비지 컬렉션(참고 자료: MDN '자바스크립트의 메모리 관리') 대상이 되었을 객체가, 클로저 패턴에서는 메모리 상에 남아 있게 됩니다. 외부 함수 스코프가 내부 함수에 의해 언제든지 참조될 수 있기 때문입니다. 따라서 클로저를 남발할 경우 퍼포먼스 저하가 발생할 수도 있습니다.

    자바스크립트는 가비지 컬렉션을 통해 메모리 관리를 합니다. 객체가 참조 대상이 아닐 때, 가비지 컬렉션에 의해 자동으로 메모리 할당이 해제됩니다.





심화 학습

  • Execution context (실행 컨텍스트)

    : 실행 가능한 코드가 실행되기 위해 필요한 환경

    (실행 가능한 코드 : 전역 코드/ 함수 코드/ eval 코드)

    자바스크립트 엔진은 코드를 실행하기 위해 실행에 필요한 여러 정보를 알고 있어야 한다.

    • 변수 : 전역변수, 지역변수, 매개변수, 객체의 프로퍼티

    • 함수 선언

    • 변수의 유효범위(scope)

    • this

      실행에 필요한 정보를 형상화하고 구분하기 위해 자바스크립트 엔진은 실행 컨텍스트를 물리적 객체의 형태로 관리한다.

  • lexical environment : 자바스크립트 코드에서 변수나 함수 등의 식별자를 정의하는데 사용하는 객체로 Environment Record와 Outer Environment Reference를 프로퍼티로 가짐. ( ES5 이전에는 변수 객체, 활성화 객체, 스코프 체인등이 기능하던 것을 ES5 이후 lexical environment가 대신 기능하게 됨.)

    • Outer Environment Reference
      중첩 유효 범위를 가질 수 있는 환경에서 상위 Lexical Environment를 참조한다.
      즉, 외부 환경에 대한 참조를 가지고 있다.
      전역 환경에서는 null이다.
    • Environment Record
      유효범위 내의 값에 식별자를 매핑한다.
      - DeclarativeER : 변수 선언 및 함수 선언을 저장한다.
      - ObjectER : with 문이나 전역 환경에서 사용된다.
      - this

참고 : https://iamsjy17.github.io/javascript/2019/06/10/js33_execution_context.html

profile
다시 일어나는 중

0개의 댓글