티스토리 뷰

프로그래밍 기초/JavaScript

클로저란?

StartCoriny 2024. 1. 11. 20:57

클로저란?

주변 상태(어휘적 환경)에 대한 참조와 함께 묶인(포함된) 함수의 조합입니다.

즉, 클로저는 내부 함수에서 외부 함수의 범위에 대한 접근을 제공합니다.

JavaScript에서 클로저는 함수 생성시 함수가 생성될 때마다 생성됩니다. - MDN

쉽게말해서 이미 생명 주기가 끝난 외부 함수의 변수를 참조하는 함수입니다. 

 

함수 내부에 함수가 존재할 경우

const name = 'start' // name은 전역변수

function func1() {
	var name = "coriny"; // name은 func1 의해 생성된 지역 변수이다.
	function func2() { // func2() 은 내부 함수이며, 클로저다.
		console.log(name); // coriny 즉, 부모 함수에서 선언된 변수를 사용한다.
	}
	func2();
}
func1();

func1()호출 은 지역변수 name과 함수 func2()를 생성합니다.

func2()는 func1()안에서 정의된 내부 함수이고 func1함수 안에서만 사용할수가 있습니다.

func2()안에는 자신만에 지역변수가 없습니다. 하지만 내부함수에서 외부함수의 변수에 접근(클로저)을 할수있기 때문에

func2()는 부모 함수에 선언된 변수에 접근을 할수가 있습니다.

■ func1()을 호출하고 func2()가 호출될때 func2()는 자신의 스코프 내부에서 name을 찾습니다.
■ 자신의 스코프에 name이라는 변수가 정의가 안되어있으면 scope chain에 의해 바깥쪽 scope를 찾습니다.
■ outerEnvireonmentReference는 해당 실행 컨텍스트의 생성시점의 LexicalEnvironment를 가지고 있으므로
    func1의 접근을 할수가 있고 func1()의 name값을 가져와 출력이 될수가 있습니다. 

 

서로 같은 곳에 함수가 위치해 있을때의 scope

const name = 'start' // name은 전역변수

function func1() {
	var name = "coriny"; // name은 func1 의해 생성된 지역 변수이다.
	func2();
}

function func2() {
    console.log(name); // start 
}

func1();

Lexical Scope

JavaScript는 함수를 어디서 '호출했는지'가 아니라 함수를 '어디에 정의했는지'에 따라 상위 스코프를 결정합니다.

즉, 스코프에 대한 참조는 함수정의가 평가되는 시점에 함수가 정의된 환경(위치)에 의해 결정됩니다.

 

위와 같은 상황일 때는 func1()과 func2()는 서로 다른 scope를 가지고 있습니다.

func1()의 name이라는 변수를 초기화 해두었지만 func2()는 호출이 되고 난 후의 name을 따라가는 것이아닌 함수가

정의된 위치에 따라 상위스코프에 접근하기 때문에 전역렉시컬 환에서의 name에 접근하여 start라는 값이 출력이 됩니다.

즉, Lexical Scope를 따르는 프로그래밍 언어이기 때문에 func2()는 func1()의 name에 접근을 할수가 없습니다.  

 

 

클로저의 기능

외부 함수보다 중첩 함수가 더 오래 유지 될 경우에

중첩함수는 이미 생명주기가 종료한 외부 함수의 변수를 여전히 참조 할수 있습니다.

const name = 'start';

function func1() {
  const name = "coriny";
  const func2 = function () {
    console.log(name);
  };
  return func2; // coriny
}

const closerFunc = func1();
closerFunc(); // coriny

 

func1()함수를 호출하면 스택에 쌓이게 되면서 중첩함수인 func2를 반환합니다.

반환을 하게 되면 func1()의 실행컨택스트는 스택에서 제거되게 됩니다.

closerFunc()함수에는 'coriny'라는 값이 전해졌지만 'coriny'가 있는 실행컨텍스트인 func1()은 스택에 제거되어서 없습니다.

func2()함수는 func1()함수의 렉시컬 환경을 여전히 참조 하고 있는것을 알수가 있습니다.

why?
가비지 컬렉터가 func1()의 렉시컬환경은 참조 되는 곳이 있으니 알아서 제거 하지 않습니다.

 

즉, func1()함수는 실행컨텍스트 스택에서는 제거가 되어있지만 렉시컬 환경까지는 소멸하지않는다는 것입니다.

 

 

 

 

클로저의 기능

1. 중첩 함수의 중첩

클로저는 함수가 선언된 시점에서의 스코프에 있는 변수에 접근할수 있도록 하는 JavaScript의 특성입니다.

그렇기 때문에 함수 내부에 다수의 중첩된 함수가 있다면

계속해서 함수가 선언된 시점에서의 스코프체인을 따라 올라가면서 외부 함수들의 변수에 접근을 할수가 있습니다.

const firstname = 'start';

function secondname() {
    const secondname = 'coriny';

    function hello() {
        const hello = 'hello';

        function func() {
            console.log(firstname, secondname, hello); // start coriny hello
        }

        func();
    }

    hello();
}

secondname();

이처럼 func()함수는 해당 변수에 각각 접근하여 출력해주는 것을 볼수 있습니다.

이를 활용하여 클로저를 통해 데이터를 보존하고 활용을 할수가 있게 됩니다.

 

2. 변수의 은닉화

클로저는 의도치 않은 상태의 변경을 막기위해 상태를 안전하게 변경하고 유지 하기 위해 사용합니다.

즉, 클로저를 사용하면 특정 변수를 외부에서 접근할 수 없게 만들어, 변수의 은닉화를 구현할 수 있습니다.

const increase = (function () {
  // 카운트 상태 변수
  let num = 0;

  // 클로저
  return function () {
    return ++num;
  };
})();

// 이전 상태값을 유지
console.log(increase()); //1
console.log(increase()); //2
console.log(increase()); //3
작동 순서
1. increase()는 즉시 실행함수가 호출되고 즉시실행함수는 반환된 함수가 increase변수에 할당됩니다.
2. 변수에 할당된 함수는 자신이 정의된 위치에 의해 결정된 상위 스코프인
    즉시 실행 함수의 렉시컬 환경을 기억하는 클로저 입니다. 
3. 즉시 실행 함수는 호출된 이후 소멸되지만, 즉시 실행 함수가 반환한 클로저는 increase 변수에 할당되어 호출됩니다.
4. 이 때 즉시 실행 함수가 반환한 클로저는 자신이 정의된 위치에 의해 결정된 상위 스코프인 즉시 실행 함수의 렉시컬 환경을 기억하고 있습니다.
5. 따라서 즉시 실행 함수가 반환한 클로저는 카운트 상태를 유지하기 위한 자유 변수 num을 언제 어디서 호출하든지 참조하고 변경할수가 있습니다.
6. num은 렉시컬 환경을 기억하고 있기 때문에 초기화도 되지 않으며 외부에서 직접 접근할수 없기 때문에 전역변수를 사용했을 때와 같이 의도적인 변경을 걱정하지 않아도 됩니다.

 

 

※ 주의

// func2()는 클로저이지만 바로 소멸하기 때문에
// 일반적으로 클로저라 하지않는다
const name = 'start';

function func1() {
  const name = "coriny";
  const func2 = function () { 
    console.log(name);
  };
}

func1();


console.log('----------------------------------------------------')

// 클로저의 예
// 중첩 함수 bar는 외부 함수보다 더 오래 유지되며 
// 상위 스코프의 식별자를 참조한다.

const name = 'start';

function func1() {
  const name = "coriny";
  const func2 = function () {
    console.log(name);
  };
  return func2; // coriny
}

const closerFunc = func1();
closerFunc();// coriny

즉, 클로저는 상위 스코프의 식별자를 참조하고 중첩함수는 외부 함수보다 더 오래 유지되어야 합니다.

 

렉시컬 스코프,  outerEnvireonmentReference 이런 말이 어려우시다면  여기를 보고 오시면 이해가 쉬우실겁니다!!

 

TIL 8일차 + 실행 컨텍스트란?

실행 컨텍스트 • 자바 스크립트가 실행 될 때 생성되는 하나의 실행 단위입니다. • 자바 스크립트는 자신만의 독특한 과정으로 신행 컨텍스트를 만들고 그 안에서 실행이 이루어 집니다. •

startcoriny.tistory.com

 

 

 

 

배워나가는 코린이 입니다!!

부족한게 있다면 댓글로 지적해주세요!! 감사합니다!😊

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2025/02   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28
글 보관함