[ECMAScript] Scope, Function Scope, Block Scope, Global Scope, Lexical Scope

Featured image

Javascript 에서 Scope 는 변수의 유효범위라고 할 수 있다.

var은 함수 스코프 범위를, let/const 는 블록 스코프 범위를 갖는다.

블록 스코프

if (true) { // if 의 block scope
  const message = 'Hello';
  console.log(message); // 'Hello'
}
console.log(message); // Uncaught ReferenceError: message is not defined

위의 코드를 실행하면 첫번째 console.log(message) 는 정의된 블록 스코프 내에서 message 변수를 접근하기 때문에 올바르게 Hello 라는 console을 출력한다.

그러나 두번째 console.log(message) 는 블록 스코프 외부에서 변수에 접근하기 때문에 참조 오류(ReferenceError)가 발생한다.

if, for, while 도 블록 스코프를 생성하기 때문에 해당 블록 스코프 외부에서 참조한다면 동일하게 참조 오류를 발생할 것 이다.

또한 독립적인 실행형 코드 블록도 생성할 수 있는데,

{
    const message = 'Hello';
    console.log(message); // 'Hello'
}
console.log(message); // Uncaught ReferenceError: message is not defined

위의 코드에서도 동일하게 블록 스코프 외부의 console.log(message) 에서는 참조 오류가 발생할 것 이다.

그러나 위에서 말했던 것과 같이 var 은 블록 스코프 범위가 아닌 함수 스코프 범위이므로 위의 예제를 var 로 바꿨을 경우 오류가 발생하지 않는다.

if (true) {
  var message = 'Hello';
  console.log(message); // 'Hello'
}
console.log(message); // 'Hello'

함수 스코프

function scopeTest() { // run 함수 스코프
  var message = 'Hello';
  console.log(message); // 'Hello'
}
scopeTest();
console.log(message); // Uncaught ReferenceError: message is not defined

scopeTest 함수 내부의 범위에서 스코프를 형성 한다. 변수 message 는 함수 범위 내부에서 접근 할 수 있지만 외부에서는 접근 할 수 없다.

같은 방식으로 let, const, 함수 선언도 함수 스코프 범위가 형성된다.

function scopeTest() {
    const one = 1;
    let two = 2;
    function three() {}

    console.log(one); // 1
    console.log(two); // 2
    console.log(three); // f three() {}
}
scopeTest();
console.log(one); // Uncaught ReferenceError
console.log(two); // Uncaught ReferenceError
console.log(three); // Uncaught ReferenceError

스코프 중첩

스코프의 특성중 하나는 중첩이 가능하다는 것이다.

function scopeTest() { // "scopeTest" 함수 스코프
  const message = 'Hello';
  
  if (true) { // "if" 블록 스코프
    const friend = 'min';
    console.log(message); // 'Hello'
  }

  console.log(friend); // Uncaught ReferenceError
}
run();

if 의 블록 스코프는 scopeTest 함수 스코프 내에 중첩된다. (모든 유형의 스코프는 중첩이 가능)

다른 스코프에 포함된 스코프를 내부 스코프라고 하고, 다른 스코프를 감싸고 있는 것을 외부 스코프라 한다.

예제에서 scopeTest 함수는 if 블록 스코프의 외부 스코프 이다.

내부 스코프에서 외부 스코프의 변수에 접근이 가능하다.

위의 예제에서 외부 스코프의 message 변수는 if 블록 스코프 내부에서 접근이 가능하기 때문에 console.log(message) 는 정상적으로 Hello 를 출력한다.

전역 스코프

전역 스코프는 가장 외부의 스코프이다. 그렇기 때문에 모든 내부 범위에서 접근이 가능하다.

전역 스코프에 선언된 변수를 전역 변수라고 하고, 전역 변수는 웹 페이지의 자바스크립트의 모든 위치에서 접근 할 수 있다.

Browser 에서는 window, document 라는 전역 변수를 제공하고, Node 환경에서는 프로세스 객체에 전역 변수로 접근할 수 있다.

렉시컬 스코프

렉시컬 스코프는 함수를 어디서 호출하는지가 아닌 어디에 선언하였는지에 따라 결정되는 것을 의미한다.

프로그래밍 언어에서 두 가지 방식 중 하나의 방식으로 상위 스코프를 결정하는데,

첫번째 방식은 어디서 호출하였는지에 따라 상위 스코프를 결정하는 동적 스코프(Dynamic Scope)이고, 두번째 방식은 어디서 선언하였는지에 따라 상위 스코프를 결정하는 렉시컬 스코프(Lexical Scope) 또는 정적 스코프(Static Scope)라고 한다.

JavaScript 에서는 렉시컬 스코프를 따르므로 함수를 선언한 시점에 상위 스코프가 결정된다.

var x = 1;

function foo() {
    var x = 10;
    bar();
}

function bar() {
    console.log(x);
}

foo();
bar();

위의 예제에서 실행 결과는 1, 1 이 출력된다.

렉시컬 스코프(어디서 선언하였는지에 따라 상위 스코프 결정)에 의하면

bar() 함수가 foo() 함수 안에서 호출된 것과는 상관 없이 bar() 함수는 global 범위에 선언되어 있으므로,

global 범위에 있는 변수 x 의 값에 의하여 1 이 두번 출력 되는 것이다.


Reference