sodol-dotcom

[TIL] 스파르타코딩클럽 프론트엔드 엔지니어 양성과정_240730 본문

TIL

[TIL] 스파르타코딩클럽 프론트엔드 엔지니어 양성과정_240730

sod0l 2024. 9. 24. 23:54

𝜗𝜚 오늘 공부한 내용

  • JavaScript 문법 종합반 3주차
    • 실행 컨텍스트, 콜 스택, VE&LE, 호이스팅, 함수 선언문&함수 표현식, 스코프, 스코프체인, outerEnverionmentReference(= outer), this의 함수, 메서드에 따른 작동

 

1-1) 실행 컨텍스트 구성 예시 코드

주어진 코드의 실행 결과와 각 단계의 해석을 설명드리겠습니다.

  • 코드
javascript코드 복사
var a = 1;
function outer() {
    function inner() {
        console.log(a); // undefined
        var a = 3;
    }
    inner();
    console.log(a);
}

outer();
console.log(a);
  • 실행 결과
javascript코드 복사
undefined
1
1
  • 해석
  1. 전역 컨텍스트:
  2. javascript코드 복사 var a = 1;
  • 전역 변수 a가 선언되고 값이 1로 초기화됩니다.
  1. 함수 outer 선언:
    • outer 함수가 선언됩니다.
    • outer 함수 안에 inner 함수가 선언됩니다.
  2. javascript코드 복사 function outer() { function inner() { console.log(a); // undefined var a = 3; } inner(); console.log(a); }
  3. outer 함수 호출:
    • outer 함수가 호출됩니다.
  4. javascript코드 복사 outer();
  5. inner 함수 호출:
    • outer 함수가 호출되면서 inner 함수가 호출됩니다.
    • inner 함수 내부에서 console.log(a)가 실행됩니다. 여기서 a는 호이스팅(hoisting)으로 인해 선언이 함수의 최상단으로 끌어올려지지만 초기화는 되지 않은 상태이므로 undefined가 출력됩니다.
    • var a = 3;이 실행되어 inner 함수의 지역 변수 a3으로 초기화됩니다.
  6. javascript코드 복사 function inner() { console.log(a); // undefined var a = 3; } inner();
  7. outer 함수에서 console.log(a):
    • inner 함수 실행이 끝난 후, outer 함수 내의 console.log(a)가 실행됩니다.
    • outer 함수 내에는 var a로 선언된 변수가 없으므로, outer 함수는 전역 변수 a를 참조하여 1을 출력합니다.
  8. javascript코드 복사 console.log(a);
  9. 전역에서 console.log(a):
    • outer 함수 실행이 끝난 후, 전역에서 console.log(a)가 실행됩니다.
    • 전역 변수 a는 여전히 1로 초기화된 상태이므로 1이 출력됩니다.
  10. javascript코드 복사 console.log(a);ß

 

1-2) 요약

  • inner 함수에서 var a 선언이 호이스팅되어 console.log(a)에서 undefined가 출력됩니다.
  • outer 함수와 전역에서는 전역 변수 a의 값 1이 출력됩니다.

 

1-3) 콜 스택(Call Stack) 분석

  • 콜 스택 분석 내용
    1. 초기 상태
      1. 전역 컨텍스트:
        • 전역 변수 a가 선언되고 1로 초기화됩니다.
    2. outer 함수 호출
      1. outer 함수가 호출됨:
        • 콜 스택: [outer]
        • outer 함수가 콜 스택에 추가됩니다.
      2. inner 함수 선언 및 호출:
        • outer 함수 안에서 inner 함수가 선언되고 호출됩니다.
        • 콜 스택: [outer, inner]
        • inner 함수가 콜 스택에 추가됩니다.
    3. inner 함수 실행
      1. inner 함수 실행:
        • console.log(a)가 실행됩니다.
        • inner 함수 안에서 var a가 선언되지만 아직 초기화되지 않은 상태이므로 호이스팅으로 인해 undefined가 출력됩니다.
        • 콜 스택: [outer, inner]
        • 이후 var a = 3;가 실행되어 inner 함수의 지역 변수 a3으로 초기화됩니다.
        • inner 함수 실행이 끝나면서 콜 스택에서 제거됩니다.
        • 콜 스택: [outer]
    4. outer 함수 실행
      1. outer 함수 실행:
        • inner 함수 호출이 끝난 후, outer 함수에서 console.log(a)가 실행됩니다.
        • outer 함수 내에 var a로 선언된 변수가 없으므로 전역 변수 a의 값 1이 출력됩니다.
        • outer 함수 실행이 끝나면서 콜 스택에서 제거됩니다.
        • 콜 스택: []
    5. 전역 컨텍스트 실행
      1. 전역 컨텍스트:
        • outer 함수 호출이 끝난 후, 전역 컨텍스트에서 console.log(a)가 실행됩니다.
        • 전역 변수 a는 여전히 1로 초기화된 상태이므로 1이 출력됩니다.
    • 콜 스택의 시각적 흐름: 아래와 같이 콜 스택에 함수가 쌓이고 제거되는 과정을 통해 코드의 실행 흐름을 이해할 수 있습니다.
      1. 초기 상태:
      2. [Global Execution Context]
      3. outer 함수 호출:
      4. [Global Execution Context] [outer Execution Context]
      5. inner 함수 호출:
      6. [Global Execution Context] [outer Execution Context] [inner Execution Context]
      7. inner 함수 실행 (console.log(a)):
      8. [Global Execution Context] [outer Execution Context] [inner Execution Context] // Logs: undefined
      9. inner 함수 종료:
      10. [Global Execution Context] [outer Execution Context]
      11. outer 함수 실행 (console.log(a)):
      12. [Global Execution Context] [outer Execution Context] // Logs: 1
      13. outer 함수 종료:
      14. [Global Execution Context]
      15. 전역 컨텍스트 실행 (console.log(a)):
      16. [Global Execution Context] // Logs: 1
  • /icons/token_pink.svg **아래 순서 요약**코드 실행 → 전역(in) → 전역(중단) + outer(in) → outer(중단) + inner(in) → inner(out) + outer(재개) → outer(out) + 전역(재개) → 전역(out) → 코드 종료

 

1-4) 실행 컨텍스트(Execution Context)

  • 객체임. 어떤 객체나하면 실행할 코드에 제공할 환경 정보를 모아놓은 객체.
  • 그 객체 안에는 3가지가 존재

 

1-5) LE 호이스팅 규칙 실습 (1)

 

1-5-1) LE 호이스팅 적용전 코드의 기초적인 해석

function a (x) {.        // 1.
    console.log(x);      // 2.
    var x;               // 3.
    console.log(x);      // 4.
    var x = 2;           // 5.
    console.log(x);      // 6.
};
a(1);                    // 7.
  1. function a(x) {...};
    • 함수 선언. 함수의 이름과 동작을 정의함. 실행은 x
    • 이 함수는 매개변수 ‘x’출력하는 기능을 가지고 있는 함수임.
    • a: 함수의 이름으로 호출시 사용. 함수의 동작을 식별하는데 필요함. 의미 있는 이름을 사용하여 명확하게 것이 더 좋음.(예: calculateSum)
    • x: 매개 변수. 함수가 호출될 때 전달받는 매개 변수
    • 매개 변수(Parameter, 인자(값)): 함수가 호출될 때 외부에서 전달된 값을 함수 내부에 사용할 수 있도록 정의하는 변수. +) x = 2; 일때 x는 매개변수 2는 인수(Arguments)이다.
      • 인수: 함수를 호출 시 함수의 매개 변수에 전달되는 실제 값
      • 매개변수 = Parameter = 인자 = 인자값 / 인수 = Arguments
      • 변수 값: 변수에 저장된 데이터 ≠ 인수: 한수 호출시 매개 변수에 전달되는 값
  2. console.log(x);
    • 함수 내부에서 매개 변수 x의 값인 1을 출력함.
      = 함수가 호출되면서 매개 변수 'x'에 값(= 인자) 1이 전달된다.
  3. ‘var x;’
    • 변수 ‘x’를 선언함 (= x라는 변수의 이름(식별자)을 설정함)
    • 선언은 코드에서 사용할 이름을 정의, 그 이름이 어떤 데이터나 동작을 의미하는지 명시하는 과정
    • 선언을 통해 변수, 함수, 클래스 등 다양한 구성 요소를 정의하고 사용할 수 있게 됨
  4. console.log(x);
    • x의 값 출력
  5. var x =2;
    • 매개 변수 ‘x’의 값을 2로 설정 → 변수 ‘x’는 이제 2로 초기화됨
    • ** 이 할당은 ‘var x;’ 선언과는 별개로 실행됨
  6. console.log(x);
    • (변수 ‘x’의 값이 2로 설정된 이후 실행)2를 출력
  7. a(1);
    • 함수 ‘a’를 호출하면서 매개 변수 ‘x’에 값 1을 전달함
    • 위의 모든 단계가 이 호출로 인해 실행 됨

 

1-5-2) LE 호이스팅 적용전 코드의 처리 과정에 대한 주석

// action point 1: 매개 변수 다시 쓰기(JS 엔진은 똑같이 이해한다)
// action point 2: 결과 예상하기
// action point 3: hoisting 적용해본 후 결과를 다시 예상해보기

function a (x) {.        // 1. 함수가 호출되면서 매개 변수 'x'에 값 '1'이 전달, 이 시점에서 x는 1이다.
    console.log(x);      // x의 값인 1이 출력
    var x;               // 2. var x;는 변수 x를 선언. JavaScript에서는 var로 선언된 변수는 함수의 시작 부분으로 끌어올려짐(hoisting). 즉, 이 var x; 선언은 함수의 처음으로 이동하지만 실제 값 할당은 여기서 이 후에 이루어짐.
    console.log(x);      // 3. 다시 console.log(x);를 호출, 이 시점에서는 변수 x가 선언되었지만 값이 할당되지 않았기 때문에 x의 값은 'undefined'임. 'undefined' 출력됨.
    var x = 2;           // 4. 변수 x에 2를 할당. 변수 x의 값이 2로 설정됨. 이 줄의 변수 할당은 함수의 맨 처음으로 호이스팅된 선언 var x;와는 별개로 진행됨
    console.log(x);      // 5. 이 시점에서 변수 x의 값은 2로 설정되었으므로 2가 출력된다.
};
a(1);                    // 6. 함수 a를 호출. 이 호출로 인해 위의 코드가 실행됨. 매개 변수 x에는 값 1이 전달되므로, 함수 내부의 실행 흐름이 시작됨
  • 위 과정의 2~3. 과정에 대한 추가적인 설명
    • C줄에서의 ‘변수 x의 선언(var x;)’은 A줄의 매개 변수 x와 별개로 새로운 변수가 선언된 것
    • C줄은 변수 x는 선언 되었지만 값은 할당되지 않은 상황 → undefined가 출력 됨(으로 예상)
    • 즉, 호이스팅을 모르면 undefined로 출력될 것으로 예상하지만 실제로는 ‘1’이 출력됨
  • // action point 1: 매개 변수 다시 쓰기(JS 엔진은 똑같이 이해한다) // action point 2: 결과 예상하기 // action point 3: hoisting 적용해본 후 결과를 다시 예상해보기 function a(x) { // A console.log(x); // B var x; // C: 호이스팅 되는 부분 console.log(x); // D var x = 2; // E console.log(x); // F } a(1); // G

 

1-5-3-1) LE 호이스팅 된 코드의 실제 코드 진행과정 살펴보기

**function a(x) {
    var x; // 이 선언이 함수 최상위로 호이스팅됨
    console.log(x);
    console.log(x);
    x = 2; // 할당은 여전히 여기에 있음
    console.log(x);
}**
  • 중요한 점은 매개 변수가 변수 ‘x’로 선언되어 초기값 ‘1’을 가지게 됨. 즉, ‘x’는 함수가 실행될 때 이미 ‘1’로 설정됨. 변수 선언만 호이스팅되고, 할당은 호이스팅되지 않음!

 

1-5-3-2) 콜 스택을 통한 설명

  • 함수 호출 ‘a(1)’
    • 코드 실행 시작 → ‘a(1);’ 함수 호출 발생 → 콜 스택에 ‘a’ 함수 추가됨
    • // 콜스택 상태: | | | | | a(1) | |__________|
  1. 첫 번째 ‘console. log(x);’
    • 함수 ‘a’의 매개 변수 ‘x’는 ‘1’로 초기화됨
    • 첫 번째 ‘console.log(x);’가 실행됨 → ‘1’이 출력됨
    • // 콜스택 상태: | | | a(1) | |__________|
  2. ‘var x;’ 선언
    • 변수 ‘x’가 재선언됨. (이는 함수 스코프에서 이미 존재하는 변수이므로 아무런 영향을 미치지 않음.)
    • 호이스팅에 의해 이 선언은 함수의 최상위로 끌어올려짐
    • 그러나 변수 ‘x’는 이미 매개 변수로 선언되어 있으므로 값 ‘1’이 유지 됨
    • // 콜스택 상태: | | | a(1) | |__________|
  3. 두 번째 ‘console.log(x);’
    • 두 번째 ‘console.log(x);’가 실행됨.
    • ‘x’는 여전히 ‘1’ 임. (재선언은 영향을 미치지 않으므로) → ‘1’이 출력됨
    • // 콜스택 상태: | | | a(1) | |__________|
  4. ‘var x = 2; ‘ 할당
    • 변수 ‘x’에 값 ‘2’가 할당됨. 이제 ‘x’는 ‘2’임
    • // 콜스택 상태: | | | a(1) | |__________|
  5. 세 번째 ‘console.log(x);’
    • 세 번째 ‘console.log(x);’가 실행됨.
    • 출력: ‘2’
    • // 콜스택 상태: | | | a(1) | |__________|
  6. 함수 종료
    • 함수 ‘a’의 실행이 끝납니다. 콜 스택에서 ‘a(1)’이 제거됩니다.
    • // 콜스택 상태: | | | | |__________|

 

1-5-3-3) 콜 스택 요약

  1. 함수 a가 호출되면 x1로 초기화됩니다.
  2. 첫 번째 console.log(x);1을 출력합니다.
  3. var x; 선언은 호이스팅되어 이미 처리되었습니다. 값은 여전히 1입니다.
  4. 두 번째 console.log(x);1을 출력합니다.
  5. var x = 2; 할당으로 x2로 바뀝니다.
  6. 세 번째 console.log(x);2를 출력합니다.
  7. 함수 실행이 끝나고 콜 스택에서 제거됩니다.
  • 콜 스택: 함수 호출 시에만 콜 스택에 쌓입니다. console.log와 같은 내장 함수는 콜 스택에 직접적으로 표시되지 않고, JavaScript 런타임 환경의 내장 기능으로 처리됩니다.
  • console.log(x): 내부적으로 호출되지만 콜 스택에는 표시되지 않습니다. 호출이 완료되면 콜 스택에 나타나지 않습니다.

 

1-6) LE 호이스팅 규칙 실습 (2)

  • 예시 코드 (1. 호이스팅을 배제하고 생각할 경우 출력 값)
  • function a() { console.log(b); // 예상 출력: 오류 var b = 'bbb'; console.log(b); // 예상 출력: bbb function b() { } console.log(b); // 예상 출력: function } a();
  • 예시 코드 (2. 호이스팅 적용)
    • A. 호이스팅의 함수 선언: 함수 선언은 전체가 호이스팅됨. 즉, 함수 정의 자체가 스코프의 최상위로 끌어올려진다.
      (이로 인해 ‘b’는 변수보다는 함수로 정의됨. 함수 선언이 변수 선언을 덮어쓰게 된다.
    • B. 선언부 호이스팅: var b;는 함수의 최상위로 끌어올려지지만, 할당부b = 'bbb';는 코드에서 변수 선언 이후의 실제 위치에서 수행됩니다.
      • var b = ‘bbb’; 에서 변수의 선언부는 ‘var b;’ 이고 변수의 할당부는 ‘b = ‘bbb’;이다. **중요!!**
  • function a() { var b; // 변수 선언부 호이스팅 function b() { } // 함수 선언은 전체를 호이스팅 -> 아래 'A.' 참고 console.log(b); b = 'bbb'; // 변수의 할당부는 원래 자리에 -> 아래 'B.' 참고 console.log(b); console.lob(b); } a();
  • 예시 코드 (3. 호이스팅 적용 후 예상 출력 값)
    • A. 호이스팅의 함수 선언: 함수 선언은 전체가 호이스팅됨. 즉, 함수 정의 자체가 스코프의 최상위로 끌어올려진다.
      (이로 인해 ‘b’는 변수보다는 함수로 정의됨. 함수 선언이 변수 선언을 덮어쓰게 된다.
    • B. 선언부 호이스팅: var b;는 함수의 최상위로 끌어올려지지만, 할당부b = 'bbb';는 코드에서 변수 선언 이후의 실제 위치에서 수행됩니다.
      • var b = ‘bbb’; 에서 변수의 선언부는 ‘var b;’ 이고 변수의 할당부는 ‘b = ‘bbb’;이다. **중요!!**
  • function a() { var b; function b() { } console.log(b); // 예상 출력: function b = 'bbb'; console.log(b); // 예상 출력: bbb console.lob(b); // 예상 출력: bbb } a();

 

1-7) 함수 선언문 & 함수 표현식

  • ‘함수 선언문과 함수 표현식이 있다.’ 정도만 기억하면 됨. 추가적으로 밑줄 친 부분만 기억!
  • 함수 선언문은 호이스팅시 함수 전체가 위로 끌어올려짐
  • 함수 표현식은 호이스팅시 변수 선언부만 위로 끌어올려짐
    → 함수 표현식을 활용하는 습관이 필요함(협업 많이하고 복잡한 코드일수록, 전역 공간에서 이루어지는 코드 협업일수록 함수 표현식을 활용히야 위험한 코드, 에러를 만드는 코드를 방지할 수 있음)
  1. 함수 선언문(함수의 정의부만 존재하는 방식)
    • 함수 이름을 소괄호 앞에 씀. (함수명 a가 곧 변수명)
    • function 정의부만 존재, 할당 명령이 없는 경우
    • function a () { /*…*/ } a( ); // 실행 oK
  2. 함수 표현식(변수에 할당하는 방식)
    • 함수 이름을 변수 이름으로 씀
    • 정의한 functinon을 별도 변수에 할당하는 경우
      1. 익명 함수 표현식:
        • 변수명 b가 곧 변수명(일반적인 case에) 
        •  
        • // 추가 예 var myFunction = function() { console.log('Hello, world!'); }; myFunction(); // 실행 ok!
        • var b = function ( ) { /*…*/ } b( ); // 실행 ok
      2. 기명 함수 표현식:
        • 변수명은 c, 함수명은 d
        • d( )는 c( ) 안에서 재귀적으로 호출될 때만 사용 가능하므로 사용성에 대한 의문
          // 추가 예
          
          var myFunction = function namedFunction() {
            console.log('Hello, world!');
          };
          
          myFunction();       // 실행 ok!
          namedFunction();    // 에러!!
        • var c = function d ( ) { /*…*/ } c( ); // 실행ok d( ); // 에러!
        • 기명 함수 표현식은 활용성이 거의 없음

 

1-8) LexicalEnvironment, 스코프, 스코프체인, outerEnvironmentReference(= outer)

  • 요약: 각각의 실행 컨텍스트는 LE(LexicalEnvironment) 안에 record와 outer를 가지고 있고, outer 안에는 그 실행 컨텍스트가 선언될 당시의 LE정보가 다들어있으니 scope chain에 의해 상위 컨텍스트의 record를 읽어 올 수 있다.
  • 실행 컨텍스트 관점에서의 스코프
    1. 스코프
      • 식별자에 대한 유효 범위
      • 대부분 언어에서 존재, JS에도 존재
    2. 스코프 체인
      • 식별자의 유효범위를 안에서부터 바깥으로 차례로 검색해나가는 것
      • 실행 컨텍스트 생성될때 형성되며 변수와 함수에 접근할 수 있는 범위를 결정한다.
        (변수와 함수의 접근을 관리하는 중요한 메커니즘!)
      • 위 두가지로 인해 변수 충돌을 방지하고 코드의 가독성을 높인다.
      • 클로저 지원을 한다. (아래 형성 과정 참고)
        • 클로저: 함수가 자신이 정의된 스코프의 변수에 접근할 수 있도록 하여, 함수가 정의될 때의 환경을 기억하게 한다.
        • 이는 함수가 자신의 외부 스코프의 변수에 접근할 수 있게 해주어 강력한 프로그래밍 패턴을 가능하게 한다. → 스코프 체이닝을 하는 이유!
      • 함수 호출 시 새로운 실행 컨텍스트가 생성되고, 이 컨텍스트는 자신이 정의된 스코프와 상위 컨텍스트를 참조한다.
      • | D | | C | | B | | A | | 전역 context |스코프 체인의 형성 과정:
    3.  - 위와 같이 call stack이 생성되어 있을때
           - 각각의 실행 컨텍스트는 `record(environmentRecord)`와 `outer(outerEnvironmentReference)` 를 갖는다.
           - `실행 컨텍스트 A`는 `전역 context`의 `LE`를 `outer`로 갖는다.(= 참조한다)
           LE: LexicalEnvironment, VE와 다르게 변경사항을 실시간으로 반영하는 실행 컨텍스트의 구성요소 중 하나 (스냅샷을 유지하지 않음)
               - 즉, A의 상위 컨텍스트는 전역 context!
           - `실행 컨텍스트 B`는 `실행 컨텍스트 A`의 `LE`를 `outer`로 갖는다.(= 참조한다)
           - `실행 컨텍스트 C`는 `실행 컨텍스트 B`의 `LE`를 `outer`로 갖는다.(= 참조한다)
           - `실행 컨텍스트 D`는 `실행 컨텍스트 C`의 `LE`를 `outer`로 갖는다.(= 참조한다)
  • 스코프 체인의 콜스텍을 그리며 푸는 예제(scope 관점에서)```jsx
    // 아래 코드를 여러분이 직접 call stack을 그려가며 scope 관점에서 변수에 접근해보세요!
    // 어려우신 분들은 강의를 한번 더 돌려보시기를 권장드려요 :) (JS 문법 3주차)
  • /icons/token_pink.svg 요약
    • ‘inner’ 함수 내 ‘console.log(a)’
      : ‘undefined’ (호이스팅에 따라 변수 ‘a’가 선언되지만 초기화되지 않음)
    • ‘outer’ 함수 내 ‘console.log(a)’
      : ‘1’(전역 스코프의 ‘a’를 참조)
    • 전역 내 ‘console.log(a)’
      : ‘1’ (전역스코프의 ‘a’를 참조)
  • // 호이스팅 전*
       console.log(a);     // 이 값은 뭐가 나올까요?(scope 관점에서)
       var a = 3;
    };
    inner();
    console.log(a); // 이 값은 또 뭐가 나올까요? 이유는요? scope 관점에서!
    };
    outer();
    console.log(a); // 이 값은 뭐가 나올까요? 마찬가지로 이유도!
    ```
  • ```jsx
    // 아래 코드를 여러분이 직접 call stack을 그려가며 scope 관점에서 변수에 접근해보세요!
    // 어려우신 분들은 강의를 한번 더 돌려보시기를 권장드려요 :) (JS 문법 3주차)
  • var a = 1;
    var outer = function () {
    var inner = function () {
  • // 호이스팅 후*
       var a;                      **// 호이스팅 되며 var a; 위로 올라옴**
    var inner = function () { // 내부 스코프에서 해결
    console.log(a); // 이 값은 뭐가 나올까요?(scope 관점에서)
    a = 3; // 호이스팅된 var a; 때문에 'undefined'이 출력
    };
    inner();
    console.log(a); // 이 값은 또 뭐가 나올까요? 이유는요? scope 관점에서! // 위의 inner 함수는 이미 나가고 없어서 3은 x
    }; // 이 때

    outer();
    console.log(a); // 이 값은 뭐가 나올까요? 마찬가지로 이유도!
  • var a = 1;
    var outer = function () {
  1. 전역 코드 실행
    • 전역 스코프:
      • var a = 1; → 변수 a 선언 및 ‘1’로 초기화
      • var outer = function () {…} → 함수 정의
    • 콜 스텍:
      • 전역 실행 컨텍스트 (Global Context)
      • ```sql
      • ----------------+
        | Global Context |
        | (a: 1, outer) |
      • ----------------+
  2. ‘outer’ 함수 호출
    • 전역 스코프:
      • ‘outer’ 함수 호출
      • 새로운 실행 컨텍스트 생성: ‘outer’ 실행 컨텍스트
    • 콜 스택:
      • ‘outer’ 실행 컨텍스트가 스택 푸시됨
      • 전역 실행 컨텍스트가 아래에 남아있음
      • ```sql
      • ----------------+
        | Outer Context | <-- 현재 실행 중
        | (inner) |
      • ----------------+
        | Global Context |
        | (a: 1) |
      • ----------------+
  3. ‘inner’ 함수 호출
    • ‘outer’ 실행 컨텍스트:
      • ‘inner’ 함수 호출
      • 새로운 실행 컨텍스트 생성: ‘inner’ 실행 컨텍스트
    • 콜 스택:
      • ‘inner’ 실행 컨텍스트가 스택에 푸쉬됨
      • ‘outer’ 실행 컨텍스트가 아래에 남아있음
      • 전역 실행 컨텍스트가 가장 하단에 있음
      • ```sql
      • ----------------+
        | Inner Context | <-- 현재 실행 중
        | (a: undefined) |
      • ----------------+
        | Outer Context |
        | (inner) |
      • ----------------+
        | Global Context |
        | (a: 1) |
      • ----------------+
  4. ‘inner’ 함수 내 ‘console.log(a)’
    • 스코프 체인:
      • ‘inner’ 함수의 로컬 스코프에서 ‘a’를 찾으려고 시도하지만 ‘a’는 아직 ‘inner’ 함수의 로컬 스코프에서 ‘var a;’가 선언만 되어 있고 초기화 되지 않은 상태이다.
      • JavaScript는 호이스팅을 고려하여 ‘a’는 ‘undefined’로 초기화함
      • 상위 스코프인 ‘outer’의 ‘a’는 아직 접근되지 않음
    • 출력: ‘undefined’
    • 콜 스택:
      • ‘inner’ 실행 컨텍스트에서 ‘console.log(a)’ 호출 후 계속 실행
      • 로컬 변수 초기화: 3이 아닌 1이 출력되는 이유
        • inner 함수 내에서 var a = 3으로 초기화되지만, 이 초기화는 console.log(a) 호출 이후에 발생
        • 초기화는 변수에 대한 값을 설정하지만, 이미 console.log(a)에서 aundefined로 출력된 상태입니다.
        ```sql
      • ----------------+
        | Inner Context | <-- 현재 실행 중
        | (a: undefined) |
      • ----------------+
        | Outer Context |
        | (inner) |
      • ----------------+
        | Global Context |
        | (a: 1) |
      • ----------------+
      • // outer 함수 내 console.log(a);의 출력 값이 1 인 이유는:
        // 전역 스코프의 a를 참조, inner 함수의 a는 로컬 변수로 3으로 설정되었지만 outer 함수에는 영향을 미치지 않음)
  5. ‘inner’ 함수 내 ‘var a = 3’
    • ‘inner’ 함수의 로컬 스코프:
      • ‘a’를 ‘3’으로 할당
    • 콜 스택:
      • ‘inner’ 함수의 실행 컨텍스트가 제거됨(실행 컨텍스트가 종료됨)
      • ```sql
      • ----------------+
        | Outer Context | <-- 현재 실행 중
        | (a: undefined) |
      • ----------------+
        | Global Context |
        | (a: 1) |
      • ----------------+
  6. ‘outer’ 함수 내 ‘console.log(a)’
    • 스코프 체인:
      • ‘outer’ 함수의 로컬 스코프 ‘a’가 정의되어 있지 않음
      • 전역 스코프의 ‘a’를 참조함
      • 출력: ‘1’
    • 콜 스택:
      • ‘outer’ 함수의 실행 컨텍스트가 제거됨
      • ```sql
      • ----------------+
        | Global Context | <-- 현재 실행 중
        | (a: 1) |
      • ----------------+
  7. 전역 ‘console.log(a)’
    • 스코프 체인:
      • 전역 스코프의 ‘a’를 참조함
    • 출력: ‘1’
  • 최종 콜 스택 시각화
    • 전역 실행 컨텍스트: 모든 함수 호출이 완료되었으므로 전역 실행 컨텍스트만 남아 있음
    • ```sql
    • ----------------+
      | Global Context | <-- 현재 실행 중
      | (a: 1) |
    • ----------------+
  • 오늘 들은 강의는 대체적으로 내용도 많고 중간에 한번 놓치면 처음부터 다시 들어야 이해가 가능했다. 내일은 좀 더 쉬운 내용이 나왔으면..ㅠㅠㅠ

2. ThisBindings

  • this가 전역환경에서 어떤 것을 의미하는지?
  • 전역 환경을 설명할때는 항상 '런타임'을 고려해야 한다.
    • 런타임: runtime, 러닝하는 타임 -> 코드가 돌아가는 시간 또는 환경을 의미함
  • 자바스크립트에는 두가지 환경이 있다. (노드&브라우저)
  • 1️⃣ **노드**
    • 터미널에서 node 검색 -> 커맨드라인이 바뀜 -> console.log(this) 검색 -> global이 나옴
    • -> this === global 검색 -> true 나옴 (this가 전역 변수임을 알 수 있음)
  • 크롬에서 option + command + I -> 개발자 도구가 뜸 -> 콘솔 열기
  • -> console.log(this) -> window가 나옴 -> this === wnindow 검색 -> true
  • 즉 전역 환경에서 this -> 노드(global 객체), 브라우저(window 객체)이다.
  • 메서드로 호출될 때와 함수로 호출될 때 this가 어떻게 바뀌는지 차이점 확인
    • 기준은 '독립성'
    • 함수는 독립적으로 쓰이고 메서드는 종속적으로 쓰임
    • 함수는 그 자체로 독립적인 기능을 수행, 메서드는 누군가에 의해서 수행됨
      = 함수는 스스로 수행되고 실행되는 주체가 없고 자신임, 메서드는 실행하는 주체가 있어야 함
      = 함수의 this -> 전역 객체, 메서드의 this -> 호출의 주체
    • 메서드는 자신을 호출한 대상 객체에 대한 동작을 수행한다.
      ( 객체.메서드(); -> 객체가 메서더의 this가 됨 )
    • 이 차이점이 중요한 이유: this를 결정하는 데 있어서 매우 중요하기 때문

 

2-1. Case 1: 함수

javascript코드 복사
var func = function (x) {
    console.log(this, x);
};
func(1); // window {...} 1
  • 설명
    1. var func = function (x) { ... };func라는 이름의 변수를 선언하고, 여기에 함수를 저장합니다. 이 함수는 x라는 입력값을 받습니다.
    2. func(1);는 함수를 호출하면서 1x에 전달합니다.
    3. 함수 내부에서 console.log(this, x);를 실행합니다. 여기서 this는 함수가 호출될 때 누가 호출했는지를 나타내는 특별한 키워드입니다.
    4. 이 경우, 함수는 아무 객체도 아닌 그냥 func로 호출되었습니다. 그래서 this는 기본적으로 전역 객체를 참조하게 됩니다. 브라우저 환경에서는 전역 객체가 window입니다.
    5. 따라서 console.log(this, x);window 객체와 1을 출력하게 됩니다.
    • 즉, func(1);을 실행하면 브라우저 콘솔에 window { ... } 1이 출력됩니다.

 

2-2. Case 2: 메서드

javascript코드 복사
var obj = {
    method: func,
};
obj.method(2); // { method: f } 2
  • 설명
    1. var obj = { method: func };obj라는 이름의 객체를 선언합니다. 이 객체는 method라는 속성을 가지며, 그 값은 앞에서 정의한 함수 func입니다.
    2. obj.method(2);obj 객체의 method를 호출하면서 2x에 전달합니다.
    3. 이 경우 method는 객체 obj의 일부로 호출됩니다. 그래서 함수 내부에서 thisobj 객체를 참조합니다.
      (메서드는 호출 주체를 명시할 수 있기 때문에 this가 해당 객체(obj)를 의미하는 것임)
    4. 따라서 console.log(this, x);obj 객체와 2를 출력하게 됩니다.
    • 즉, obj.method(2);을 실행하면 브라우저 콘솔에 { method: f } 2가 출력됩니다.
    • 핵심 포인트:
    • 전역 함수 호출: 함수를 그냥 호출하면 (func(1)) this는 전역 객체를 참조합니다. 브라우저 환경에서는 window가 됩니다.
    • 객체의 메서드 호출: 객체의 메서드를 호출하면 (obj.method(2)) this는 그 객체를 참조합니다. 이 경우에는 obj입니다.
    • 따라서, 이 두 가지 경우에 대해 다른 결과가 출력되는 이유는 this 키워드가 어떻게 해석되느냐에 따라 달라지는 것입니다.*

 

2-3. 메서드 내부에서의 this(예제 1)

  • 위의 내용에서 보았듯, this에는 호출을 누가 했는지에 대한 정보가 담긴다.
  • 아래 예제을 분석해보면 메소드로서의 호출인지 함수로서의 호출인지를 구분할 수 있는 눈이 생길 수 있을 것임!
  • vscode에 꼭 찍어보기
var obj = {
    methodA: function () { console.log(this) },
    inner: {
        methodB: function () { console.log(this) },
    }
};

obj.methodA();
obj['methodA']();

obj.inner.methodB();
obj.inner['methodB']();
obj['inner'].methodB();
obj['inner']['methodB']();
  • 해석:
    1. obj.methodA();
      • methodA가 호출됩니다.
      • thismethodA를 호출한 객체를 가리킵니다.
      • 여기서 thisobj 객체를 가리킵니다.
      • 출력: { methodA: f, inner: { methodB: f } }
    2. obj['methodA']();
      • methodA가 배열 표기법으로 호출됩니다.
      • 배열 표기법으로 접근해도 this는 동일하게 obj 객체를 가리킵니다.
      • 출력: { methodA: f, inner: { methodB: f } }
    3. obj.inner.methodB();
      • methodB가 호출됩니다.
      • thismethodB를 호출한 객체를 가리킵니다.
      • 여기서 thisinner 객체를 가리킵니다.
      • 출력: { methodB: f }
    4. obj.inner['methodB']();
      • methodB가 배열 표기법으로 호출됩니다.
      • 배열 표기법으로 접근해도 this는 동일하게 inner 객체를 가리킵니다.
      • 출력: { methodB: f }
    5. obj['inner'].methodB();
      • inner 객체에 접근한 후 methodB가 호출됩니다.
      • thismethodB를 호출한 객체를 가리킵니다.
      • 여기서 thisinner 객체를 가리킵니다.
      • 출력: { methodB: f }
    6. obj['inner']['methodB']();
      • inner 객체와 methodB에 배열 표기법으로 접근한 후 호출됩니다.
      • 배열 표기법으로 접근해도 this는 동일하게 inner 객체를 가리킵니다.
      • 출력: { methodB: f }
  • 요약:
    • obj.methodA();obj['methodA']();thisobj를 가리킵니다.
    • obj.inner.methodB();, obj.inner['methodB']();, obj['inner'].methodB();, obj['inner']['methodB']();thisinner를 가리킵니다.

 

2-4. 메서드 내부에서의 this(예제 2)

  • this가 단계별로 어떻게 작동하는지 아래 예제도 풀어보자.
var obj1 = {
    outer: function() {
        console.log(this);
        avr innerFunc = function() {
            console.log(this);
        }
        innerFunc();

        var obj2 = {
            innerMethod: innerFunc
        };
        obj2.innerMethod();
    }
};
obj1.outer();
  • 해석:
    • 이 코드는 this가 어떻게 동작하는지 이해하기 위한 좋은 예제입니다. this는 함수가 어떻게 호출되었는지에 따라 달라집니다.
      1. obj1.outer() 호출:
        • 여기서 obj1 객체의 outer 메서드를 호출합니다.
        • outer 메서드 내부에서 console.log(this);를 실행합니다.
        • thisouter 메서드를 호출한 객체인 obj1을 가리킵니다.
        • 따라서 첫 번째 출력은 obj1 객체가 됩니다.
      2. obj1.outer();
      3. innerFunc 호출 (outer 메서드 내부):
        • innerFuncouter 메서드 내부에서 선언된 함수입니다.
        • innerFunc가 별도로 호출될 때, this는 전역 객체를 가리킵니다. (브라우저 환경에서는 window, Node.js 환경에서는 global)
        • 따라서 console.log(this)는 전역 객체를 출력합니다.
        • 출력: window (브라우저 환경)
      4. var innerFunc = function() { console.log(this); } innerFunc();
      5. obj2.innerMethod() 호출 (outer 메서드 내부):
        • obj2라는 새로운 객체를 outer 메서드 내부에서 선언합니다. 이 객체는 innerMethod라는 메서드를 가지며, 이 메서드는 앞서 선언한 innerFunc 함수를 가리킵니다.
        • obj2.innerMethod()를 호출할 때, thisinnerMethod를 호출한 객체인 obj2를 가리킵니다.
        • 따라서 세 번째 출력은 obj2 객체가 됩니다.
      6. var obj2 = { innerMethod: innerFunc }; obj2.innerMethod();
  • 요악:
    • 첫 번째 출력: obj1.outer() 호출 시 thisobj1을 가리킵니다.
    • { outer: f, inner: { innerMethod: f } }
    • 두 번째 출력: innerFunc() 호출 시 this는 전역 객체를 가리킵니다.
    • window // 브라우저 환경
    • 세 번째 출력: obj2.innerMethod() 호출 시 thisobj2를 가리킵니다.
    • { innerMethod: f }
  • 전체 출력:
  • { outer: f, inner: { innerMethod: f } } window { innerMethod: f }

 

 

𝜗𝜚 어려웠던 내용

  • 오늘은 실행 컨텍스트, 콜스택, 호이스팅 등등 많은 내용을 배웠는데 한번에 이해하기에는 좀 어려웠다. 강의를 여러번 다시 들으면서 하느라 진도를 많이 못나가서 좀 아쉽다.

 

 

𝜗𝜚 궁금한 내용과 부족한 내용

  • 오늘의 공부 마지막 쯤에 thisBindings가 메서드나 함수에 의해 호출될때 각각 다르게 작동하는 부분을 마무리 짓지 못했는데 개념 자체를 내일 다시한번 들어보고 예제를 꼼꼼히 뜯어봐야할 것 같다.

 

 

𝜗𝜚 느낀 점

  • 오늘은 공부가 참 쉽지 않았다. 내용도 많고 중간에 놓치면 처음부터 다시들어야만 이해가 가는 것들도 많았다. 내일은 좀 쉬운 내용이 나왔으면…ㅜㅜ 



Ig. https://www.instagram.com/sodol_dotcom/