[JavaScript] 클로저(closure)

Dodam·2023년 9월 17일
0

[JavaScript]

목록 보기
6/10
post-thumbnail

클로저(closure)를 이해하기에 앞서

  • 클로저(closure)는 함수와 함수가 선언됐을 때의 렉시컬 환경(Lexical environment)의 조합이다.
    따라서, 클로저를 이해하기 위해서는 변수 스코프(scope)와 렉시컬 환경에 대한 이해가 필요하다.

  • 스코프(scope)는 변수가 유효한 범위를 나타내며,
    전역 스코프(global scope)와 지역 스코프(local scope)로 나뉜다.

    • 전역 스코프는 코드 전체에서 접근 가능한 스코프이며,
    • 지역 스코프는 함수 내부에서 선언한 변수가 접근 가능한 스코프이다.
  • 렉시컬 환경(Lexical environment)은 변수와 값의 매핑을 저장하는 객체이다.
    함수가 호출될 때마다 새로운 렉시컬 환경이 생성되며, 그 함수 내에서 선언한 변수와 값이 저장된다.
    또한, 함수가 선언될 때의 렉시컬 환경을 기억하여 이후에도 사용할 수 있다.

Lexical scoping

스코프는 함수를 호출할 때가 아니라, 함수를 어디에 선언하였느냐에 따라 결정된다.
이를 Lexical scoping이라고 한다.

Lexical scoping은 함수나 변수의 scope를 '선언된 위치'를 기준으로 정한다.
참고로 Dynamic scoping은 함수나 변수의 scope를 '호출된 시점'을 기준으로 사용한다.

예제

  • outerFunc() (= 외부 함수)는 지역 변수 name과 innerFunc()을 생성한다.
  • innerFunc()은 outerFunc() 안에서 정의된 내부 함수이다.
  • innerFunc()은 외부 함수인 outerFunc()에 정의된 지역 변수에 접근할 수 있다.

다음 코드를 실행할 경우,
innerFunc()이 outerFunc()에 정의된 name의 값을 성공적으로 출력한다.

function outerFunc() {
	let name = 'dam'
    
    function innerFunc() {
    	console.log(name) // dam
    }
  
  	innerFunc()
}

outerFunc()

자바스크립트는 위 코드를 다음과 같은 순서로 실행한다.

  1. innerFunc() 내에서 변수 name을 검색한다.
  2. 실패할 경우, outerFunc() (= 외부 함수)의 스코프에서 변수 name을 검색한다.
  3. 실패할 경우, 전역 스코프에서 변수 name을 검색한다.

클로저(closure)

클로저는 외부함수의 변수에 접근할 수 있는 내부 함수를 일컫는 말로,
스코프 체인(scope chain)으로 표현하기도 한다.

클로저는 다음 3가지 스코프 체인을 가진다.

  1. 클로저 자신에 대한 접근 (= 자신의 블럭 내에 정의된 변수)
  2. 외부 함수의 변수에 대한 접근
  3. 전역 변수에 대한 접근

클로저(closure)의 특징

  • 외부 함수 scope에서 내부 함수 scope로 접근이 불가능하다.
  • 내부 함수는 외부 함수 scope에 접근이 가능하다.
  • 외부 함수의 실행이 종료된 후에도, 클로저는 외부 함수의 스코프에 접근할 수 있다.

🤔 클로저(closure)가 외부 함수의 실행이 종료된 후에도,
렉시컬 환경(Lexical environment)에 접근 가능한 이유는 무엇일까?

  1. 외부 함수가 클로저(closure)를 반환하는 경우
function outerFunc() {
	let a = 10
    
    return function(value) {
    	return a += value
    }
}

let closure = outerFunc()
console.log(closure(5))	// 15
console.log(closure(5))	// 20
console.log(closure(5))	// 25

  1. 전역으로 선언한 변수에 클로저(closure)를 할당하는 경우
let closure

function outerFunc() {
	let a = 10
    
    closure = function(value) {
    	return a += value
    }
}

outerFunc()
console.log(closure(5))	// 15
console.log(closure(5)) // 20
console.log(closure(5)) // 25

클로저(closure)의 사용 목적

1. 상태 유지

클로저는 현재 상태를 기억하고 변경된 최신 상태를 유지해야 하는 상황일 때 유용하게 사용된다.

let changeState = (function() {
	let state = false
    
    return function() {
    	return state = !state
    }
}())

console.log(changeState())	// true
console.log(changeState())	// false
console.log(changeState())	// true

2. 전역 변수의 사용 억제

클로저를 사용하면 전역 변수의 사용을 억제할 수 있다.

전역 변수의 특성상, 다른 모든 함수가 전역 변수에 접근할 수 있으므로 의도치 않게 값이 변경될 수 있다.
이 때, 클로저를 사용하면 의도치 않은 상태 변경을 방지할 수 있다.

다음 코드는 conter 변수를 전역 변수로 선언하고, increase() 함수를 통해 1씩 증가시키는 코드이다.

클로저(closure)를 사용하기 전

let count= 0

function increase() {
	return ++count
}

console.log(increase())	// 1
console.log(increase()) // 2
console.log(increase()) // 3

클로저(closure)를 사용하여 코드를 다음과 같이 변환

let increase = (function() {
	
  let count = 0
  
  return function() {
  	return ++count
  }
}())

console.log(increase())	// 1
console.log(increase()) // 2
console.log(increase()) // 3

3. 정보의 은닉

this에 바인딩되지 않은 변수들을 클로저(closure)를 통해 반환함으로써,
자바의 private 키워드처럼 사용할 수 있다.

function Student() {
	let name = 'dam'
    let age = 20

    this.getName = function() {
    	return name
    }
  
  	this.getAge = function() {
    	return age
    }
}

let dam = new Student()
console.log(dam.getName())
console.log(dam.getAge())
profile
Good things take time

0개의 댓글