프로그래밍 언어/Javascript

[Javascript] Closure란?

hanrabong 2022. 4. 23. 03:39

JS(ko.wikipedia.org)

 

 Closure는 JS를 이용해서 개발을 할 때 한 번쯤 들어봤을 개념입니다. 중요한 개념이라고 하는데 많이 헷갈리기에 내부적으로 어떻게 동작하는지 정리하려고 합니다.

 

Closure란?


 클로저는 외부 함수가 반환된 후에도 외부 함수 scope에 접근할 수 있는 함수입니다. 즉, 클로저는 함수가 완료된 후에도 외부 함수의 변수와 인수를 기억하고 접근할 수 있습니다.

 

 

Scope


Closure에 앞서 scope에 대해서 알고 있어야 합니다. scope는 말그대로 범위입니다. scope에는 3가지 종류가 있습니다.

 

  • Global scope
  • Function scope(local scope)
  • Block scope

 다음과 같은 예시를 통해서 scope에 대해 알아보겠습니다.

console.log('global scope');
let a = 'global';

function hello() {
	let b = 'function';
	console.log('functional scope');
}

{
	let c = 'block';
	console.log('block scope');
}

 

global scope는 function이나 block 안에서 정의되지 않은 변수들을 말합니다.

function scope는 함수 내에서 정의된 변수들의 범위를 말합니다. 해당 변수는 함수 안에서만 접근이 가능합니다. 

block scope는 block 내에서만 존재하는 변수들을 말합니다. 해당 block 밖에서는 접근할 수 없습니다.

 

 

위에서 3 종류의 scope에 대해서 알아봤습니다. Closure를 좀 더 잘 이해하기 위해서 Lexical Scope를 이해해야 합니다.

 

Lexical Scope


 Lexical Scope는 Static Scope라고도 하는데 runtime에 scope가 정해지는 것이 아니라 lexing time에 scope가 정해지는 것을 말합니다. 쉽게 말해서 함수를 호출할 때가 아닌 함수를 어디서 선언을 하였는지에 따라 scope가 결정이 됩니다.

 

예시를 통해 자세히 알아보겠습니다.

let n = 'rabong';

function hello() {
  console.log(`hello ${n}`);
}

function meet() {
  let n = 'hanrabong';
  hello();
}

meet();

다음과 같은 코드를 실행시키면 어떤 결과가 나올까요? 다음과 같은 결과가 나옵니다.

hello rabong

 

위의 코드를 보면 meet()함수 안에서 hello() 함수를 호출했기에 meet 내부의 n 변수인 'hanrabong'을 호출할 것이라고 생각이 들지만 실상 rabong이라는 값이 나오게 됩니다. 그 이유는 위에서 설명했듯이 runtime이 아닌 함수를 어디서 선언했는지에 대해 scope를 정하였기에 hello() 함수의 outer scope는 global이기에 global execution context에 있는 n = 'rabong'이라는 변수를 사용하게 됩니다.

 

 

※ execution context에 대해 알고 싶으면 다음글을 참고해주세요! execution context를 알면 closure에 대한 이해가 더 쉽습니다.

2022.03.20 - [웹/Javascript] - [Javascript] Javascript 동작(1)

 

[Javascript] Javascript 동작(1)

Javascript란? Javascript는 싱글 쓰레드로 동작하는 객체 기반의 스크립트 언어입니다. 웹 브라우저나 Node와 같은 런타임 환경에서 실행이 됩니다. Javascript에는 hoisting, call by sharing, prototype, clos..

hanrabong.com

 

 

 

Closure 예시


 Closure에 대한 예시를 들어보겠습니다. 

 

function getCount() {
	let cnt = 0;
	return function up() {
		return cnt++;
	}
}

let count = getCount();

console.log(count());   // 0
console.log(count());   // 1
console.log(count());   // 2

 

 다음과 같은 코드를 보면 getCount()의 scope에 있는 cnt 변수를 계속 접근하여 증가시키는 것을 볼 수 있습니다. 밖에서는 직접 cnt 변수에 접근하지 못하고 내부에서 접근하여 증가를 시키고 있습니다. 

 Java와 같은 OOP에서의 private 변수와 비슷합니다. 객체 내부에서만 접근 가능하고 외부에서는 접근이 불가능합니다. Closure를 이용하여 외부에서는 접근이 불가능하고 내부에서만 가능한 변수나 함수등을 만들 수 있습니다.

 

function Counter() {
	let cnt = 0; // private 변수

	this.up = function () {
		return ++cnt;
	}

	this.down = function () {
		return --cnt;
	}
}

let count = new Counter();

console.log(count.up());    //  1
console.log(count.up());    //  2
console.log(count.down());  // 1

 

Closure 동작 원리


 위에서 Closure가 무엇인지, Closure에 대한 예시와 Closure를 이용한 private method와 변수에 대해서 알아보았습니다. 이제 Closure가 어떻게 동작하는지에 대해 알아보겠습니다.

 

 Closure에 대해 완벽하게 이해하려면 Execution ContextLexical Environment에 대해서 알아야합니다.(위에 언급한 글을 참고하시면 됩니다.) 위 2개 개념을 알고있다는 가정하에서 어떻게 동작하는지 설명하겠습니다.

 

예시를 통해서 알아보겠습니다.

function getCount() {
	let cnt = 0;
	return function up() {
		return cnt++;
	}
}

let count = getCount();

console.log(count());   // 0
console.log(count());   // 1
console.log(count());   // 2

getCount() 함수가 실행이 될 때 Javascript engine은 새로운 execution context를 만들고 그 안에 lexical environment를 생성합니다. 함수가 실행이 끝나고 나면 count 변수에 up 함수를 반환합니다.

 

lexical environment는 다음과 같습니다.

functionExecutionContext = {

	getCountLexicalEnvironment = {
		environmentRecord: {
			cnt: 0,
			up: <up function reference>
		}
	}
	outer: <globalLexicalEnvironment>
	
	// getCounetVariableEnvironment 등등
}

 getCount 함수가 실행이 끝나면 해당 execution context는 stack으로부터 제거됩니다. 하지만 up 함수의 lexical environment에 의해 getCount lexical environment가 참조가 되기에 여전히 memory에 남아있습니다.

 

 

up 함수의 LexicalEnvironment를 보면 다음과 같습니다. (당연히 execution context안에 lexical environment가 존재하지만 편의를 위해 lexical environment만 표현하였습니다.)

upLexicalEnvironment = {
	environmentRecord: {
	}
	outer: <getCountLexicalEnvironemnt>
}

 up 함수 내부에 변수가 없기에 environment record는 비어있는 상태입니다. up함수가 실행이 될 때 Javascript engine은 해당 scope의 Lexical environment내에서 변수를 찾다가 못 찾으면 outer Lexical environment에서 변수를 찾아서 처리합니다.

 

 

결론


Closure의 정의와 Closure의 동작원리 등을 알아보았습니다. Closure에 대한 이해를 위해 Execution context와 lexical environment 등 javascript 동작원리를 정확히 알고 있어야 이해가 가능하였습니다. Closure에 대하여 헷갈렸던 부분을 정리해보면서 scope에 대해서도 좀 더 잘 이해할 수 있었습니다.

 

 

※ 아래 블로그를 참고하여 내용을 덧붙여 정리하였습니다. 궁금하거나 틀린 내용 댓글 부탁드려요

 

 

 

참고자료

https://medium.com/bitsrc/a-beginners-guide-to-closures-in-javascript-97d372284dda

 

Understanding Closures in JavaScript

Learn How Closures Really Work in JavaScript: hands-on guide

blog.bitsrc.io

 

'프로그래밍 언어 > Javascript' 카테고리의 다른 글

[Javascript] Data type - Primitive type  (0) 2022.04.26
[JavaScript] Hoisting이란?  (0) 2022.04.19
[Javascript] Javscript 동작(2)  (0) 2022.04.05
[Javascript] Javascript 동작(1)  (0) 2022.03.20