본문 바로가기
🦎모던 자바스크립트 Deep Dive

[Chap 13] 스코프

by egg.silver 2024. 5. 1.


13.1 스코프란?

- 스코프(유효범위)는 변수, 함수에 깊은 관련이 있음

 

1.  스코프는 식별자가 유효한 범위 의미
➔ 모든 식별자는 (변수 이름. 함수 이름. 클래스 이름 등) 자신이 선언된 위치에 의해
     다른 코드가 식별자 자신을 참조할 수 있는 유효   범위가 결정됨

var var1 = 1; // 코드의 가장 바깥 영역에서 선언한 변수

if (true) {
  var var2 = 2; // 코드 블록 내에서 선언한 변수
  if (true) {
    var var3 = 3; // 중첩된 코드 블록 내에서 선언한 변수
  }
}

function foo() {
  var var4 = 4; // 함수 내에서 선언한 변수
  function bar() {
    var var5 = 5; // 중첩된 함수 내에서 선언한 변수
  }
}

console.log(var1); // 1
console.Iog(var2); // 2
console.Iog(var3); // 3
console.log(var4); // ReferenceError: var4 is not defined
console.log(var5); // ReferenceError: var5 is not defined

 

 

2.  스코프는 식별자를 검색할 때 사용하는 규칙
➔ 자바스크립트 엔진은 이름이 같은 두 개의 변수 중에서 어떤 변수를 참조해야 할 것인지를 결정해야 하는데,
     이를 식별자 결정(identifier resolution)이라고 함

➔ 자바스크립트 엔진은 코드를 실행할 때 코드의 문맥(context)을 고려하기 때문에 동일한 코드도 다른 결과를 만들어 냄

var x = 'global';

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

foo();

console.log(x); // global

 

 

 

      3.  스코프는 네임스페이스
➔ 스코프(유효 범위)를 통해 식별자인 변수 이름의 충돌을 방지하여같은 이름의 변수를 사용할 수 있게 함

➔ 스코프 내에서 식별자는 유일해야 하지만 다른 스코프에는 같은 이름의 식별자 사용 가능
(컴퓨터를 사용할 때 식별자인 파일 이름을 중복해서 사용할 수 이유는 폴더(디렉터리)라는 개념이 있기 때문, 이와 같은 맥락임)

 


13.2 스코프의 종류

- 코드는 전역(global)과 지역(local)으로 구분 가능

 

13.2.1 전역과 전역 스코프

- 전역 : 코드의 가장 바깥 영역

- 전역에 선언된 전역 변수는 어디서든지 참조 가능

 

 

13.2.1 지역과 지역 스코프

- 지역 : 함수 몸체 내부

- 지역 변수는 자신의 지역 스코프와 하위 지역 스코프에서 유효함

 


13.3 스코프 체인

- 중첩 함수 : 함수 몸체 내부에서 정의한 함수

- 외부 함수 : 중첩 함수를 포함하는 함수

 

➔  함수 중첩 가능 = 스코프 중첩 가능
➔  스코프가 함수의 중첩에 의해 계층적 구조 가짐

 

스코프 체인

- outer 함수의 지역과 inner 함수의 지역이 존재
- inner 함수는 outer 함수의 중첩 함수
- outer 함수가 만든 지역 스코프는 inner 함수가 만든 지역 스코프의 상위 스코프
- outer 함수의 지역 스코프의 상위 스코프는 전역 스코프임

 

스코프 체인(scope chain) : 스코프가 계층적으로 연결된 것
└ 모든 스코프는 하나의 계층적 구조로 연결되며, 모든 지역 스코프의 최상위 스코프는 전역 스코프임

 

➔  변수를 참조할 때, 자바스크립트 엔진스코프 체인을 통해 변수를 참조하는 코드의 스코프에서 시작하여, 상위 스코프 방향으로 이동하며 선언된 변수를 검색함(identifier resolution)

➔  자바스크립트 엔진코드(전역 코드와 함수 코드)를 실행하기에 앞서 위 그림과 유사한 자료구조인 렉시컬 환경을 실제로 생성

*렉시컬 환경(Lexical Environment)
: 코드 block, function, script를 실행하기 앞서 생성되는 특별한 객체로, 실행할 스코프 범위 안에 있는 변수와 함수를 프로퍼티로 저장하는 객체

 

 

13.3.1 스코프 체인에 의한 변수 검색

더보기


위 그림의 4, 5, 6 참고

- ④ x 변수를 참조하는 코드의 스코프인 inner 함수의 지역 스코프에서 x 변수가 선언되었는지 검색
   inner 함수 내에는 선언된 x 변수가 존재하기 때문에 검색된 변수를 참조하고 검색 종료

 

- ⑤ y 변수를 참조하는 코드의 스코프인 inner 함수의 지역 스코프에서 y 변수가 선언되었는지 검색

   inner 함수 내에는 y 변수의 선언이 존재하지 않으므로 상위 스코프인 outer 함수의 지역 스코프로 이동
   outer 함수 내에도 y 변수의 선언이 존재하지 않으므로 또 다시 상위 스코프인 전역 스코프로 이동
   전역 스코프에는 y 변수의 선언이 존재하기 때문에 검색된 변수를 참조하고 검색 종료

 

- ⑥ z 변수를 참조하는 코드의 스코프인 inner 함수의 지역 스코프에서 z 변수가 선언되었는지 검색
   inner 함수 내에는 z 변수의 선언이 존재하지 않으므로 상위 스코프인 outer 함수의 지역 스코프로 이동
   outer 함수 내에는 z 변수의 선언이 존재하기 때문에 검색된 변수를 참조하고 검색 종료

자바스크립트 엔진스코프 체인을 따라 변수를 참조하는 코드의 스코프에서 시작해서 상위 스코프 방향으로 이동하며 선언된 변수를 검색➔  상위 스코프에서 유효한 변수는 하위 스코프에서 자유롭게 참조 가능
➔  하위 스코프에서 유효한 변수를 상위 스코프에서 참조 불가능

 

13.3.2 스코프 체인에 의한 함수 검색

// 전역 함수
function foo() {
  console.log('global function foo');
}

function bar() {
  // 중첩 함수
  function foo() {
    console.log('local function foo');
  }
  foo(); // 'local function foo'
}

bar(); // 'local function foo'


// bar() 함수가 실행될 때마다 내부적으로 정의된 로컬 foo 함수가 호출되며, 
// 전역 foo 함수는 이 코드 실행 동안에는 호출되지 않음
// 전체 코드 실행 과정에서 로컬 foo 함수만이 두 번 호출되어, 두 번 모두 'local function foo'가 출력

 

- 함수 선언문으로 함수 정의 시 런타임 이전에 함수 객체 생성 후, 자바스크립트 엔진에 의하여 함수 이름과 동일한 식별자에 할당됨

- 함수도 식별자에 할당되기 때문에 스코프를 가짐
-  함수 객체가 할당된 것 이외에는 일반 변수와 다를 바가 없기 때문에, 스코프는 식별자를 검색하는 규칙이라고 표현하는 것이 적합


13.4 함수 레벨 스코프

- 지역 : 함수 몸체 내부를 의미하며 지역은 지역 스코프를 만듦

- 코드 블록이 아닌 함수에 의해서만 지역 스코프가 생성

 

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

 : C Java 등의 대부분의 프로그래밍 언어는 함수 몸체만이 아니라 모든 코드 블록( if, for, while, try/catch ) 지역 스코프를 만듦

 

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

 : var 키워드로 선언된 변수는 오로지 함수의 코드 블록(함수 몸체)만을 지역 스코프로 인정

var x = 1;
if (true) {
  // var 키워드로 선언된 변수는 함수의 코드 블록(함수 몸체)만을 지역 스코프로 인정
  // 함수 밖에서 var 키워드로 선언된 변수는 코드 블록 내에서 선언되었다 할지라도 모두 전역 변수
  // 따라서 x는 전역 변수
  // 이미 선언된 전역 변수 x가 있으므로 x 변수는 중복 선언됨
  // 이는 의도치 않게 변수 값이 변경돠는 부작용 발생

  var x = 10;
}
console.log(x); // 10

└ if 문의 코드 블록 내에서 선언된 x는 전역 변수임

var 키워드로 선언된 변수는 함수 레벨 스코프만 인정하기 때문

 

var i = 10;
// for 문에서 선언한 i는 전역 변수
//이미 선언된 전역 변수 i가 있으므로 중복 선언

for (var i = 0; i < 5; i++) {
console.log(i); // 0 1 2 3 4
}
// 의도치 않게 변수의 값이 변경됨
console.log(i); // 5

var 키워드로 선언된 변수는 블록 레벨 스코프를 인정하지 않기 때문에 i 변수는 전역 변수
 따라서 전역 변수 i는 중복 선언되고 그 결과 의도치 않은 전역 변수의 값이 재할당됨

var 키워드로 선언된 변수는 오로지 함수의 코드 블록만을 지역 스코프로 인정하지만
ES6에서 도입된 let, const 키워드는 블록 레벨 스코프를 지원


13.5 렉시컬 스코프

-동적 스코프(dynamic scope) : 함수를 어디서 호출했는지에 따라 함수의 상위 스코프 결정

-렉시컬 스코프(lexical scope) : 함수를 어디서 정의했는지에 따라 함수의 상위 스코프 결정

 

➔  자바스크립트는 렉시컬 스코프를 따름

var x = 1;

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

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

foo(); // 1
bar(); // 1

 bar 함수는 전역에서 정의된 함수
└ 함수 선언문으로 정의된 bar 함수는 전역 코드가 실행되기 전에 먼저 평가되어 함수 객체를 생성
└ 이때 생성된 bar 함수 객체는 자신이 정의된 스코프, 즉 전역 스코프 기억

└ 때문에 위 예제를 실행하면 전역 변수 x의 값 1을 두 번 출력