Javascipt의 스코프(scope)와 클로져(closure)

허정민·2021년 6월 8일
0

스코프란(scope)?

위키피디아는 스코프(scope)의 컴퓨터 공학적 정의를 다음과 같이 내리고 있다.

In computer programming, the scope of a name binding — an association of a name to an entity, such as a variable — is the part of a program where the name binding is valid, that is where the name can be used to refer to the entity.

대략적으로 번역하자면, "스코프는 프로그램속에서 우리가 선언한 변수들이 유효하게 쓰일수 있는 특정 범위를 나타내는 말" 이라고 할 수 있다. 스코프는 다시 또 2가지 종류로 나누어지는데 '특정 범위(직역하자면 프로그램의 한 부분)'에 주목할 필요가 있다.

In practice for most programming languages, "part of a program" refers to a portion of source code (area of text), and is known as lexical scope. In some languages, however, "part of a program" refers to a portion of run time (time period during execution), and is known as dynamic scope.

이 특정범위는 스코프의 종류마다 다른데, 소스 코드의 한 부분을 특정범위로 본다면(변수가 선언된 지점을 의미하는 것), 이를 렉시컬 스코프(lexical scope)라고 하고 특정범위를 실행하는 순간으로 본다면(실행하는 순간의 변수가 무엇이냐 하는 것) 이를 다이나믹 스코프(dynamic scope)라고 한다.

렉시컬 스코프(Lexical Scope) 와 다이나믹 스코프(Dynamic Scope)

스코프의 개념을 말하면서 렉시컬 스코프와 다이나믹 스코프의 대략적 차이점에 대해서 알아보았다. 그렇다면 오늘 알아볼 자바스크립트는 어떤 스코프 체계를 가지고 있을까? 결론부터 말하면 렉시컬 스코프 체계를 사용하고 있다. 간단한 예시를 통해 알아보자

var x = 'global'

function foo() {
    var x = 'local'
    bar()
}
function bar () {
    console.log(x)
}

foo()//global
bar()//global

위의 예제는 자바스크립트가 렉시컬 스코프를 사용하는것을 증명하는 간단한 예제이다. 위 코드를 실행하면 foo 함수와 bar 함수 모두 global을 출력한다. 렉시컬 스코프는 변수의 사용가능 여부를 알기 위해서 변수가 어느 위치에 선언되었는지가 중요한 스코프라고 할 수 있다.

$x = "global";

sub foo {
    local $x = "local"
    bar();
}

sub bar {
    print "$x\n"
}

foo(); //local
bar(); //global

위의 예시는 다이나믹 스코프 체계를 사용하는 Perl의 코드이다. 위 코드를 실행하면 foo와 bar 함수가 각 각 다른 값을 출력하는데 이는 렉시컬 스코프가 함수가 선언된 시점의 변수를 처리하는 반면, 다이나믹 스코핑은 함수가 실행되는 시점의 변수를 처리하여 다른 결과가 나오는 것을 볼 수 있다.

JavaScript에서의 스코프(Scope)

자바스크립트에서는 렉시컬 스코프 이외에도 많은 스코프들이 존재한다. 우선 전역 스코프(Global Scope)와 지역 스코프(Local Scope)부터 알아보자.

  • 전역 스코프 (Global Scope)
var x = 'global'

function foo () {
  var x = 'local'
  x = 'change'
}

foo();
console.log(x)//global이 찍힌다

전역 스코프는 스코프 영역에 변수를 선언하면 전역에서 호출이 가능한 것을 의미한다. 자바스크립트의 가장 바깥쪽에 있는 스코프라고 생각하면 되고, 보통 이 전역 스코프에 변수를 선언하면 window 객체에 변수를 선언하는 것과 같다. 위의 예시를 보면 함수 foo안에서 x의 값을 새로 선언하여도, 전역 스코프에 선언한 x는 영향을 받지 않는 것을 볼 수 있다. 이는 함수 내부에서 새로 선언한 변수는 지역 스코프에 선언한 것이기 때문으로 자바스크립트는 이를 독립된것으로 간주한다.

  • 지역 스코프 (Local Scope)
var x = 'global'

function foo () {
  var x = 'local'
  x = 'change'
  console.log(x)
}

foo()//change가 찍힌다

지역 스코프는 전역 스코프와는 다르게 스코프 영역에 변수를 선언하면 특정 블록 또는 지역내에서만 호출이 가능한 것을 의미한다. 자바스크립트를 예로 들면 주로 함수 내부에 선언한 변수들이 지역 스코프에 선언된 변수라고 보면 편하다. 위의 예시를 보면 전역 스코프에 선언된 x와는 무관하게 foo 함수를 실행시 함수 내부의 x값을 찍어보면 local에서 change로 바뀐것을 확인 할 수 있다. 이는 전역변수 x와는 무관한 지역변수 x의 값을 반환했기 때문이다.

! 주의할점 !

var x = 'global'

function foo () {
  x = 'local'
}

foo()
console.log(x)//local이 찍힌다

전역 스코프를 설명할때 썼던 예시인데 살짝 바뀐게 있다. 함수 foo 내부에서 x를 선언할때 var 선언문을 쓰지 않았다. 이렇게 되면 자바스크립트의 실행 컨텍스트 상, 변수의 범위를 호출한 함수의 지역 스코프부터 전역 변수들이 있는 전역 스코프까지 넓혀 가면서 찾기 때문에 전역에 선언한 x의 값이 바뀌게 된다. 이렇듯 변수를 선언할때 var을 생략하게 되면 암묵적으로 전역 취급 (Implied Global)을 받기때문에 주의 해야 한다. 이런 변수 오염 (Scope Pollution)때문에 전역 변수를 취급할때는 주의을 요해야 한다.

  • 스코프 체인 (Scope Chain)
var x = 'global'

function foo () {
  var y = 'local'
}

console.log(y)//ReferenceError: y is not defined 가 찍힌다

전역 스코프와 지역 스코프는 bottom to top 의 형태로 작동한다. 만약 함수 내부에서 선언된 지역 변수를 전역에서 접근하려고 하면 오류가 난다. 하지만 모든 함수는 전역객체에 접근이 가능하다. 이는 함수 내의 함수가 부모 함수의 변수에 접근가능한것도 포함한다. 즉 하위 객체에서 타고 올라가면서 각 객체에 접근하는 것은 가능하지만 전역에서 내부에 접근하는 것은 불가능하다. 이렇게 스코프를 타고 올라가면서 변수를 설정하는 것을 스코프 체인이라고 한다.

클로져(Closure)

클로져는 외부 함수의 스코프를 기억하여 외부 함수의 변수에 접근할 수 있는 내부 함수를 말한다. 말이 어렵지만 간단한 예시를 보면서 이해해 보자.

function makeFunc() {
    var name = "closure"
    function displayName() {
        console.log(name);
    }
    return displayName;
}

var myFunc = makeFunc()
myFunc()//closure가 찍힌다

다른 프로그래밍 언어에서는 함수 안의 지역 변수들은 그 함수가 처리되는 동안에만 존재하기도 한다. makeFunc() 실행이 끝나면 name 변수에 더 이상 접근할 수 없게 될 것으로 예상할 수도 있다. 하지만 자바스크립트의 경우는 다르다. 그 이유는 자바스크립트는 함수를 리턴하고, 리턴하는 함수가 외부 함수의 스코프를 기억하기 때문이다. 클로저는 함수와 함수가 선언된 렉시컬(lexical) 스코프를 기억하는 것이다. 첫 번째 예시의 경우, myFunc은 makeFunc이 실행될 때 생성된 displayName 함수의 참조다. displayName은 변수 name 이 있는 렉시컬 스코프를 참조 한다. 이런 이유로 myFunc가 호출될 때 변수 name은 사용할 수 있는 상태로 남게 되고 "closure" 가 console에 전달된다.

참고자료 - https://meetup.toast.com/posts/86
참고자료 - https://developer.mozilla.org/ko/docs/Glossary/Scope
참고자료 - https://www.zerocho.com/category/JavaScript/post/5740531574288ebc5f2ba97e
참고자료 - https://en.wikipedia.org/wiki/Scope_(computer_science)

profile
프론트앤드 개발자 입니다.

0개의 댓글