ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Notes from reading 🔖 You Don't Know JS Yet - 8
    JavaScript 2025. 12. 27. 17:11

    ### 파트 2 - chapter 3 스코프 체인

     

    • 스코프와 중첩 스코프 사이에 맺어진 연결 -> 스코프 체인
    • 변수 접근 시 스코프 체인을 기반으로 변수를 찾는데, 위/밖으로만 찾음
    • 렉시컬 스코프는 컴파일 초기에 확정 -> 런타임에서는 이미 정해진 렉시컬 스코프를 활용하여 변수를 탐색한다
    • 런타임에 찾지 않기에, 효율적 👽
    • 다만, 런타임에 다른 프로그램이 변수를 전역에 선언할 가능성 등이 있기에, 런타임 도중에 스코프가 정해지는 경우도 있다
    • 따라서 완전히 확정되는 것은 런타임중이긴 함

     

    • 이름은 같은 두 개의 변수가 하나의 스코프에 동시에 존재할 수는 없다
    • 같은 이름의 변수를 굳이 사용하고자 한다면, 스코프를 분리해야 한다
    • 변수를 찾는 순서는 현재 위치부터 위로 올라가기에, 동일한 이름의 변수가 지역, 전역에 모두 있는 경우 지역에 있는 변수가  사용된다
    • 이 경우 전역에 있는 변수는 가려지게 되는데, 이를 변수 섀도잉이라고 한다 🐦
    • 다만, 지역/전역에 같은 이름의 변수가 있더라도 전역의 변수를 참조할 수 있는 꼼수 방법은 있다 (전역 언섀도잉)
    • window. 과 같이 전역 객체를 활용하여 전역 스코프의 변수를 직접 접근하는 방법이다. (비추천되긴 함. var나 function 변수만 가능)

     

    • 원시값 복사가 아닌 객체 참조를 통해서 외부 프로퍼티에 직접 접근할 수 있다는 관점은 틀림.
    • 참조 복사본을 통해 객체값을 수정하는 것은, 렉시컬 환경을 고려하여 변수 자체에 접근하는 것이 아님. (렉시컬 스코프는 그대로임)

     

    • 섀도잉이 불가능한 조합도 있는데, let은 var를 가릴 수 있지만, var는 let을 가릴 수 없음 (구문 오류 발생)
    • 함수 경계가 있는 경우에만 var는 let을 가릴 수 있다
    • var 변수에 화살표 함수를 할당하는 경우에는, 함수 자체는 호이스팅 되지 않음
    • 변수에 function 선언 함수를 할당하면, function 함수에 이름을 써줘도 읽기전용으로 선언되어 접근할 땐 변수 이름만 사용이 가능하다 (기명함수 표현식)
    • 이 경우에 function 선언 함수의 이름을 써주지 않으면, 익명함수 표현식이 되고 스코프에 영향을 미치는 이름 식별자 자체가 없게 된다.

     

    • ES6의 화살표 함수가 되입되었다. 이를 사용하면 function을 사용하지 않고 함수를 선언할 수 있다.
    • 화살표 함수는 렉시컬 스코프 관점에서는 익명이다.
    • 화살표 함수는 function 선언 함수와 동일하게 렉시컬 스코프의 규칙을 따름
    • 화살표 함수는 익명이라는 특성을 빼면, function을 사용해 선언한 함수처럼 렉시컬 스코프 규칙을 받음.

     

     

    /**
     * [1] 전역 스코프
     * 이 파일 전체가 하나의 렉시컬 스코프
     */
    var globalVar = "global var";
    let globalLet = "global let";
    
    const globalObj = { count: 0 };
    
    /**
     * [2] 함수 선언
     * → 이 함수의 렉시컬 스코프는 "정의 시점"에 전역 스코프로 확정됨
     */
    function outer() {
      /**
       * [3] outer 함수 스코프
       * 전역과는 분리된 새로운 스코프
       */
      var outerVar = "outer var";
      let outerLet = "outer let";
    
      // 전역 변수 접근 가능 (스코프 체인을 따라 위로 탐색)
      console.log(globalVar);
      console.log(globalLet);
    
      /**
       * [4] 블록 스코프 (let / const 전용)
       */
      {
        let blockLet = "block let";
    
        // blockLet은 여기서만 접근 가능
        console.log(blockLet);
    
        // outer 스코프 변수 접근 가능
        console.log(outerLet);
    
        // ❌ var는 블록 스코프를 만들지 않음
        var leakedVar = "I am function scoped";
      }
    
      // blockLet; // ReferenceError
      console.log(leakedVar); // 가능 (var는 함수 스코프)
    
      /**
       * [5] 변수 섀도잉
       * 같은 이름의 변수가 더 안쪽 스코프에 존재
       */
      let shadow = "outer shadow";
    
      function inner() {
        let shadow = "inner shadow";
    
        // 가장 가까운 스코프의 shadow가 사용됨
        console.log(shadow); // "inner shadow"
    
        // 전역까지 올라가며 탐색 가능
        console.log(globalVar);
      }
    
      inner();
    
      /**
       * [6] 전역 언섀도잉 (비추천)
       * var / function 으로 선언된 전역 변수만 가능
       */
      var globalVar = "outer globalVar shadow";
    
      console.log(globalVar);        // outer globalVar shadow
      console.log(window.globalVar); // 진짜 전역 globalVar
    
      /**
       * [7] 객체 참조와 스코프는 무관
       */
      function updateObject(obj) {
        // obj라는 식별자는 이 함수 스코프의 지역 변수
        // 하지만 참조값을 통해 외부 객체의 프로퍼티를 수정할 수 있음
        obj.count++;
      }
    
      updateObject(globalObj);
      console.log(globalObj.count); // 1
    
      /**
       * [8] 기명 함수 표현식
       */
      const namedFn = function realName() {
        // realName은 함수 내부에서만 접근 가능 (읽기 전용)
        console.log("inside:", realName.name);
      };
    
      namedFn();
      // realName(); // ReferenceError
    
      /**
       * [9] 화살표 함수
       * - 익명 함수
       * - 렉시컬 스코프 규칙은 function과 동일
       */
      const arrowFn = () => {
        console.log("arrow:", outerLet);
      };
    
      arrowFn();
    }
    
    /**
     * [10] 함수 실행
     * 호출 위치는 렉시컬 스코프에 영향 없음
     */
    outer();
    
    /**
     * [11] var + 화살표 함수 호이스팅 차이
     */
    console.log(hoistedArrow); // undefined
    
    var hoistedArrow = () => {
      console.log("not hoisted as function");
    };
Designed by Tistory.