ABOUT ME

작은 디테일에 집착하는 개발자

Today
-
Yesterday
-
Total
-
  • [JavaScript] 문제를 통해 `실행 컨텍스트, 클로저, 스코프` 이해하기
    IT Study/FE 2023. 9. 15. 16:57
    728x90

    문제 풀기 바로 시작

     

    1. 다음 코드 조각을 살펴보고, 각 console.log 라인에서 출력될 값과 그 이유를 설명하세요.

    var a = 10;
    
    function outer() {
      var b = 20;
      
      function inner() {
        var c = 30;
        console.log(a); // 1번 라인
        console.log(b); // 2번 라인
        console.log(c); // 3번 라인
      }
    
      inner();
      console.log(a); // 4번 라인
      console.log(b); // 5번 라인
      console.log(c); // 6번 라인
    }
    
    outer();
    console.log(a); // 7번 라인
    console.log(b); // 8번 라인
    console.log(c); // 9번 라인
    라인 답변 위치 접근 여부
    1 10 전역 스코프에 위치 inner → 전역 접근 가능
    2 20 outer 함수의 지역(함수) 스코프에 위치 inner → outer 접근 가능
    3 30 inner 함수의 지역(함수) 스코프에 위치 inner → inner 접근 가능
    4 10 전역 스코프에 위치 outer→ 전역 접근 가능
    5 20 outer 함수의 지역(함수) 스코프에 위치 outer → outer 접근 가능
    6 ReferenceError   outer → inner 접근 불가
    7 10 전역 스코프에 위치 전역 → 전역 접근 가능
    8 ReferenceError   전역 → outer 접근 불가
    9 ReferenceError   전역 → inner 접근 불가

     

    2. 다음 코드를 보고 예상되는 출력과 그 이유를 설명하세요.

    var foo = 10;
    
    function bar() {
      console.log(foo);
      var foo = 20;
      console.log(foo);
    }
    
    bar();
    답변
    undefined 
    // bar 함수의 지역(함수) 스코프 내에 위치한 var foo;(선언)을 호이스팅 하여 undefined 출력
    20               // bar 함수의 지역(함수) 스코프 내에 위치한 foo = 20;(초기화)로 20 출력

     

    3. 아래 코드를 분석하고, 각각의 console.log 문에서 출력될 값과 그 이유를 설명해 주세요.

    function outer() {
      var a = 'outer';
      var b = 'outer';
      function middle() {
        var b = 'middle';
        function inner() {
          var b = 'inner';
          console.log(a); // 라인 1
          console.log(b); // 라인 2
        }
        inner();
        console.log(b); // 라인 3
      }
      middle();
      console.log(a); // 라인 4
      console.log(b); // 라인 5
    }
    
    outer();
    라인 답변 위치 접근 여부
    1 ReferenceError
    outer
    outer 함수의 지역(함수) 스코프에 위치 inner → middle → outer 순으로
    스코프 체인을 따라 변수 a에 접근
    2 inner inner 함수의 지역(함수) 스코프에 위치 inner  inner 내 접근 가능
    3 middle middle 함수의 지역(함수) 스코프에 위치 middle  middle 내 접근 가능
    4 outer outer 함수의 지역(함수) 스코프에 위치 outer  outer 내 접근 가능
    5 outer outer 함수의 지역(함수) 스코프에 위치 outer  outer 내 접근 가능

     

    4. 다음 자바스크립트 코드를 보고 각각의 console.log에서 출력될 값과 그 이유를 설명하세요.

    var a = 1;
    
    function first() {
      var b = 2;
      
      function second() {
        var c = 3;
        
        if(true) {
          var a = 4;
          let b = 5;
          
          console.log(a); // 라인 1
          console.log(b); // 라인 2
          console.log(c); // 라인 3
        }
    
        console.log(a); // 라인 4
        console.log(b); // 라인 5
        console.log(c); // 라인 6
      }
      
      second();
      console.log(a); // 라인 7
      console.log(b); // 라인 8
    }
    
    first();
    console.log(a); // 라인 9
    라인 답변 위치 접근 여부
    1 4 second 함수의 지역(함수) 스코프에 위치 second(if) second(if) 접근 가능
    2 5 second 함수의 지역(블록) 스코프에 위치 second(if) second(if) 접근 가능
    3 3 second 함수의 지역(함수) 스코프에 위치 second(if)  second 접근 가능
    4 4 second 함수의 지역(함수) 스코프에 위치 second second(if) 접근 가능
    5 2 first 함수의 지역(함수) 스코프에 위치 second first 접근 가능
    6 3 second 함수의 지역(함수) 스코프에 위치 second second 접근 가능
    7 1 전역 스코프에 위치 first 전역 접근 가능
    8 2 first 함수의 지역(함수) 스코프에 위치 first first 접근 가능
    9 1 전역 스코프에 위치 전역 전역 접근 가능

     

    5. 아래 코드 실행 시 결과와 그 이유를 설명해 주세요.

    function asyncLoop() {
      for (var i = 0; i < 5; i++) {
        setTimeout(() => {
          console.log(i);
        }, i * 1000);
      }
    }
    asyncLoop();

    *setTimeout : (특정 시간이 경과한 후 함수를 실행하도록 예약) 비동기적으로 실행되어, 각 반복에서 등록된 콜백 함수는 나중에 실행

     

    오답 (더 보기 눌러서 확인하기)

    더보기

    var로 선언된 변수 i는 전역적으로 사용되기 때문에
    0과 1이 바로 출력되고, 1초 후 2, 2초 후 3, 3초 후 4가 출력됩니다.

     

    정답 (더 보기 눌러서 확인하기)

    더보기

    1. asyncLoop 호출과 동시에 for 루프 실행

    ▶ i는 0부터 5까지 증가
    ▶ 마지막 i++ 후, i < 5이므로 i = 5에서 정지

    2. 각 루프 반복해서 setTimeout 함수 호출
    ▶ 콜백 함수와 타임아웃 시간을 전달

    3. setTimeout 함수는 지정된 시간이 경과한 후 콜백 함수를 태스크 큐에 추가 (실행 아님)
    이벤트 루프 : 호출 스택이 비었을 때, 태스크 큐에서 함수를 가져와 호출 스택에 추가

    ▶ 따라서, for 루프가 완전히 실행된 후 setTimeout의 콜백 함수가 순차적으로 실행할 예정

    4. 이후 콜백 함수 내에서 i를 참조하는데, for 루프가 완료된 시점의 i인 5를 참조

    5. 최종적으로, 콘솔에는 5가 5번 출력 (0, 1, 2, 3, 4초 후 각각)

       Execution Context
      ┌───────────────────┐
      │                   │
      │    Global Scope   │
      │       var i       │
      │                   │
      ├───────────────────┤
      │                   │
      │   for loop (0-4)  │
      │     i = 0 to 5    │
      │                   │
      ├───────────────────┤
      │                   │
      │  setTimeout (0s)  │
      │     console(i)    │ (5)
      │                   │
      ├───────────────────┤
      │                   │
      │  setTimeout (1s)  │
      │     console(i)    │ (5)
      │                   │
      └───────────────────┘

     

    Q. var 대신 let 사용 시에는 어떤 결과가 나올까요? (더보기 눌러서 확인하기)

    더보기

    1. asyncLoop 호출과 동시에 for 루프 실행

    ▶ i는 0부터 4까지 증가
    ▶ let 키워드는 각 반복마다 새로운 변수 인스턴스 생성
    ▶ 각 setTimeout 콜백 함수는 고유한 i 값 캡처

    2. 각 루프 반복해서 setTimeout 함수 호출
    ▶ 콜백 함수와 타임아웃 시간 전달

    3. setTimeout 함수는 지정된 시간이 경과한 후 콜백 함수를 태스크 큐에 추가 (실행 아님)
     이벤트 루프 : 호출 스택이 비었을 때, 태스크 큐에서 함수를 가져와 호출 스택에 추가
    ▶ 따라서, for 루프가 완전히 실행된 후 setTimeout의 콜백 함수가 순차적으로 실행할 예정

    4. 이후 콜백 함수에서 i를 참조하는데, let을 사용했기 때문에 각 반복마다 고유한 i 값이 콜백 함수에 캡처되어 있는 상태

    5. 최종적으로, 콘솔에는 0부터 4까지의 숫자 출력 (0, 1, 2, 3, 4초 후 출력)

       Execution Context
      ┌───────────────────┐
      │                   │
      │    Global Scope   │
      │                   │
      │                   │
      ├───────────────────┤
      │                   │
      │   for loop (0-4)  │
      │                   │
      │                   │
      ├───────────────────┤
      │                   │
      │  setTimeout (0s)  │
      │     console(i)    │ (0)
      │   (let i = 0)     │
      ├───────────────────┤
      │                   │
      │  setTimeout (1s)  │
      │     console(i)    │ (1)
      │   (let i = 1)     │
      └───────────────────┘

     

    6. greeting 함수의 동작 원리, greetJohn 변수가 참조하는 것, greetJohn() 호출 시 결과와 이유를 설명하세요.

    function greeting(name) {
      let greetingMessage = "Hello, " + name;
    
      return function () {
        console.log(greetingMessage);
      };
    }
    
    const greetJohn = greeting("John");
    greetJohn();
    답변
    greeting 함수의 동작 원리

    (1) name을 매개변수로 받아
    (2) "Hello, "라는 문자열에 name을 붙여 greetingMessage라는 변수에 저장하고
    (3) 콘솔 창에 출력하는 함수를 반환한다. 이때 클로저가 형성된다.

    greetJohn 변수가 참조하는 것
    greeting 함수에 John이라는 매개변수를 전달하여 호출한 결과로 *반환된 함수(클로저)를 참조한다.

    greetJohn() 호출 시 결과와 이유
    "Hello, John"
    클로저 함수 덕분에, greeting 함수의 지역 변수 greetingMessage에 접근 가능하기 때문이다.

     

    7. 아래 코드의 실행 결과와 이유를 설명하세요.

    function outer() {
      var a = 1;
      var b = { value: "first" };
    
      function inner() {
        var a = 2;
        b.value = "second";
    
        console.log(a); // 라인 1
        console.log(b.value); // 라인 2
      }
    
      inner();
      console.log(a); // 라인 3
      console.log(b.value); // 라인 4
    }
    
    outer();
    라인 답변 위치 접근 여부
    1 2 inner 함수의 지역(함수) 스코프 내 위치 inner inner 접근 가능
    2 second outer 함수의 지역(함수) 스코프 내 위치 inner outer 접근 가능
    3 1 outer 함수의 지역(함수) 스코프 내 위치 outer outer 접근 가능
    4 second outer 함수의 지역(함수) 스코프 내 위치  outer outer 접근 가능
    객체 b의 경우, inner 함수 내에서 객체 b의 value 속성을 second로 변경한다.
    이때, 객체가 참조형이므로 outer 지역(함수) 스코프 내 객체 b에도 영향을 미친다.
    따라서 outer 함수 내에서도 b.value를 참조하면 변경된 값인 second가 출력된다.

     

    🏆 마무리

    안녕하세요! 오랜만에 블로그로 돌아온 Three입니다.

    '모던 자바스크립트 Deep Dive'를 통해 자바스크립트를 다시 공부 중인데,

    실행 컨텍스트와 클로저 개념이 머리에 들어오지 않아

    문제를 풀어보며 개념을 익힐 수 있도록 공부하였습니다.

     

    창업경진대회와 함께, 네이버클라우드 캠프의 최종 프로젝트를 마무리하느라 약 1-2달간 블로그에 소홀했는데요,

    꾸준히 공부하는 내용을 블로그에 업로드할 테니 쭉 지켜봐 주시길 바랄게요. 감사합니다 :)

Designed by Tistory.