**실행 컨텍스트(Execution Context)**는 javascript 코드가 평가되고 실행되는 환경의 추상적인 개념입니다. 모든 javascript 코드는 실행 컨텍스트 내에서 실행됩니다.
실행 컨텍스트는 다음과 같은 주요 구성 요소를 가집니다.
렉시컬 환경은 식별자와 식별자에 바인딩된 값, 그리고 외부 렉시컬 환경에 대한 참조를 포함하는 구조입니다. 렉시컬 환경은 다음 두 가지 구성 요소를 가집니다:
실행 컨텍스트의 생성 과정은 다음과 같습니다:
graph TD
%% 실행 컨텍스트 트리 구조
Global[전역 실행 컨텍스트] --> Outer[outer 실행 컨텍스트]
Outer --> Inner[inner 실행 컨텍스트]
%% 전역 실행 컨텍스트 생명주기
subgraph GlobalContext[전역 실행 컨텍스트 생명주기]
G1[생성] --> G2[변수 x, y 초기화]
G2 --> G3[outer 함수 정의]
G3 --> G4[outer 함수 호출]
G4 --> G5[대기]
G5 --> G6[프로그램 종료]
end
%% outer 실행 컨텍스트 생명주기
subgraph OuterContext[outer 실행 컨텍스트 생명주기]
O1[생성] --> O2[변수 x 초기화]
O2 --> O3[inner 함수 정의]
O3 --> O4[inner 함수 호출]
O4 --> O5[inner 함수 완료 대기]
O5 --> O6[종료]
end
%% inner 실행 컨텍스트 생명주기
subgraph InnerContext[inner 실행 컨텍스트 생명주기]
I1[생성] --> I2[변수 y 초기화]
I2 --> I3[console.log 실행]
I3 --> I4[종료]
end
%% 컨텍스트 간 연결
G4 -.-> O1
O4 -.-> I1
I4 -.-> O5
O6 -.-> G5
%% 스타일링
classDef contextNode fill:#f9f,stroke:#333,stroke-width:2px;
class Global,Outer,Inner contextNode;
classDef globalStep fill:#bbf,stroke:#333,stroke-width:1px;
class G1,G2,G3,G4,G5,G6 globalStep;
classDef outerStep fill:#bfb,stroke:#333,stroke-width:1px;
class O1,O2,O3,O4,O5,O6 outerStep;
classDef innerStep fill:#ffb,stroke:#333,stroke-width:1px;
class I1,I2,I3,I4 innerStep;
다음 코드를 통해 실행 컨텍스트와 렉시컬 환경의 동작을 살펴보겠습니다:
let x = 10;
let y = 20;
function outer() {
let x = 100;
function inner() {
let y = 200;
console.log(x + y);
}
inner();
}
outer();
이 코드의 실행 과정을 단계별로 설명하겠습니다
const createCounter = () => {
// 클로저를 이용한 private 변수
let count = 0;
return {
increment: () => {
count++;
console.log(`Count: ${count}`);
},
decrement: () => {
count--;
console.log(`Count: ${count}`);
},
getCount: () => count
};
};
const counter = createCounter();
counter.increment(); // Count: 1
counter.increment(); // Count: 2
counter.decrement(); // Count: 1
console.log(counter.getCount()); // 1
console.log(counter.count); // undefined
이 예제에서 createCounter
함수의 렉시컬 환경은 count
변수를 포함합니다. 반환된 객체의 메서드들은 이 렉시컬 환경에 대한 클로저를 형성하여 count
에 접근할 수 있지만, 외부에서는 직접 접근할 수 없습니다.
function curry(fn) {
return function curried(...args) {
if (args.length >= fn.length) {
return fn.apply(this, args);
} else {
return function(...args2) {
return curried.apply(this, args.concat(args2));
}
}
};
}
function add(a, b, c) {
return a + b + c;
}
const curriedAdd = curry(add);
console.log(curriedAdd(1)(2)(3)); // 6
console.log(curriedAdd(1, 2)(3)); // 6
console.log(curriedAdd(1)(2, 3)); // 6
이 예제에서 curry
함수는 새로운 실행 컨텍스트를 생성하고, 그 안에서 curried
함수를 반환합니다. curried
함수는 자신의 렉시컬 환경에 fn
과 이전에 전달받은 args
를 유지하면서, 새로운 인자가 전달될 때마다 새로운 실행 컨텍스트를 생성합니다.
class DynamicList {
constructor(containerId) {
this.container = document.getElementById(containerId);
this.items = [];
// 이벤트 리스너에서 this를 올바르게 바인딩
this.handleClick = this.handleClick.bind(this);
// 이벤트 위임
this.container.addEventListener('click', this.handleClick);
}
addItem(text) {
const id = Date.now();
this.items.push({ id, text });
this.render();
}
removeItem(id) {
this.items = this.items.filter(item => item.id !== id);
this.render();
}
handleClick(event) {
if (event.target.classList.contains('remove-btn')) {
const id = parseInt(event.target.dataset.id);
this.removeItem(id);
}
}
render() {
this.container.innerHTML = this.items.map(item => `
<div>
${item.text}
<button class="remove-btn" data-id="${item.id}">Remove</button>
</div>
`).join('');
}
}
const list = new DynamicList('list-container');
list.addItem('Item 1');
list.addItem('Item 2');