
**자바스크립트 함수(Javascript Function)**와 이어지는 개념으로써
자바스크립트 엔진(Javascript Engine)에서 함수가 어떤 원리로 동작하는지 알 필요가 있다.
그래서 등장하는 개념이 Execution Context (실행 컨텍스트)와 Closure(클로저)이다.
이런 자바스크립트 동작 원리를 현업에서 알고 있어야 디버깅 및 문제 해결에 용이해진다.
자바스크립트 엔진(Javascript Engine)
자바스크립트 엔진을 사용하는 환경은 브라우저와 Node.js가 있으며,
자바스크립트 엔진 내부에는 콜 스택(Call Stack)과 힙(Heap) 영역으로 이루어져 있다.
더 디테일한 영역도 있겠지만, 엔진을 추상화한 그림은 아래와 같다.

먼저, 콜 스택이란, 자바스크립트에서 실행에 필요한 정보를 담고 있는 실행 컨텍스트가
스택(Stack) 자료구조 형태로 쌓여져 있는 공간을 의미한다.
그리고, 힙 메모리 안에는 오브젝트(객체)가 저장되어 있는 형태이다.
본격적으로 실행 컨텍스트에 대해 알아보자.
실행 컨텍스트(Execution Context)
한마디로, 자바스크립트가 실행되는 환경이라는 의미로 컴파일과 동시에 실행을 하게 되는데
실행 과정에서 가장 먼저 전역 실행 컨텍스트가 생성이 되고, 실행이 된다.
여기에는 실행에 필요한 여러가지 정보들이 포함이 되어있다.
그 정보들은 다음과 같다.
- 변수 : 전역 변수, 지역 변수, 매개변수, 객체의 속성 등
- 함수 선언문
- 변수의 스코프 (유효 범위)
- this
이와 같이, 실행에 필요한 정보를 형상화하고, 구분하기 위해 물리적 객체 형태로 보관하게 된다.
그리고, 실행중인 전역 컨텍스트에서 함수 단위로 새로운 별도의 컨텍스트를 생성하는데
이것을 함수 실행 컨텍스트라고 한다.
아래의 코드를 예시로 실행 컨텍스트의 적재 방식 및 반환 방식을 이해해보자.
var x = 'aaa';
function foo() {
var y = 'bbb';
function bar() {
var z = 'ccc';
console.log(x + y + z);
}
bar();
}
foo();먼저 전역 컨텍스트가 쌓이고, 그 안에 foo() 선언문이 보이는 순간, foo() 함수 컨텍스트가 쌓인다.
foo() 함수 안에서 bar() 선언문이 보이면, bar() 함수 컨텍스트가 쌓인다.
함수를 실행을 통해 반환하면, bar() -> foo() 순서로 함수 실행 컨텍스트는 제거된다.
이 과정을 그림으로 나타내면 아래와 같다.

실행 컨텍스트의 물리적 객체
위에서 실행 컨텍스트는 실행에 필요한 요소들이 물리적인 객체의 형태로 존재한다고 했다.
총 3개의 속성을 가지는 객체의 형태로 존재하게 된다.
- 변수 객체(Variable Object)
- this 변수 정보
- 스코프 체인(Scope Chain)
1. 변수 객체 (VO)
실행 컨텍스트가 생성되면, 실행에 필요한 여러 정보를 담을 객체를 생성한다.
변수 객체는 변수, 매개변수 및 인수, 함수 선언식 등의 정보를 담고 있다.
또한, 속성이기 때문에 값을 갖지만, 이 값은 다른 객체를 참조하고 있는 형태이다.
전역 컨텍스트와, 함수 실행 컨텍스트에 따라 참조하고 있는 객체들이 다를 수 있는데
전역 컨텍스트는 최상위에 위치하는 전역 변수 및 전역 함수를 가리킨다.
함수 실행 컨텍스트는 활성 객체를 가리키며
내부 함수와 내부 변수 및 인수들을 배열로 묶은 arguments 객체들을 요소로 한다.
2. 스코프 체인 (Scope Chain)
스코프 체인은 전역 혹은 함수가 참조할 수 있는 전역 객체, 활성 객체의 리스트를 가리킨다.
이 리스트는 연결 리스트 형태로 구현되며, 엔진은 이 스코프 체인을 통해 렉시컬 스코프를 파악하여
함수가 중첩 상태일 때, 내부 함수 내에서 외부 함수의 스코프와 전역 스코프까지 접근할 수 있다.
예를 들어, 함수 내의 코드에서 변수를 참조하면, 가장 첫번째 리스트 요소를 가리키는 활성 객체부터
전역 객체까지 검색을 이어나가며 찾는다. 전역 객체까지 검색에 실패할 경우 참조 에러를 낸다.
3. this 정보
this에 할당되는 값은 함수 호출 패턴에 따라 다른데, 하나씩 살펴보자.
① 함수 호출 (전역 함수 및 내부 함수)
일반 전역 함수 뿐만 아니라 내부 함수의 경우에도 this는 모두 전역 객체에 바인딩된다.
function foo() {
console.log("foo's this :", this) // Window
function bar() {
console.log("bar's this :", this) // Window
}
bar();
}
foo();② 함수 호출 (콜백 함수)
콜백 함수의 경우에도 모두 this는 모두 전역 객체에 바인딩된다.
var value = 1;
var obj = {
value: 100,
foo: function() {
setTimeout(function() {
console.log("callback's this :", this); // Window
console.log("callback's value :", this.value); // 1
}, 1000)
}
}
obj.foo();③ 메소드 호출
함수가 객체의 메소드로써 호출되면, this는 해당 메소드를 호출한 객체에 바인딩된다.
var obj1 = {
name: 'Lee',
sayName: function() {
console.log(this.name) // Lee
}
}
var obj2 = {
name: 'Kim'
}
obj2.sayName = obj1.sayName; // obj2 메소드에 obj1 메소드 할당
obj1.sayName() // Lee
obj2.sayName() // Kim④ 프로토타입 객체
프로토타입 객체도 메소드를 가질 수 있어, 프로토타입 객체 내부의 this는
해당 메소드를 호출한 프로토타입 객체를 가리킨다.
혹시나 없을 경우, new를 통해 생성한 새로운 객체에서 찾게 된다.
⑤ 생성자 함수 호출
생성자 함수를 통해 생성한 객체를 가리킨다.
클로저 (Closure)
클로저는 내부 함수가 자신이 선언되었을 때의 렉시컬 스코프(실행 컨텍스트)를 기억하여
자신이 선언되었을 때의 컨텍스트 밖에서 호출되어도 스코프에 접근할 수 있는 함수를 말한다.
예시를 통해 살펴보자.
function outerFunc() {
var x = 10;
var innerFunc = function() { console.log(x) }
innerFunc();
}
outerFunc();자세한 원리를 살펴보면, outerFunc() 내부에 있는 innerFunc() 함수는 선언되고 호출되었는데
내부함수 innerFunc()는 외부 함수 outerFunc()의 변수 x에 접근할 수 있다고 정리 가능하다.
이것은 함수 outerFunc()의 렉시컬 스코프를 기억해 함수 실행 컨텍스트가 사라져도
innerFunc()에서 변수에 접근할 수 있음을 의미한다.
최종적으로, 함수가 다른 함수 내부에서 정의되고, 내부 함수가 외부 함수 변수를 참조할 때 사용할 수 있다.
또한, 내부 함수가 외부 함수로부터 반환되거나, 다른 코드 블록 내에서 참조될 때 클로저가 형성된다.
클로저의 활용
1. 상태 유지
현재 상태를 기억하고, 변경된 최신 상태를 유지하는 경우 활용된다.
예시로, closure()라는 함수를 만들고, 내부 함수를 반환시켜 다른 변수에 저장한다.
반환한 내부 함수는 외부 함수의 렉시컬 스코프를 기억해, 외부 함수의 실행 컨텍스트가 종료되어도
외부 함수의 지역 변수에 접근할 수 있게 되는 걸 응용할 수 있다.
이 변수는 외부 함수의 렉시컬 스코프를 기억해, 변경된 최신 상태를 계속해서 유지할 수 있다.
function closure() {
var isShow = false;
return function() {
isShow = !isShow;
}
}
var returnedClosure = closure();이것을 이용하여 차후에 배울 리액트(React) 빌트인 훅이나 커스텀 훅에서는
클로저 함수를 객체나 배열 형태로 반환시켜, 상태를 유지할 수 있도록 한다.
2. 전역 변수의 사용 억제
예시로, 버튼이 클릭될 때마다 클릭한 횟수가 누적되는 카운터 함수를 만들어보자.
단, 전역 변수로 설정하면, 누구나 접근하여 값을 변경할 수 있으므로, 외부 함수의 지역 변수로 관리한다.
최종적으로 클로저 함수를 반환시켜, 정보를 은닉하면서 상태를 유지할 수 있게 된다.
var increase = (function() {
var counter = 0;
return function() {
return ++counter;
}
})(); // IIFEReference
[JS] 📚 클로저 (Closure) 개념 완벽 정리클로저(closure) 란? 클로저는 반환된 내부함수가 자신이 선언됐을 때의 환경(Lexical environment)인 스코프를 기억하여, 만일 자신이 선언됐을 때의 환경(스코프) 밖에서 호출되어도 스코프에 접근할inpa.tistory.com
[JS] 📚 자바스크립트 실행 컨텍스트 원리실행 컨텍스트 실행 컨텍스트(Execution Context)는 scope, hoisting, this, function, closure 등의 동작원리를 담고 있는 자바스크립트의 핵심원리이다. 실행 컨텍스트를 바로 이해하지 못하면 코드 독해가inpa.tistory.com
[JS] 📚 자바스크립트 this 💯 완전 정복this 정의 let group = { title: "1모둠", students: ["보라", "호진", "지민"], title2 : this.title, title3() { console.log(this.title) } }; console.log(group.title2); //undefined group.title3(); // 1모둠 this는 함수의 블록 스코프 내에inpa.tistory.com
개발자 단민 | 클로저와 더 가까워지기closure와 closer해져보자 ㅋ.ㅋ 클로저를 알기 전에, 자바스크립트의 렉시컬 스코핑에 대해 먼저 알아보도록 하자! 스코프(Scope)란? 스코프란 참조 대상 식별자(변수와 함수같이 어떤 대상을 다www.jeong-min.com