스코프(Scope)

이재홍·2022년 12월 27일
0

JavaScript

목록 보기
3/10
post-thumbnail

스코프(Scope) 란?

: 스코프란 자바스크립트 엔진이 참조의 대상이 되는 식별자(Identifier)를 검색할 때 사용하는 규칙의 집합 이다. 즉, 어떤 변수를 사용하거나 함수를 호출하려고 할 때 해당하는 식별자로 사용하는데, 그 식별자를 검색하는 메커니즘이라고 이해하면 된다.

좀 더 근본적으로 Scope를 우리말로 번역하면 범위라는 뜻을 가지고 있다.
즉, 스코프(Scope)변수에 접근할 수 있는 범위라고 할 수 있다.

var x = "global";

function foo() {
  var x = "function scope";
  console.log(x);
}

foo(); // ?
console.log(x); // ?

위와 같은 코드를 보고 어떤 결과가 나올까를 생각해보자🤔

이름이 같은 변수 x가 중복 선언되었다. 전역에서 변수 x를 참조할 때, 그리고 함수 foo 내부에서 변수 x를 참조할 때 이름이 중복된 2개의 변수 중 어떤 변수를 참조해야 하는가?
자바스크립트는 어떻게 변수를 식별하는 것일까?

스코프는 참조 대상 식별자(identifier, 변수, 함수의 이름과 같이 어떤 대상을 다른 대상과 구분하여 식별할 수 있는 유일한 이름)를 찾아내기 위한 규칙 이라는 설명이 좀 더 이해가 갈 것이다.

함수 레벨 스코프 ( function-level scope )

자바스크립트의 스코프는 타 언어와는 다른 특징을 가지고 있다.
대부분의 C-family language(C언어 기반)는 블록 레벨 스코프(block-level scope)를 따른다.
하지만 자바스크립트는 함수 레벨 스코프(function-level scope)를 따른다.
함수 레벨 스코프란 함수 코드 블록 내에서 선언된 변수는 함수 코드 블록 내에서만 유효하고 함수 외부에서는 유효하지 않다.

var x = 0;
{
  var x = 1;
  console.log(x); // 1 함수
}
console.log(x); // 1 전역

let y = 0;
{
  let y = 1;
  console.log(y); // 1 함수
}
console.log(y); // 0 전역

비 블록 레벨 스코프(Non block-level scope)

if (true) {
  var x = 5;
}
console.log(x);

변수 x는 코드 블록 내에서 선언되었다. 하지만 자바스크립트는 블록 레벨 스코프를 사용하지 않으므로
함수 밖에서 선언된 변수는 코드 블록 내에서 선언되었다할지라도 모두 전역 스코프을 갖게된다.
따라서 변수 x는 전역 변수이다.

단, ECMAScript 6에서 도입된 let keyword를 사용하면 블록 레벨 스코프를 사용할 수 있다.

블록 레벨 스코프 (Block-level scope)

for (let i = 0; i < 5; i++) {
  console.log(i);
}
// 블록 스코프 안에서 정의된 변수 i는 블록 범위를 벗어나는 즉시 접근할 수 없다.
console.log("final i:", i); // ReferenceError
  • let과 const 키워드는 블록 스코프를 따른다.
  • var 키워드는 블록 스코프를 무시하고, 함수 스코프만 따른다. (단, 화살표 함수의 블록 스코프는 무시하지 않는다)

중첩 스코프는 가장 인접한 지역을 우선하여 참조한다

var foo = function () {
  var a = 3,
    b = 5;
  var bar = function () {
    var b = 7,
      c = 11;

    // 이 시점에서 a는 3, b는 7, c는 11
    a += b + c;

    // 이 시점에서 a는 21, b는 7, c는 11
  };
  // 이 시점에서 a는 3, b는 5, c는 not defined
  bar();

  // 이 시점에서 a는 21, b는 5
};

스코프의 종류(Global, Local)

전역 스코프(Global Scope) 와 전역 변수 (Global variable)

전역 스코프(Global Scope)는 말 그대로 전역에 선언되어있어 어느 곳에서든지 해당 변수에 접근할 수 있다,
전역 변수 (Global variable)는 전역에서 선언된 변수이며 어디에든 참조할 수 있다.

지역 스코프(Local Scope)와 지역 변수 (Local variable)

지역 스코프(Local Scope)는 해당 지역에서만 접근할 수 있어 지역을 벗어난 곳에선 접근할 수 없다,
지역 변수 (Local variable)는 지역(함수) 내에서 선언된 변수이며 그 지역과 그 지역의 하부 지역에서만 참조할 수 있다.

전역 스코프, 지역(함수) 스코프를 더 자세히 알아보자

var a = 1; // 전역 스코프
function print() {
  // 지역(함수) 스코프
  var a = 111;
  console.log(a);
}
print(); // 111
console.log(a); // 1

print함수를 호출하면 console엔 1이 출력될까요? 아니면 111이 출력될까?

print함수를 호출하면 111이 출력되는 것을 볼 수 있다.

print 함수에서 console.log(a);a를 출력하기 위해 자신의 함수 스코프 안에 변수 a가 있는지 찾아볼 거다.
그러면 var a = 111; 을 찾아내면 111을 console에 출력하고 함수는 자신의 사명을 다하게 된다.

변수 중복

자바스크립트는 타 언어와는 달리 특별한 시작점(Entry point)이 없어서 위 코드와 같이 전역에 변수나 함수를 선언하기 쉽다.
따라서 전역에 변수를 선언하기 쉬우며 이것는 전역 변수를 남발하게 하는 문제를 야기시킨다.
전역 변수의 사용은 변수 이름이 중복될 수 있고, 의도치 않은 재할당에 의한 상태 변화로 코드를 예측하기 어렵게 만드므로 사용을 억제하여야 한다

지역 변수는 전역 변수보다 더 높은 우선순위를 가진다.

렉시컬 스코프 (Lexical scope)

렉시컬 스코프는 함수를 어디서 호출하는지가 아니라 어디에 선언하였는지에 따라 결정된다.
자바스크립트는 렉시컬 스코프를 따르므로 함수를 선언한 시점에 상위 스코프가 결정된다.
함수를 어디에서 호출하였는지는 스코프 결정에 아무런 의미를 주지 않는다.

var x = 1;

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

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

foo(); // ?
bar(); // ?

예제의 함수 bar는 전역에 선언되었다. 따라서 함수 bar의 상위 스코프는 전역 스코프이고 위 예제는 전역 변수 x의 값 1을 두번 출력한다.

암묵적 전역

var x = 10; // 전역 변수

function foo() {
  // 선언하지 않은 식별자
  y = 20;
  console.log(x + y);
}

foo(); // 30

y는 선언하지 않은 식별자, 따라서 y = 20이 실행되면 참조 에러가 발생할 것처럼 보인다.
하지만 선언하지 않은 식별자 y는 마치 선언된 변수처럼 동작한다. 이는 선언하지 않은 식별자에 값을 할당하면 전역 객체의 프로퍼티가 되기 때문이다. (전역변수가 된다.)

foo 함수가 호출되면 자바스크립트 엔진은 변수 y에 값을 할당하기 위해 먼저 스코프 체인을 통해 선언된 변수인지 확인.
이때 foo 함수의 스코프와 전역 스코프 어디에서도 변수 y의 선언을 찾을 수 없으므로 참조 에러가 발생해야 하지만, 자바스크립트 엔진은 y = 20window.y = 20으로 해석.
결국 y는 전역 객체의 프로퍼티가 되어 마치 전역 변수처럼 동작
이러한 현상을 암묵적 전역(implicit global)이라 한다.

하지만 y는 변수 선언없이 단지 전역 객체의 프로퍼티로 추가되었을 뿐이다. 따라서 y는 변수가 아니다. 따라서 변수가 아닌 y는 변수 호이스팅이 발생하지 않는다. (아래 예제 확인)

// 전역 변수 x는 호이스팅이 발생한다.
console.log(x); // undefined
// 전역 변수가 아니라 단지 전역 프로퍼티인 y는 호이스팅이 발생하지 않는다.
console.log(y); // ReferenceError: y is not defined

var x = 10; // 전역 변수

function foo() {
  // 선언하지 않은 변수
  y = 20;
  console.log(x + y);
}

foo(); // 30

스코프체인(Scope Chain)

현재 스코프에서 식별자를 검색할 때 상위 스코프를 연쇄적으로 찾아나가는 방식,
실행컨텍스트의 개념을 알아야한다.

  1. 현재 실행 컨텍스트의 LexicalEnvironmentEnvironmentRecord에서 식별자를 검색한다.
  2. 없으면 outer 참조 값으로 스코프 체인을 타고 올라가 상위 스코프의 EnvironmentRecord에서 식별자를 검색한다.
  3. 이를 outer 참조 값이 null 일 때까지 계속하고 찾지 못한다면 에러를 발생시킨다.


모던 자바스크립트 - 스코프
Velog 이미지 참조 - 스코프
스코프 예제

0개의 댓글