본문 바로가기

프로그래밍/JavaScript

클로저와 스코프 체인

클로저

  • 클로저는 내부 함수가 외부 함수의 context에 접근할 수 있도록 하는 것이다. 이렇게 되면 외부 함수의 실행이 끝나 외부 함수가 소멸한 이후에도 내부 함수가 외부 함수의 변수에 접근할 수 있다.
  • 다음 4가지는 클로저의 특징이다.
    - 클로저는 객체의 메소드에서도 사용할 수 있다.
    - 동일한 외부 함수 안에서 만들어진 내부 함수나 메소드는 외부 함수의 지역 변수를 공유한다.
    - 똑같은 외부 함수를 공유하고 있는 내부 함수나 메소드의 결과는 서로 다를 수 있는데, 외부 함수가 실행될 때마다 새로운 지역변수를 포함하는 클로저가 생성되어 각각 완전히 독립적인 객체가 되기 때문이다.
    - 클로저 내부의 변수들은 클로저 내부에서 생성된 함수나 메소드만 접근할 수 있다. 이를 이용하면 자바스크립트가 기본적으로 지원하지 않는 private가 구현 가능해진다.
function func() {
    const title = "Hello";
    return function () {
        console.log(title);
    }
}

const outter = func();

outter();
  • 위 코드에서 func() 함수는 outter에 참조되며 호출된 후 소멸된다. 하지만 outter()함수가 호출되면 func 함수 내에 선언되어 있던 titlie변수가 호출되며 Hello가 출력된다. 이로서 다른 함수에 참조되면 해당 함수까지 소멸될 때까지 소멸되지 않고 존재한다는 사실을 알 수 있다.
  • 클로저로 인해 문제가 주로 발생하는 곳은 반복문이다.

 

//반복문에서의 클로저 문제
function counter() {
    const i;
    for (i = 0; i < 10; i++) {
        setTimeout(function() {
            console.log(i);
        }, 1000);
    }
}

counter();
  • 위 코드에서는 목적했던 1~10의 출력이 아니라 10이 10번 출력되는 결과가 출력된다.
  • 컴퓨터의 성능은 엄청나게 좋기 때문에 첫 1초가 끝나기 전에 이미 1부터 10까지 반복을 모두 끝마치고 10을 1초마다 한 번씩 출력하기 때문에 클로저 문제가 발생하는 것이다.
  • 해당 문제를 해결하기 위한 방법은 크게 두 가지가 있다.
    - 새로운 스코프를 추가해 매 반복마다 값을 따로 저장하는 방식
    - ES6에서 새로 추가된 방식인 블록 스코프를 이용하는 방식
//새로운 스코프를 반복마다 추가해 값을 따로 저장하는 방식
function counter() {
    const i;
    for (i = 0; i < 10; i**) {
        (function(num) {
            setTimeout(function() {
                console.log(num);
            }, 1000);
        })(i);
    }
}

counter();
  • 새로운 스코프를 추가함으로서 반복문이 빠른 시간에 끝 숫자까지 도달하더라도 각 반복에 해당하는 값을 미리 저장해두어 시간이 지남에 따라 원하는 값이 출력될 수 있도록 한다.
//블록 스코프를 이용하는 방식
function counter() {
    for (let i = 0; i < 10; ++i) {
        setTimeout(function() {
            console.log(i);
        }, 1000);
    }
}

counter();
  • 기존의 함수 기반 스코프에서 발생했던 클로저 문제를 블록 레벨 스코프인 let으로 변경해주어 문제를 해결한다.
    • let은 블록 레벨로서 해당 변수가 선언된 괄호를 넘어서지 않기 때문에 함수 기반인 var과는 달리 해당 코드가 작성된 중괄호를 기준으로 let으로 선언한 스코프를 찾은 후 점차 해당 코드가 포함된 함수로 퍼져나가게 된다.

 


스코프 체인

  • 함수가 실행될 때 표를 찾는 것이 스코프이다.
  • 자바스크립트는 기본적으로는 함수 단위로 스코프가 생성된다.
  • 함수 내에 또다른 함수를 선언하면 해당 함수만의  스코프가 다시 생성되며 외부를 감싸고 있는 함수와 연결된다. 이로 인해 내부 함수에서 원하는 값이 없으면 외부로 거슬러 올라가며 스코프를 탐색하는 절차를 진행할 수 있게 된다.
  • 자신이 속한 함수 내에서 해당 변수를 찾고 찾고자 하는 변수가 없다면 점차 바깥 함수로 올라가며 전역 변수까지 찾아간다. 전역 범위에까지 원하는 변수가 없다면 오류가 발생하게 된다.
function myFunction() {
    var a = 1;
    function innerFunction() {
        var a = 2;
        console.log(a);
    }
    console.log(a);
    innerFunction();
    console.log(a);
}

myFunction();

console.log(a);
  • 위 코드에서의 출력은 순서대로 1, 2, 1, 오류가 된다.
  • innerFunction에서 호출된 a값은 바로 근처에 있는 같은 함수 내 변수값인 2가 되며 해당 함수를 빠져나올 때 값이 증발된다.
  • myFunction() 내부에서 호출된 모든 a값은 별개의 함수 안에 a를 중복 선언하지 않는 이상 1이 된다.
  • 함수 밖에서 호출된 a값은 전역 범위에서 a변수가 존재하지 않기 때문에 레퍼런스 오류가 발생한다.

 

 

'프로그래밍 > JavaScript' 카테고리의 다른 글

var과 let, const  (0) 2019.08.22
this  (0) 2019.08.21
화살표 함수  (0) 2019.08.20
JavaScript (3)  (0) 2019.08.01
Java Script (2)  (0) 2019.07.12