Closure 와 Scope

junamee·2021년 7월 6일
2

자바스크립트

목록 보기
1/11

Scope

변수에 접근할 수 있는 범위를 말한다. (식별자에 대한 유효 범위) 변수의 값을 찾을 때 들여다 보는 곳이다.

  • global(전역) scope
  • local(지역) scope
  • 전역공간의 스코프와 함수단위(지역)로 생성되는 스코프가 있다.
  • 함수 내부에서 선언한 변수는 해당 함수의 내부에서만 접근가능하고, 함수 내부는 외부의 값을 참조, 접근할 수 있다.
    : 이처럼 식별자의 값을 찾기 위해 내부에서 외부로 차례로 검색해 나가는 것을 스코프 체인(Scope Chain)이라고 한다.
    : 무조건 스코프 체인 상에서 가장 먼저 발견된 식별자에만 접근가능하다.=>Lexical Scope

이해1) (스코프 체인)

var c ='C'
function outer(){
    var a =1
    var b='B'
    
    function inner(){
        var a=2
        return {a, b, c}
    }
    return inner()
}
var someFun = outer() 
inner의 스코프
[ a: 2 ]

outer의 스코프
[ a: 1 ]
[ b: 'B']

global 스코프
[ c: 'C']
  1. inner함수 내에서 a를 찾아본다.
    : inner의 스코프에 a에 해당되는 값 2가 있으므로 2를 리턴.

  2. inner함수 내에서 b를 찾아본다.
    : 없으므로 outer스코프로 나가서 b값을 찾고 'B'를 리턴한다.

  3. inner함수 내에서 c를 찾아본다.
    :없으므로 outer스코프로 나가 찾아본다. 없다.
    global 스코프에서 c를 찾고 'C'를 리턴한다.

inner -> outer -> global 스코프로 이동하며 검색하였다.

이해2) (렉시컬 스코프)

var a=1
var outer = function(){
  var inner = function(){
    console.log(a)* 
    var a =3
    
  }
  inner()
  console.log(a)
}
outer()
console.log(a)

여기서 4번째줄의 a는 undefined이다.
이해1번에서와같이 outer로 나가서 global 스코프에서 a를 찾을 수 있을 것이라고 생각할수 있지만, innner의 스코프에 a가 선언되어있기 때문에 가장 가까운 스코프, 자기 내부 스코프의 값을 사용하지, 외부로 접근할 수 없다. 때문에 3이 선언되기 전, 해당 시점의 undefined를 반환하게 된다.

Closure

어떤 함수A에서 선언한 변수를 참조하는 내부함수를 외부로 반환했을 시,
함수의 실행컨텍스트가 종료되어도 (함수A종료 후에도) 해당 변수가 사라지지 않고 참조가능한 상태를 말한다.

동일한 이해1)의 코드를 다시 해석해본다.
이해1)

var c = 'C';
function outer() {
  var a = 1;
  var b = 'B';

  function inner() {
    var a = 2;
    return { a, b, c };
  }
  return inner();
}
var someFunc = outer();
console.log(someFunc);      //{ a: 2, b: 'B', c: 'C' }

someFun함수는 outer함수를 호출하고, outer함수는 inner함수를 리턴하는 임무를 수행하고 종료된다. 즉, someFunc함수는 실행전의 inner함수 결과를 변수로 저장하고 있다.

이때 outer에 선언되었었던 변수 값이 사라진다고 생각할 수 있지만 생성시점의 스코프 체인을 계속 보유하고 있어 outer함수가 종료되어도 해당 값을 인식할 수 있다.

이게 가능한 이유는 가비지 컬렉터가 어떤 값을 참조하는 변수가 하나라도 있다면 그 값은 가비지 컬렉터의 수집대상에 포함시키지 않기 때문이다. outer함수는 종료되었어도 SomeFunc은 inner함수를 실행할 수 있기 때문에 가비지컬렉터로 정리하지 않아 해당 종료된 outer스코프를 참조할 수 있다.

즉, 클로저는 어떤 함수와 해당 함수의 변수, 그 변수를 참조하는 내부함수가 있는 상황일때, 외부함수가 실행되어 종료되어도 해당 변수값들이 사라지지 않고 언제든 내부함수가 종료된 외부함수의 변수값에 접근할 수 있는 것이다.

함수Outer, 함수Outer의 지역변수 b, b를 참조하는 내부함수inner가 외부함수 someFunc에 전달된 상황

클로저 실제 사용

콜백함수와 클로저

const alertFunc = function(fruit) {
  return function(){
    alert('eat '+fruit)}}


var fruits = ['apple', 'mango','cherry']
var $ul = document.createElement('ul')
fruits.forEach(function(fruit){
  var $li = document.createElement('li')
  $li.innerText=fruit
  $li.addEventListener('click', alertFunc(fruit))
  $ul.appendChild($li)
})
document.body.appendChild($ul)

li를 클릭하면 alertFunc 이벤트 핸들 콜백함수가 실행되는데
함수를 반환한다. 이벤트 발생 시 이 반환된 함수가 실행되면서 alertFunc의 인자로 넘어온 fruit를 참조하게 된다.

즉 반환된 함수는 alertFunc의 내부함수인데 외부에 선언되면서도 실행이 종료된 alertFunc와 그 인자 fruit를 참조하고 있는 상태인 것이다.

정리

클로저는 스코프 체인과 렉시컬 스코프의 성격을 바탕으로 정의된다.
필요한 값을 체이닝을 통해 상위레벨에서 한번 더 검색하고,
렉시컬 스코프의 성격으로 가장 가까운 값을 해당 값으로 사용하게 된다.

가비지 컬렉터의 성질로 인해 상위레벨의 함수가 종료되었어도 내부 함수가 상위레벨의 변수값, 데이터를 사용하고 있었다면 가비지 컬렉터는 이를 삭제하지 않아 상위레벨함수는 사라져도 스코프는 그대로 존재해 계속해서 종료된 상위함수의 데이터를 사용할 수 있으며 이것이 클로저 이다.
실제 적용 상황은 이벤트 콜백함수를 예로 들 수 있었다.


profile
아티클리스트 - bit.ly/3wjIlZJ

0개의 댓글