8/18/2023

Scope & Scope Chain

Scope

스코핑(scoping)은 "변수들은 어디에 존재하는가?, "특정 변수를 어디에서 접근할 수 있고 어디에서는 접근할 수 없나?"와 같이 프로그램의 변수가 어떻게 조직화되고 접근되는지 제어한다. 렉시컬(lexical) 스코핑은 변수가 어떻게 구성되고 접근되는지를 결정하는 것이 전적으로 프로그램 코드 내에서 함수와 블록의 배치에 의해 제어된다는 것을 의미한다. 스코프는 특정 변수가 선언된 공간 또는 환경이다. 함수의 경우 함수의 실행 컨텍스트에 저장된 변수 환경이기도 하다. 스코프는 전역 스코프, 함수 스코프 및 블록 스코프 총 3개가 존재한다. 변수의 스코프는 기본적으로 특정 변수에 접근할 수 있는 코드 영역 전체이다. 스코프와 변수의 스코프는 비슷하지만 같지않다.
  • 전역 스코프: 최상위 레벨 코드에 대한 스코프로 어느 함수나 블록 내부가 아닌 곳에서 선언된 변수로 프로그램 전체에서 어디서든 접근할 수 있다.
  • 함수 스코프: 함수마다 생성되는 스코프로 함수 스코프 내에서 선언된 변수는 해당 함수 안에서만 접근할 수 있다. 전역 스코프와 대조적으로 지역 스코프라고도 부른다. 기술적으로 함수의 변수 환경과 동일하다. 함수의 종류(함수 선언식, 함수 표현식, 화살표 함수)와 관계없이 생성된다.
  • 블록 스코프: ES6부터 블록도 스코프를 생성할 수 있으며 if 문, for 문과 같이 중괄호로 둘러싸인 모든 것을 의미한다. 함수와 마찬가지로 블록 내에서 선언된 변수들은 해당 블록 안에서만 접근할 수 있다. 중요한 점은 블록 스코프가 let 또는 const로 선언된 변수에만 적용된다는 것이다. 하지만 블록에서 var를 사용하여 변수를 선언한다면 변수는 사실 블록 외부에서도 여전히 접근할 수 있기에 현재 함수 스코프에 지정된다. ES6부터 strict 모드에서 함수 역시 블록 스코프를 가지며 let, const 변수처럼 블록 안에 선언된 함수는 블록 안에서만 접근할 수 있다.


Scope Chain

https://dasha.ai/en-us/blog/javascript-scope-and-scope-chain

스코프 체인이란 모든 스코프는 항상 외부 스코프의 모든 변수에 접근할 수 있다는 것이다. 즉, 모든 부모 스코프에 접근할 수 있다. 하나의 스코프가 특정 변수를 사용해야 하는데 현재 스코프에서 해당 변수를 찾을 수 없는 경우 변수를 부모 스코프 중 하나에서 찾는다. 만약 찾을 수 있다면, 그 변수를 사용할 것이지만 찾을 수 없다면 오류가 발생한다. 이 과정을 변수 검색(variable lookup)이라고 한다. 매우 중요한 점은 변수 검색/스코프 체인이 반대로 작동하지 않는다는 것이다. 즉, 항상 단방향으로 어떤 스코프도 절대로 자식 스코프 즉, 내부 스코프의 변수에 접근할 수 없다. 또한, 렉시컬 스코핑의 규칙에 따라 한 스코프의 자식 스코프는 서로의 변수에 접근할 수 없다. 한 스코프가 다른 스코프 안에 있지 않기 때문이다. 특정 스코프의 스코프 체인은 모든 부모 스코프의 모든 변수 환경을 더한 것과 같다고 볼 수 있다(즉, 스코프는 변수 환경보다 더 넓을 수 있다.). 함수 호출 순서와 상관있는 호출 스택과 달리 스코프 체인은 함수가 호출된 순서와는 관련이 없다. 즉, 스코프 체인은 호출 스택 내 실행 컨텍스트의 순서와는 아무런 관련이 없다. 스코프 체인은 함수가 코드에 작성된 순서와 관련이 있다.


Exercise

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
"use strict";
 
function caclBmi(weight, height) {
  // bmi 변수의 스코프는 caclBmi 블록.
  const bmi = weight / (height * height);
 
  // username을 찾을 수 없기 때문에 변수 검색(lookup)을 실행한다, 부모 스코프(전역 스코프)
  console.log(username);
 
  // password는 부모 스코프(전역 스코프)에 존재하지 않기 때문에 오류가 발생한다.
  // console.log(password);
 
  function printBmi() {
    // bmi 변수 검색, 부모 스코프(calBmi 스코프)
    // username 변수 검색, 부모 스코프(전역 스코프)
    let output = `${username}님의 체질량지수는 ${bmi} 입니다.`;
    console.log(output);
 
    if (bmi >= 18.5 && bmi <= 22.9) {
      var normal = true;
      // JavaScript는 항상 현재 스코프에서 검색한다.
      // 외부 스코프 변수와 같은 이름을 가진 변수를 생성한다.
      const username = "박선심";
 
      // 외부 스코프 변수의 값을 재할당한다.
      output = "Anvity Gravity Device!";
 
      const text = `${username}님 정상 체중입니다!`;
      console.log(text);
 
      function troll(username) {
        return `${username}은 비정상!`;
      }
    }
 
    // const와 let은 블록 스코프이기 때문에 오류가 발생한다.
    // text 변수의 스코프는 if 블록.
    // console.log(text);
 
    // var는 함수 스코프이기 때문에 정상적으로 동작한다.
    // normal 변수의 스코프는 caclBmi 블록.
    console.log(normal);
 
    // ES6부터 strict 모드에서 함수도 블록 스코드 따라서 오류가 발생한다.
    // troll 함수의 스코드 if 블록.
    // console.log(troll("박선심"));
 
    //  let output: 내부 스코프에서 이미 존재하는 변수를 수정한다.
    console.log(output); // Anvity Gravity Device!
    // const output: 새로운 변수 생성 따라서 외부 스코프 변수에 영향을 주지 않는다.
    // console.log(output);
  }
 
  printBmi();
  return bmi;
}
 
const username = "chang";
caclBmi(651.7);
// 변수 검색은 단방향(내부 스코프만 외부 스코프 접근 가능), 따라서 오류가 발생한다.
// console.log(bmi);
// printBmi();
cs

Update: 2023-09-04

댓글 없음:

댓글 쓰기