본문 바로가기

Javascript

Javascript 스코프(scope) / 클로저(closure) 정리

1. 클로저를 들어가기 전에 알아야 할 개념

 

1-1)  스코프란?

 

변수 이름, 함수 이름, 클래스 이름과 같은 식별자가

본인이 선언된 위치에 따라 다른 코드에서 자신이 참조될 수 있을지 없을지 결정되는 것

 

function add(x,y) {
   console.log(x,y);
   return x + y;
 }
 
 add(3,6);
 
 console.log(x,y);

 

 

더보기

실행결과

 

3,6

ReferenceError: x in not defined 어쩌고..

 

 

 

 

 


 

 

 

함수의 중첩 스코프

 

var x = "나는 전역 X";

function outer() {
	var y = "나는 outer함수의 지역 Y";
    console.log(x);
    console.log(y);
    
    function inner() {
    	var x = "나는 inner함수의 지역 X";
        
        console.log(x);
        console.log(y);
    }
    
    inner();
  }
  
  outer();
  
  console.log(x);
  console.log(y);

 

 

더보기

실행결과

 

var x = "나는 전역 X";

function outer() {
var y = "나는 outer함수의 지역 Y";
    console.log(x); // 나는 전역 X
    console.log(y); // 나는 outer함수의 지역 Y
    
    function inner() {
     var x = "나는 inner함수의 지역 X";
        
        console.log(x); // 나는 inner함수의 지역 X
        console.log(y); // 나는 outer함수의 지역 Y
    }
    
    inner();
  }
  
  outer();
  
  console.log(x); // 나는 전역 X
  console.log(y); // ReferenceError: x in not defined 어쩌고..

 

 

 

 

 

 

 

 

▲스코프 체인

 

 

 

1-2) 스코프 체인

  • 계층적으로 연결 (무조건 위로만 올라감)
  • 물리적으로 존재
  • 자바스크립트 엔진은 스코프 체인을 통해 변수를 참조
  • 최상위 스코프는 전역 스코프

 

 

 

 

 

자바스크립트는 렉시컬 스코프를 따르기 때문에 함수는 정의되자마자 상위 스코프가 결정되고 이후에 해당 함수에 의해

함수 객체가 생성이 되면 해당 함수 객체는 내부 슬롯 에 상위 스코프에 대한 참조를 저장하여 본인의 상위 스코프를 알 수 있게 됨

 

*렉시컬(Lexical) : 포함하는 식별자, 식별자에 바인딩 된 값, 상위 렉시컬 환경에 대한 참조 

 

 

 


 

 

 

 

2. 클로저

 

const x = 1;

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

 

 

  • outer 함수는 중첩 함수 inner를 jh에게 반환하면서 생명주기를 마감, 즉 outer 함수가 실행이 종료되면 실행 컨텍스트 스택에서 제거
  • outer 함수의 지역변수 x도 생명주기가 마감하게 되어 지역 변수 x에 접근할 방법이 없지 않을까?

 

더보기

실행결과

 

10

 

 

---------------------------------

 

x의 부활......

 

 

 

중첩함수 inner가 이미 생명주기를 마감한 outer 함수의 지역 변수 x를 참조할 수 있다면

이때 inner를 클로저라고 함

 

 

 

 

outer 함수 종료 전 실행 컨텍스트 렉시컬 환경

 

 

outer 함수 종료 후 실행 컨텍스트 렉시컬 환경

 

 

  • outer 함수는 종료되면서 jh 한테 inner 함수를 반환하기 때문에 inner 함수 객체를 참조
  • outer 함수의 생명주기가 종료되어도 렉시컬 환경까지 소멸하지 않음

 

 

클로저는 중첩 함수가 상위 스코프의 식별자를 참조하고 있고, 외부 함수보다 더 살아있음

 

 

∴ 클로저는 함수와 함수가 선언된 어휘적 환경의 조합이다

 

 

 

 

 

 

사용처

 

  • 하나의 state가 의도치 않게 변경되지 않도록 state를 안전하게 은닉
  • 특정 함수에게만 state 변경을 허용하기 위해 사용

 

 

주의해야 할 점

 

  • 클로저 함수를 볼 때 외부에서 어떤 함수를 가져다가 사용하는지 직관적으로 알 수 있게 해야 함
  • 클로저는 명확한 것을 생략하여 가독성을 높여주는 역할을 할 수 있는데 이것을 잘못 활용하면 오히려 코드 가독성을 나쁘게 만들 수 있음

 

 

3. 추가 내용

 

3-1) 클로저 예제 (모듈 패턴 예제)

 

  • 자바스크립트는 이런 방법을 제공하지 않지만 클로저를 이용하여 프라이빗 메소드를 흉내내는 것이 가능
  • 프라이빗 메소드는 코드에 제한적인 접근만을 허용한다는 점 뿐만 아니라 전역 네임 스페이스를 편리하게 관리가능

 

var makeCounter = function() {
      var privateCounter = 0;
      function changeBy(val) {
        privateCounter += val;
      }
      return {
        increment: function() {
          changeBy(1);
        },
        decrement: function() {
          changeBy(-1);
        },
        value: function() {
          return privateCounter;
        }
      }
    };

    var counter1 = makeCounter();
    var counter2 = makeCounter();
    console.log(counter1.value());
    counter1.increment();
    counter1.increment();
    console.log(counter1.value()); 
    counter1.decrement();
    console.log(counter1.value());
    console.log(counter2.value());

 

 

더보기

실행결과

 

0

2

1

0

 

  

  • counter.increment, counter.decrement, counter.value 세 함수에 의해 공유되는 하나의 어휘적 환경 (렉시컬 환경)을 만듦
  • 어휘적 환경은 실행되는 익명 함수 안에서 만들어짐
  • 어휘적 환경에 privateCounter와 changeBy 함수 포함, 익명 함수 외부에서 접근 X
  • 익명 래퍼에서 반환된 세 개의 퍼블릭 함수를 통해서만 접근

*렉시컬(Lexical) : 포함하는 식별자, 식별자에 바인딩 된 값, 상위 렉시컬 환경에 대한 참조 

 

∴ 세 가지 퍼블릭 함수는 같은 환경을 공유하는 클로저다.

 

 

각 클로저는 그들 고유의 클로저를 통한 privateCounter 변수의 다른 버전을 참조하기 때문에 각 카운터가 호출될 때 하나의 클로저에서 변수값을 변경해도 다른 클로저의 값에는 영향을 주지 않음

 

 

 

 

3-2) 클로저 함수의 장점 (활용)

  • 데이터 보존 (함수의 중첩 스코프 예제)
    - 클로저 함수는 외부 함수의 실행이 끝나더라도 외부 함수 내의 변수 사용 가능
    - 특정 데이터를 함수 스코프 안에 가둬두고 사용할 수 있는 폐쇄성을 갖음

  • 정보의 접근 제한 (캡슐화 3-1 예제)
    - '클로저 모듈 패턴'을 사용하여 객체에 여러 개의 함수를 담아 리턴하도록 만듦

 

 

 

 

3-3) 클로저 사용 이유

 

 

자바스크립트는 private,public 등의 별도 문법이 없는 언어로 private 기능이 없다는 것은 예상치 못한 누군가가 접근과 조작이 가능하다는 것

 

클로저는 제한적인 접근만을 허용하며, private한 변수 또는 메소드의 효과를 낼 수 있음

 

-> 객체지향 프로그래밍의 정보 은닉과 캡슐화의 이점을 얻을 수 있음

 

 

 

 

3-4) 자바(JAVA)와 자바스크립트(Javascript) 메모리 구조

 

 

  • 자바스크립트(Javascript) 메모리 구조

사진 출처: https://curryyou.tistory.com/276

 - 콜스택(Call Stack) : 원시타입 데이터가 저장, 실행 컨텍스트를 통해 변수 식별자 저장, 스코프 체인 및 this 관리, 코드 실행 순서 관리

 

- 메모리힙(Memory Heap) : 참조타입 데이터가 저장

 

 

  • 자바(JAVA) 메모리 구조

사진 출처: https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=nature128&logNo=220768546595

 

 - 스택 영역(Java Stack) : 메소드가 호출되어 실행될 때 메소드의 매개변수, 메소드 내에 선언된 지역변수, 값 , 임시 변수 저장

 

- 힙 영역(Heap) : 데이터를 저장하기 위해 동적으로 할당하여 사용, 모든 프로그램에 의해 공유 , GC으로 관리