1. 실행 컨텍스트의 정의

  • 자바스크립트의 실행 컨텍스트는 코드가 실행되는 환경을 정의하는 기본 개념이다.
    • 일단 추상적으로만 말해보면, 자바스크립트가 코드를 실행할 때 필요한 모든 정보를 담고 있는 박스라고 생각하면 된다.
    • 자바스크립트의 동적 언어로서의 성격을 가장 잘 파악할 수 있는 개념이다.
  • 실행 컨텍스트는 동일한 환경에 있는 코드들을 실행할 때 필요한 환경 정보들을 모아 구성된 객체이다.
    • 자바스크립트 코드가 실행되는 환경을 정의하며, 이는 코드가 실행될 때 필요한 변수, 객체, 함수 등을 정의한다.
    • 코드를 실행하기 위해 객체로서 모인 이 환경 정보들은, 콜 스택에 쌓아올려지고, 가장 위에 쌓여있는 컨텍스트와 관련 있는 코드들을 실행하는 식으로 전체 코드의 환경과 순서가 보장된다.
  • 아래는 NEXTREE의 실행 컨텍스트 포스트에서 설명하는 실행 컨텍스트에 대한 내용이다.
    1. 사용자가 웹 페이지에 접근하면, 브라우저의 자바스크립트 엔진이 자바스크립트 파일을 스캔한다.
    2. 스캔이 완료되면, 스크립트의 모든 코드를 변환하고 실행하는 과정을 관리하는 '실행 컨텍스트'라는 환경이 생성된다.
    3. 이 환경은 실행할 코드에 전달할 정보를 담고 있는 객체로 볼 수 있다.
    4. 여기에는 스코프, 호이스팅, 클로저, this, function 등의 동작 원리가 포함되어 있으며, 이런 원리들은 자바스크립트의 핵심 원리로 간주된다.
    5. 이러한 방법으로, 자바스크립트는 해당 스크립트 코드에 있는 실행 컨텍스트에 관한 정보들을 수집한다.
    6. 모아진 실행 컨텍스트는 콜 스택에 쌓아올려진 후 실행되는데, 이 덕분에 코드의 실행환경과 순서는 보장될 수 있다.
    7. 각 실행 컨텍스트는 활성화되는 시점(자바스크립트 엔진이 해당 실행 컨텍스트와 관련된 코드를 실행하기 시작하는 순간)에 해당 컨텍스트 내부에 선언된 변수를 끌어올리고(호이스팅), 외부 환경정보를 구성하고, this 값을 설정하는 등의 동작을 수행하게 된다.

2. 실행 컨텍스트의 역할

1. 변수 관리

※ 변수 선언 및 초기화 관리
  • 실행 컨텍스트는 함수나 전역에서 선언된 변수들을 메모리에 등록하고 초기화한다.
  • 선언 단계에서 변수는 undefined로 초기화된다. (var기준)
  • 함수 선언은 실행 전에 메모리에 저장된다.
    • 호이스팅: 선언된 변수와 함수가 코드의 상단으로 끌어올려지는 것처럼 동작한다.
console.log(a); // undefined
var a = 10;
function foo() { return "Hello"; }

2. 스코프 관리

※ 스코프 체인 생성
  • 실행 컨텍스트는 현재 코드에서 접근할 수 있는 변수와 함수의 유효 범위를 결정한다.
  • 내부 함수는 외부 함수의 스코프에 접근할 수 있으며, 이를 스코프 체인이라 한다.
※ Lexical Environment (어휘적 환경) 관리
  • 실행 중인 코드의 스코프와 관련된 정보를 저장한다.
    • 전역 환경: 전역 변수와 함수
    • 함수 환경: 함수 내부의 지역 변수와 매개변수
function outer() {
    let a = 1;
    function inner() {
        console.log(a); // outer의 스코프에 접근 가능
    }
    inner();
}
outer();

3. this 바인딩

  • 실행 컨텍스트는 this 키워드의 값을 결정한다.
    • 전역 컨텍스트: 전역 객체(window 또는 global)
    • 함수 컨텍스트: 함수 호출 방식에 따라 결정
      • 일반 함수 호출: undefined(strict 모드) 또는 전역 객체
      • 객체의 메서드 호출: 메서드를 호출한 객체
      • 화살표 함수: 선언 시점의 상위 스코프에서 this를 결정
const obj = {
    a: 10,
    method() {
        console.log(this.a); // obj의 a
    }
};
obj.method();

4. 코드 실행 흐름 제어

  • 실행 컨텍스트는 자바스크립트 코드의 실행 순서를 가리킨다.
    - 콜 스택에 쌓이고, 실행이 끝나면 콜 스택에서 제거된다.
    - 비동기 작업의 경우, 이벤트 루프와 호출 스택이 협력하여 실행 흐름을 제어한다.
function first() {
    console.log("First");
}
function second() {
    console.log("Second");
}
first();
second(); // 호출 순서대로 실행: first() => second() 실행 순서가 보장된다.

5. 클로저와 메모리 관리

  • 실행 컨텍스트는 함수 종료 이후에도 변수나 함수가 계속 참조되고 있다면, 이를 유지한다.
  • 이를 통해 클로저가 가능하며, 스코프 외부에서도 변수에 접근할 수 있다.
    • 클로저 : 반환된 내부함수와 내부함수가 선언되었던 렉시컬 환경의 조합
function makeCounter() {
    let count = 0;
    return function() {
        return ++count; // 클로저를 통해 count 유지
    };
}
const counter = makeCounter();
console.log(counter()); // 1
console.log(counter()); // 2

6. 호출 스택 관리

  • 실행 컨텍스트는 콜 스택에 쌓이고, 실행이 끝나면 콜 스택에서 제거된다.
    - 전역 실행 컨텍스트는 프로그램이 종료될 때까지 콜 스택에 유지된다.
    - 함수 호출 시마다, 새로운 실행 컨텍스트가 생성되어 콜 스택에 추가된다.
function a() {
    b();
}
function b() {
    console.log("b");
}
a(); // 호출 스택: global → a → b

7. 환경 정보 관리

  • 실행 컨텍스트는 현재 실행 중인 코드와 관련된 모든 환경 정보를 저장한다.
    • 변수
    • 함수 선언
    • 외부 스코프 정보 : 스코프 체인
    • this

3. 실행 컨텍스트의 구성

이미지 출처 : heycoding tistory

1. Lexical Environment (렉시컬 환경)

  • let, const 변수와 함수 선언 뿐 아니라 코드의 스코프와 관련된 정보를 관리한다.
    • Outer Environment Reference를 통해 상위 스코프를 참조한다.
  • 실행 중 변수의 추가, 수정 등 변경 사항이 실시간으로 반영된다.
  • Lexical Environment는 Variable Environment와 역할이 다르며, 초기화 시에도 독립적으로 생성된다.

2. Variable Environment (변수 환경)

  • 현재 컨텍스트 내의 var로 선언된 변수와 함수 선언의 정보를 관리한다.

    • 외부 환경 정보는 Lexical Environment의 일부인 Outer Environment Reference에 의해 관리된다.
    • 외부 환경 참조는 렉시컬 환경의 역할이다.
  • 실행 컨텍스트 생성 시, Variable Environment는 var 변수와 함수 선언만 초기화하고, letconst는 Lexical Environment에서 관리된다.

    • Variable Environment는 Lexical Environment의 스냅샷이 아니며, 각자 독립적으로 생성되고 관리된다.

3. This Binding (this 값)

  • 현재 실행 컨텍스트에서 this가 참조하는 객체를 나타낸다.
  • 전역 컨텍스트에서는 window 또는 global 객체를 참조한다.
  • 함수 내부에서는 함수 호출 방식에 따라 달라진다.

4. 렉시컬 환경 (Lexical Environment)

1. 정의

  • 렉시컬 환경은 자바스크립트 엔진이 변수와 함수 선언, 스코프 체인, 외부 환경 정보를 관리할 수 있도록 하는 구조적인 방식을 의미한다.
    • 코드가 실행될 때 어휘적(Lexical) 으로 정의된 환경에 따라 변수나 함수에 접근할 수 있다.

2. 렉시컬 환경의 구성

※ 환경 레코드(Environment Record)
  • 현재 실행 중인 스코프 내에서 선언된 변수와 함수의 실제 정보를 저장한다.
    • 대상은 변수의 값, 함수 선언, 매개변수 등이 있다.
  • 예: { a: 10, b:function(){} }
※ 외부 렉시컬 환경 참조 (Outer Lexical Environment Reference)
  • 현재 환경의 상위 스코프를 참조한다.
  • 이를 통해 스코프 체인이 만들어지고, 변수 검색이 상위 스코프로 이어진다.
    • 변수 환경에는 이게 없다.

3. 렉시컬 환경이 생성되는 시점

※ 전역 실행 컨텍스트
  • 프로그램이 시작될 때 생성된다.
  • 전역 환경에서는 window 객체(브라우저)나 global 객체(Node.js)가 환경 레코드 역할을 한다.
※ 함수 실행 컨텍스트
  • 함수가 호출될 때마다 새로운 렉시컬 환경이 생성된다.
  • 함수 내부에서 선언된 변수와 매개변수가 환경 레코드에 저장된다
※ 블록 실행 컨텍스트 (ES6 이후)
  • let, const를 사용하는 블록 스코프({})도 독립적인 렉시컬 환경을 생성한다.
{
    let a = 10;
    const b = 20;
}

4. 렉시컬 환경의 동작 과정

  • 렉시컬 환경은 코드가 작성된 어휘적 위치(Lexical position)을 기준으로 스코프를 결정한다.
  • 즉, 코드가 선언된 위치에 따라 변수와 함수에 접근할 수 있는 범위인 스코프가 결정된다는 뜻이다.
function outer() {
    let a = 10; // outer 렉시컬 환경의 변수
    function inner() {
        console.log(a); // 상위 렉시컬 환경(outer)을 참조
    }
    inner();
}
outer();
  1. outer() 실행 시:

    • outer 함수의 렉시컬 환경 생성
      • 환경 레코드: { a: 10 }
      • 외부 렉시컬 환경 참조: 전역 환경
  2. inner() 실행 시:

    • inner 함수의 렉시컬 환경 생성
      • 환경 레코드: {} (함수 내부에서 선언된 변수 없음)
      • 외부 렉시컬 환경 참조: outer 함수의 렉시컬 환경

5. 렉시컬 환경과 스코프 체인

  • 자바스크립트 엔진은 식별자를 찾을 때 우선, 일단 자신이 속한 스코프에서 찾는다.
    • 이 때, 해당 스코프에 식별자가 없으면, 해당 스코프를 포함하는 상위 스코프에서 식별자를 다시 찾아 나가는 연결과정을 거치는데, 이를 스코프 체인이라 한다.
  • 렉시컬 환경은 이 스코프 체인을 통해 변수 검색을 수행할 수 있다.
    • 변수를 찾을 때, 현재 렉시컬 환경의 환경 레코드에서 먼저 검색한다.
    • 찾지 못하면 외부 렉시컬 환경 참조로 이동해 검색을 이어간다.
    • 전역 환경까지 검색했음에도 변수가 없다면, 에러(ReferenceError)가 발생한다.
let x = 10;
function foo() {
    let y = 20;
    function bar() {
        let z = 30;
        console.log(x + y + z); // 10 + 20 + 30
    }
    bar();
}
foo();
  1. bar 함수의 렉시컬 환경:
    • 환경 레코드: { z: 30 }
    • 외부 렉시컬 환경 참조: foo 함수의 렉시컬 환경
  1. foo 함수의 렉시컬 환경:
    • 환경 레코드: { y: 20 }
    • 외부 렉시컬 환경 참조: 전역 환경
  1. 전역 환경:
    • 환경 레코드: { x: 10 }

6. 렉시컬 환경이 중요한 이유

※ 스코프를 이해하는 핵심
  • 렉시컬 환경은 스코프와 변수 접근을 관리한다.
  • 변수가 어디에서 선언되고 어떻게 참조되는지를 결정한다.
※ 클로저(Closure)를 사용할 수 있도록 하는 기반
  • 앞서, 클로저는 반환된 내부함수와 내부함수가 선언되었던 렉시컬 환경의 조합으로 정의할 수 있다.
    • 지금까지의 내용을 잘 이해했다면, 클로저는 반환된 내부함수와 반환된 내부함수가 선언되었던 렉시컬 환경의 외부 참조가 유지된 함수라는 것임을 알 수 있을 것이다.
function makeCounter() {
    let count = 0; // 클로저로 참조됨
    return function () {
        count++;
        return count;
    };
}
const counter = makeCounter();
console.log(counter()); // 1
console.log(counter()); // 2
※ ES6 이후 블록 스코프 관리를 가능케 함: 렉시컬 환경은 let, const로 선언된 변수를 블록 스코프로 관리한다.

5. 변수 환경 (Variable Environment)

1. 정의

  • 변수 환경은 실행 컨텍스트의 구성요소 중 하나로, 변수 선언과 초기화 정보를 관리하는 데에 사용된다.
  • 이 환경은 특히나 var 키워드와 관련된 변수의 호이스팅과 스코프를 처리한다.

2. 변수 환경의 구성

※ 환경 레코드(Environment Record)
  • 변수와 함수 선언을 저장한다.
  • var 키워드로 선언된 변수는 환경 레코드의 변수 환경(Variable Environment)에 저장된다.
  • 함수 선언문(Function Declaration)은 환경 레코드에 저장되어, 호출 시점에 사용할 수 있게 된다.

※ 외부 렉시컬 환경 참조 X

  • 변수 환경외부 렉시컬 환경 참조를 가지지 않으며, 단지 현재 스코프 내의 변수를 관리한다.

3. 변수 환경과 렉시컬 환경의 공통점과 차이점

※ 공통점
  • 변수 환경렉시컬 환경은 모두 실행 컨텍스트 내에서 변수와 함수의 스코프선언 정보를 관리한다.
※ 차이점
  1. 변수 환경 (Variable Environment)

    • 변수 환경변수의 선언을 관리한다. 하지만, 실행 중에 값이 변경되더라도, 변수 환경 자체는 변경된 값을 추적하지 않는다.

    • 초기화 단계에서 설정되며, 실행 중에 변경된 값은 변수 환경이 아닌, 실행 스택이나 스코프 체인을 통해 처리된다.

      • 주요 역할: 변수의 선언 정보초기 값을 관리.
      • 변경된 값실행 스택스코프 체인을 통해 관리되며, 변수 환경 자체는 값의 변화를 추적하지 않는다.
    • 변수 환경렉시컬 환경과 다르게 외부 렉시컬 환경을 참조하지 않는다. 변수 환경은 현재 함수의 변수들만 관리하며, 외부 함수의 변수나 스코프에는 접근하지 않는다.

      • 대신, 렉시컬 환경상위 함수의 스코프스코프 체인을 관리하며, 이를 통해 상위 스코프에 있는 변수들에 접근할 수 있도록 합니다. 즉, 변수 환경은 단지 현재 함수 내의 변수만을 관리하는 역할을 하고, 외부 함수나 환경을 참조하지 않는다.
      • 렉시컬 환경은 함수가 호출될 때마다 현재 함수의 스코프외부 함수의 스코프를 포함한 스코프 체인을 관리하고, 이를 통해 상위 함수의 변수들을 참조할 수 있다.
  2. 렉시컬 환경 (Lexical Environment)

    • 렉시컬 환경(Lexical Environment)변수 환경(Variable Environment) 을 포함하고 있으며, 상위 렉시컬 환경에 대한 참조도 함께 관리한다. 즉, 변수 환경과 함께 스코프 체인을 구성한다.
    • 렉시컬 환경변수 선언 위치를 기준으로 스코프 체인을 설정하며, 변수 선언과 함수 선언을 처리한다. 변수 값의 변경은 렉시컬 환경에서도 마찬가지로 관리하지 않는다.
      • 주요 역할: 변수와 함수의 선언을 추적하고, 상위 스코프와 연결된 스코프 체인을 관리.
      • 변수 값의 변화렉시컬 환경에서 추적하지 않으며, 이 또한 실행 컨텍스트실행 스택스코프 체인에서 처리된다.
    • 즉, 렉시컬 환경은 실행 중에 변경된 값도 반영하기 위해, 변수 환경초기 값을 저장하는 것 외에도, 콜 스택에 저장된 최신 값을 참조할 수 있다는 뜻이다.

4. 예시

function outer() {
  var x = 10; // 'x' 변수 선언
  function inner() {
    console.log(x); // 'x'를 참조
  }
  x = 20; // x 값을 변경
  inner(); // x 값을 출력
}

outer();
  1. 변수 환경 (Variable Environment)

    • 변수 환경변수 선언과 초기 값을 관리한다. outer 함수 내에서 x변수 환경에 선언되고, 초기 값10으로 설정된다.

    • 실행 중에 값이 변경되면, 변수 환경은 변경된 값을 추적하지 않는다.

      • 예를 들어, x의 값이 20으로 변경되었지만, 변수 환경 자체는 초기값인 10만을 관리하고 변경된 값을 추적하지 않는다.
      • 즉, 변수 환경은 단지 변수의 선언초기값만을 저장하고 변경된 값실행 스택이나 스코프 체인을 통해 처리된다.
      • 이 예시에서 x변수 환경10으로 설정된 후, 실행 중에 20으로 변경된다. 변수 환경은 여전히 10을 관리하지만, x의 값은 실행 스택에서 20으로 변경된다.
      • 변수 환경에서는 x 값의 변경 사항을 추적하지 않기 때문에, inner 함수가 호출될 때 초기 값 10을 참조할 수 있다.
  • 실행 흐름은 아래와 같다.
    1. outer() 함수 실행:
      • var x = 10;이 실행되며, xouter 함수의 변수 환경10으로 저장된다.
    2. x = 20;로 값 변경:
      • x의 값을 20으로 변경하지만, 변수 환경은 여전히 10을 저장하고 있다.
      • 왜냐하면 var로 선언된 변수는 함수의 변수 환경에서 초기값을 관리하고, 변수의 값 변경은 변수 환경에서 추적되지 않기 때문이다.
    3. inner() 함수 호출:
      • inner() 함수가 호출된다. 이때 inner() 함수의 렉시컬 환경outer() 함수의 변수 환경을 참조한다.
        • inner()함수는 자신의 변수환경에 x가 없기 때문에 outer()함수의 변수 환경을 참조하게 된다.
      • 중요 포인트: inner() 함수는 x를 참조할 때, 변수 환경에서 초기값인 10을 참조한다.
        • 이유는 inner()가 실행될 때 변수 환경에는 x = 10만 저장되어 있기 때문이다.
      • 즉, inner() 함수에서 x는 여전히 10을 출력한다.
        • 왜 자꾸 렉시컬 환경이 언급되는지 헷갈릴 수 있다. 변수 환경은 단지 현재 함수 내 변수와 함수 선언만을 관리하지만, 렉시컬 환경변수 환경을 포함하면서 외부 렉시컬 환경 참조를 통해 스코프 체인을 구성한다.
        • inner() 함수에서 x를 참조할 수 있는 이유는 렉시컬 환경 덕분인데, 렉시컬 환경상위 스코프의 변수를 참조할 수 있기 때문에, inner() 함수는 outer() 함수의 변수 환경을 참조하여 x = 10을 출력하는 것이다.
        • 결론적으로, 변수 환경만으로는 해결할 수 없는 변수 참조는 렉시컬 환경을 이용해서 상위 스코프에 정의된 변수들을 찾아 내부적을 참조하게 된다.
  1. 렉시컬 환경 (Lexical Environment)

    • 렉시컬 환경변수 환경을 포함하고 있으며, 상위 렉시컬 환경에 대한 참조도 관리한다.
      • inner 함수는 렉시컬 환경을 생성할 때, 상위 렉시컬 환경으로 outer 함수의 스코프를 참조한다.
      • 이로 인해 inner 함수는 렉시컬 환경을 통해 outer 함수의 x를 참조할 수 있다.
    • 렉시컬 환경변수의 선언 위치스코프 체인을 관리한다. 즉, inner 함수는 렉시컬 환경을 통해 상위 함수outer스코프 체인을 참조하여 x를 찾을 수 있다.
      • inner 함수는 렉시컬 환경을 통해 outer 함수의 변수 환경에 접근하고, x변경된 값 20을 참조합니다. 즉, xouter에서 실행 중에 변경된 값인 20을 출력한다.
    • 변수 값의 변경렉시컬 환경에서 관리되지 않으며, 값의 변화는 실행 스택에서 처리된다.
      • 이 예시에서 inner 함수는 렉시컬 환경을 통해 outer변수 환경에 접근하여 x를 참조한다.
      • 렉시컬 환경변수 선언의 위치를 기준으로 스코프 체인을 설정하고, inner 함수가 실행될 때 x변경된 값20으로 출력된다. innerouter스코프 체인을 참조하여 변경된 값인 20을 출력한다.
  • 실행 흐름은 아래와 같다.
    1. outer() 함수 실행:
      • outer() 함수가 실행될 때, 실행 컨텍스트가 생성된다. 이 컨텍스트에는 변수 환경렉시컬 환경이 포함되어 있다.
        • 이 때, 변수 환경에는 x = 10이 저장되어 있다.
      • inner() 함수는 outer() 함수의 렉시컬 환경 내에 존재한다.
        - 따라서 inner() 함수는 렉시컬 환경에서 outer() 함수의 변수 환경을 참조할 수 있다.
    2. x 값 변경:
      • x의 값이 10에서 20으로 변경된다. 변수 환경x의 초기값만 저장하고, 값이 변경되면 해당 변경을 추적하지 않는다.
      • 그러나, 실제로 x의 값은 실행 스택에서 변경된 20이 저장됩니다.
    3. inner() 함수 실행:
      • inner() 함수가 실행될 때, inner()의 실행 컨텍스트가 생성된다.
      • 렉시컬 환경 내에서는 inner() 함수가 outer() 함수의 렉시컬 환경을 참조하게 된다.
        • 즉, inner()outer() 함수의 변수 환경을 참조할 수 있다.
      • 렉시컬 환경스코프 체인을 통해 상위 스코프(즉, outer() 함수)의 변수 환경을 참조하고, 이때 변경된 x 값인 20을 출력하게 된다.
        • 그러니까, 렉시컬 환경은 실행 컨텍스트에서 실제로 사용되고 있는 값 20을 콜 스택에서 보고 찾는데, 변수 환경은 콜 스택을 확인하지 않고, 변수 환경에서 최초 초기화된 값만 기억한다는 것이다.
    4. 출력:
      • console.log(x)에서 x는 변경된 값인 20을 출력한.
  1. 결론
    • 변수 환경은 단지 변수의 선언과 초기 값만을 기억하고, 실행 중에 값이 변경되더라도 변경된 값을 추적하지 않는다.
    • 반면, 렉시컬 환경스코프 체인을 관리하여 실행 시 변경된 값을 사용할 수 있다.
      • 따라서, 변수 환경에서는 x의 변경된 값 20을 추적하지 않지만, 렉시컬 환경에서는 실행 시 값이 변경된 x를 참조하여 20을 출력할 수 있다.

6. this 바인딩

1. 바인딩

  • 식별자와 값을 연결하는 과정을 의미한다.
  • this 바인딩은 this 식별자 키워드와 this가 가리켜야 하는 객체를 연결하는 것을 의미한다.

2. this

  • this는 자바스크립트에서 함수가 호출될 때 그 함수가 속한 객체를 참조하는 키워드이다..
  • this의 값은 함수가 호출되는 방식에 따라 달라질 수 있다.
    - 아래 3번 내용에서 this값이 어떻게 바인딩되는지 살펴보자.

3. 함수 호출과 this 바인딩

  1. 전역 스코프(global scope), 전역 문맥(global context)에서의 this
    • 브라우저에서는 window, Node.js에서는 global
console.log(this);  // 브라우저에서는 window 객체를 가리킴
function highOrderFunction(callback) {
    callback(); // 일반 함수 호출로 실행됨
}

function callbackFunction() {
    console.log(this); // 전역 스코프에서 호출된 일반 함수
}

highOrderFunction(callbackFunction);
// 출력: window (브라우저 환경)
function highOrderFunction(callback) {
    callback();
}

const obj = {
    name: "example",
    method: function () {
        highOrderFunction(function () {
            console.log(this); // 일반 함수 호출
        });
    }
};

obj.method();
// 출력: window (브라우저 환경)
  1. 일반 함수 내부
    • 일반 함수에서는 use strict 사용 여부에 따라 this가 다르게 출력될 수 있다.
      • use strict 사용 : undefined
      • use strict 미사용 : window(브라우저)
function hello(){
	console.log(this); // window/global
}
hello();
  1. 객체의 메서드 호출 (암시적 바인딩)
    • 객체 메서드 내에서의 this객체 자체를 참조한다.
    • 객체에 속한 메서드에서 this를 호출하면 해당 메서드를 포함하는 객체가 this이다.
      • 이 때, 객체 내에서 호출되는 메서드가 전역 스코프에 위치한 고차함수 내 일반 함수 호출을 진행하는 경우 전역 객체(window/global)가 this이다.
const obj = {
    name: 'John',
    greet: function() {
        console.log(this.name);
    }
};
obj.greet();  // 'John' 출력, this는 obj를 참조
  1. 생성자 함수
    • 생성자 함수에서의 this는 새로 생성되는 객체의 인스턴스이다.
function Person(name) {
    this.name = name;
}
const person1 = new Person('Alice');
console.log(person1.name);  // 'Alice'
  1. 화살표 함수
    • 화살표 함수에서는 객체의 메소드 호출처럼 this가 자신의 문맥을 따르지 않는다. 대신, 외부 스코프의 this를 상속받는다.
const obj = {
    name: 'John',
    greet: () => {
        console.log(this.name);
    }
};
obj.greet();  // undefined, 화살표 함수는 외부 문맥의 this(전역 객체)를 참조
  1. 명시적 바인딩
    • call, apply, bind 메소드를 사용하여, this를 명시적으로 설정할 수 있다.
const obj = { name: 'Alice' };
function greet() {
    console.log(this.name);
}
greet.call(obj);  // 'Alice', call을 사용하여 this를 obj로 설정

call(thisArg, arg1, arg2, ...)

  • 바인딩할 this객체를 1번째 인수로, 함수 내에서 필요로 하는 파라미터는 2번째 인수로 전달하면 된다.
const obj = {name: 'tom'};
const say = function(city){
  console.log(`hello ${this.name}, this is ${city}`);
};

say.call(obj, 'seoul'); // hello tom, this is seoul

// ------------------

function foo(a, b, c) {
    console.log(a + b + c);
    console.log(this);
};


foo.call(obj, 1, 2, 3); // 6, { name: 'tom' }

apply(thisArg, [arg1, arg2, ...]

  • 바인딩할 this객체를 1번째 인수로, 함수 내에서 필요로 하는 파라미터는 배열 형태로 인수를 전달할 수 있도록 하면 된다.
const obj = {name: 'tom', age: 21};  
const say = function(city, dongNm){  
    console.log(`hello ${this.name}, ${this.age}, this is ${city} ${dongNm}`);  
};  
  
say.apply(obj, ['seoul', '법정동']) // hello tom, 21, this is seoul 법정동

// -----------------------------

function foo(a, b, c) {
    console.log(a + b + c);
    console.log(this);
};
  
foo.apply(obj, [1, 2, 3]); // 6, { name: 'tom' }

const boundFunc = bind(thisArg, arg1, arg2, ...);

  • bind함수는 새로운 함수를 생성하고, 해당 함수가 호출될 때 항상 특정한 this값을 가질 수 있도록 한다.
    • bind함수로 별도 함수를 생성할 때 전달한 인수를 this로 가지며, 이 함수를 가지고 있다가 나중에 실행할 수 있다는 것이 큰 특징이다.
const obj = {name: 'tom'};
const say = function(city){
  console.log(`hello ${this.name}, this is ${city}`);
};

const boundSay = say.bind(obj);
boundSay("busan"); // hello tom, this is busan

7. 실행 컨텍스트의 동작

  • 아래 예제는 라다디님의 포스트 내용을 출처로 한 코드 예제와 이미지이다.
    • 아래 이미지의 콜 스택 구조와 코드 설명을 보면 실행 컨텍스트가 콜 스택의 맨 위에 쌓이는 순간이 곧 현재 실행할 코드에 관여하게 되는 시점임을 알 수 있을 것이다.
    • 특정 실행 컨텍스트가 활성화될 때, 자바스크립트 엔진은 해당 컨텍스트와 관련된 코드들을 실행하는 데 필요한 환경 정보들을 수집해서 실행 컨텍스트 객체에 저장함을 잊지 말자.
var x = 'xxx'; 
function foo () {
	var y = 'yyy';
	function bar () { 
		var z = 'zzz'; 
		console.log(x + y + z); 
	} 
	bar(); 
} 
foo();

실행과정

  1. 처음 자바스크립트 코드를 실행하는 순간 전역 실행 컨텍스트(Global Execution Context) 가 콜 스택에 담긴다.
  • 전역 실행 컨텍스트

    • 전역 영역에 존재하는 코드를 관리하는 실행 컨텍스트로, 모든 스크립트 코드는 전역 실행 컨텍스트 안에서 실행된다.

      • 자바스크립트 코드가 실행될 때 가장 처음 생성되고, 프로그램 전체를 감싸는 기본 환경을 관리하는 컨텍스트이다. (코드 실행을 위한 무대라고 생각)

      • 아래는 전역 실행 컨텍스트의 역할이다.

        1. 코드의 시작점 : 자바스크립트 엔진이 코드를 실행하기 위해 가장 먼저 생성하는 컨텍스트

        2. 전역 변수와 함수 관리

          • 전역에서 선언된 변수와 함수가 여기에서 관리되며, 브라우저에서는 window 객체, Node.js에서는 global 객체가 자바스크립트 실행 환경에서 가장 최상위 객체가 된다.
          • 전역 컨텍스트는, 이 window, global 객체를 이용해 전역 코드의 변수와 함수를 저장하고 실행한다.
        3. 프로그램 전체를 실행 : 함수 호출 전까지 실행되는 모든 전역 코드가 이 컨텍스트에서 처리된다.

        4. 항상 하나만 존재 : 프로그램이 실행되는 동안 단 하나의 전역 실행 컨텍스트만 존재하며, 실행되는 동안 절대 사라지지 않는다.

  • 함수 실행 컨텍스트

    • 함수 내에 존재하는 코드로, 함수가 호출되는 시점에 만들어지는 실행 컨텍스트이다. 함수가 선언되었을 때는 실행 컨텍스트가 생성되지 않으며, 단지 전역 실행 컨텍스트의 렉시컬 환경(Lexical Environment) 에 기록된다.
    • 각 함수는 고유의 실행 컨텍스트를 가지며, 함수가 실행되어 호출될 때 생성된다.
  1. 콜 스택에서 전역 컨텍스트와 관련된 코드들을 자바스크립트 엔진이 순차적으로 실행시키다가 foo()함수가 호출되면 자바스크립트 엔진은 foo()함수에 대한 환경 정보를 수집해서 foo()의 함수 실행 컨텍스트를 생성한 후 콜 스택에 담는다.

    • 그러면 콜 스택의 맨 위에 foo()의 함수 실행 컨텍스트가 놓였으므로 전역 컨텍스트와 관련된 코드보다 foo()의 함수 실행 컨텍스트와 관련된 코드가 우선순위가 더 높아지면서, foo() 함수 내부의 코드들이 순차로 실행되기 시작한다.
  2. foo() 함수 실행 도중, bar()함수가 실행되면, bar() 함수에 대한 함수 실행 컨텍스트가 콜 스택 가장 위에 담기게 된다.

    • bar()함수에 대한 함수 실행 컨텍스트가 콜 스택 가장 위에 쌓이게 되면, 콜 스택에서 bar() 함수 실행 컨텍스트가 제거되고, 제어권이 다시 이전 컨텍스트인 foo() 함수 실행 컨텍스트로 전환된다.

    • 코드가 모두 진행되고 bar() 함수의 실행이 종료되면 bar() 함수 실행 컨텍스트가 콜 스택에서 제거되고, 다시 foo() 함수 실행 컨텍스트가 콜 스택의 맨 위에 존재하게 되므로, 코드 실행이 중단되었던 부분부터 이어서 실행하게 된다.

※ 참고: 전역 실행 컨텍스트가 window 객체를 이용해 전역 코드의 변수와 함수를 저장하고 실행하는 방법 예시

// 브라우저 환경
var a = 5;
function hello() {
  console.log("Hello!");
}

console.log(window.a); // 5
window.hello(); // Hello!
  • ahello는 전역 실행 컨텍스트에 의해 window 객체의 속성으로 관리된다.
    • 그래서 window.awindow.hello()로 접근이 가능하다.
profile
프론트엔드 입문 개발자입니다.

0개의 댓글