JavaScript 문법 종합반
2. 실행 컨텍스트 (스코프, 변수, 객체, 호이스팅)
(1) 실행 컨텍스트란?
실행할 코드에 제공할 환경 정보들을 모아놓은 객체
자바스크립트는 어떤 실행 컨텍스트가 활성화되는 시점에 다음과 같은 일을 함
- 선언된 변수를 위로 끌어올림 (호이스팅)
- 외부 환경 정보를 구성함
- this 값을 설정함
실행 컨텍스트를 이해하기 위해서는 먼저 콜 스택에 대한 이해가 필요함!!
콜 스택
실행 컨텍스트를 구성하고 이것을 콜 스택에 쌓아올리는데, 이 때 가장 위에 쌓여있는 컨텍스트와 관련된 코드를 실행하는 방법으로 코드의 환경 및 순서를 보장할 수 있다
- 컨텍스트의 구성
- 구성 방법 (여러가지 있지만, 사실 함수만 생각하면 됨)
- 전역 공간
- eval() 함수
- 함수(우리가 흔히 실행 컨텍스트를 구성하는 방법)
- 실행 컨텍스트 구성 예시
- 구성 방법 (여러가지 있지만, 사실 함수만 생각하면 됨)
// ---- 1번
var a = 1;
function outer() {
function inner() {
console.log(a); //undefined
var a = 3;
}
inner(); // ---- 2번
console.log(a);
}
outer(); // ---- 3번
console.log(a);
코드 진행 순서
코드실행 → 전역(in) → 전역(중단) + outer(in) → outer(중단) + inner(in) → inner(out) + outer(재개) → outer(out) + 전역(재개) → 전역(out) → 코드종료
결국, 특정 실행 컨텍스트가 생성되거나 활성화되는 시점에 콜 스택 맨 위에 쌓이고, 스택 위에서부터 코드가 실행된다
실행 컨텍스트 객체의 실체(=담기는 정보)
- VariableEnvironment
- 현재 컨텍스트 내의 식별자 정보(=record)를 갖고 있음
- var a = 3 에서, var a 를 의미
- 외부 환경 정보(=outer)를 갖고 있음
- 선언 시점 LexicalEnvironment의 snapshot (초기 정보)
- 현재 컨텍스트 내의 식별자 정보(=record)를 갖고 있음
- LexicalEnvironment
- VariableEnvironment와 동일하지만, 변경사항을 실시간으로 반영함
- ThisBinding
- this 식별자가 바라봐야할 객체
(2) VariableEnvironment, LexicalEnvironment의 개요
VE vs LE
이 둘에 감기는 항목은 완벽하게 동일하지만, 스냅샷 유지여부에 따라 다름
- VE: 스냅샷을 유지 (초기상태를 유지함)
- LE: 스탭샷을 유지하지 않음. 즉, 실시간으로 변경사항을 계속해서 반영함
실행 컨텍스트를 생성할 때, VE에 정보를 먼저 담은 다음, 이를 그대로 복사해서 LE를 만들고 이후에 LE를 활용하는 것
구성 요소
VE와 LE 모두 동일하며, environmentRecord와 outerEnvironmentReference로 구성
- environmentRecord(=record)
- 현재 컨텍스트와 관련된 코드의 식별자 정보들이 저장됨
- 함수에 지정된 매개변수 식별자, 함수 자체, var로 선언된 변수 식별자 등
- outerEnvironmentReference(=outer)
(3) LexicalEnvironment(1) - environmentRecord(=record)와 호이스팅
- 현재 컨텍스트와 관련된 코드의 식별자 정보들이 저장됨. (record)
- 수집 대상 정보: 함수에 지정된 매개변수 식별자, 함수 자체, var로 설언된 변수 식별자 등
- 컨텍스트 내부를 처음부터 끝까지 순서대로 훑어가며 수집(실행이 아님!!)
호이스팅
- hoisting: 게양. 끌어올리는 것
- 변수정보 수집을 모두 마쳤더라도, 아직 실행 컨텍스트가 관여할 코드는 실행 전의 상태임.
- 호이스팅이란, 변수 정보 수집 과정을 이해하기 쉽게 설명한 '가상 개념'임
호이스팅 규칙
규칙 1: 매개변수 및 변수는 선언부를 호이스팅함
// 호이스팅 적용 전 (실행하고자 하는 코드)
function a (x) {
console.log(x);
var x;
console.log(x);
var x = 2;
console.log(x);
}
a(1);
// 호이스팅 적용 후
function a () {
var x; // 매개변수 적용
var x;
var x;
// 매개변수 및 변수의 선언부를 호이스팅함(끌어올림)
x = 1;
console.log(x);
console.log(x);
x = 2;
console.log(x);
}
a(1);
결과: 1 / 1 / 2
규칙 2: 함수 선언은 전체를 호이스팅
// 호이스팅 전 (실행하고자 하는 코드)
function a () {
console.log(b);
var b = 'bbb';
console.log(b);
function b() { }
console.log(b);
}
a();
// 호이스팅 적용 후
function a () {
var b; // 변수 선언부 호이스팅
function b() { } // 함수 선언은 전체를 호이스팅
console.log(b);
b = 'bbb'; // 변수의 할당부는 원래 자리에
console.log(b);
console.log(b);
}
a();
결과: function b / bbb / bbb
3. 함수 선언문, 함수 표현식
// 함수 선언문
function a () {}
a();
// 함수 표현식: 정의한 function을 별도 변수에 할당하는 경우
var b = function () {}
b();
함수 호이스팅
console.log(sum(1, 2));
console.log(multiply(3, 4));
function sum (a, b) { // 함수 선언문 sum
return a + b;
}
var multiply = function (a, b) { // 함수 표현식 multiply
return a + b;
}
위 코드가 호이스팅 되면,
// 함수 선언문은 전체를 hoisting
function sum (a, b) { // 함수 선언문 sum
return a + b;
}
// 변수는 선언부만 hoisting
var multiply;
console.log(sum(1, 2));
console.log(multiply(3, 4));
multiply = function (a, b) { // 변수의 할당부는 원래 자리
return a + b;
};
위와 같이 된다.
함수 선언문을 주의해야 하는 이유!!
...
console.log(sum(3, 4));
// 함수 선언문으로 짠 코드
// 100번째 줄 : 시니어 개발자가 짠 코드(활용하는 곳 -> 200군데)
// hoisting에 의해 함수 전체가 위로 쭉!
function sum (x, y) {
return x + y;
}
...
...
var a = sum(1, 2); // 아래에 있는 sum함수가 되버림
...
// 함수 선언문으로 짠 코드
// 5000번째 줄 : 신입이 개발자가 짠 코드(활용하는 곳 -> 10군데)
// hoisting에 의해 함수 전체가 위로 쭉!
// 5000번째 줄 아래에만 적용하고 싶었지만, 함수 전체가 위로 올라가버려 모든 코드에 적용되버리는 사고가 일어남
function sum (x, y) {
return x + ' + ' + y + ' = ' + (x + y);
}
...
var c = sum(1, 2);
console.log(c);
함수 표현식이었다면
...
console.log(sum(3, 4));
// 함수 표현식으로 짠 코드
// 함수 선언부만 위로 쭉!
// 이 이후부터의 코드만 영향을 받아요!
var sum = function (x, y) {
return x + y;
}
...
...
var a = sum(1, 2); // 위에 있는 sum 함수
...
// 함수 표현식으로 짠 코드
// 함수 선언부만 위로 쭉!
// 이 이후부터의 코드만 영향을 받아요!
var sum = function (x, y) {
return x + ' + ' + y + ' = ' + (x + y);
}
...
var c = sum(1, 2); // 아래에 있는 sum 함수
console.log(c);
(4) LexicalEnvironment(2) - 스코프, 스코프 체인, outerEnvironmentReference(=outer)
- 스코프: 식별자에 대한 유효범위
- 스코프 체인: 식별자의 유효범위를 안에서부터 바깥으로 차례대로 검색해나가는 것
- outerEnvironmentReference(=outer): 스코픞 체인이 가능토록 하는 것(외부 환경의 참조정보)
스코프 체인
- outer는 헌재 호출된 함수가 선언될 당시의 LexicalEnvironment를 참조!!
- 예를 들어서, a 함수 내부에서 b 함수를 선언, b 함수 내부에서 c 함수를 선언한 경우, 타고 올라가다 보면 전역 컨텍스트의 LexicalEnvironment를 참조하게됨
- outer는 항상 자신이 선언된 시점의 LexicalEnvironment를 참조하고 있으므로, 가장 가까운 요소부터 차례대로 접근 가능함
- 결국, 무조건 스코프 체인 상에서 가장 먼저 발견된 식별자에게만 접근 가능
var a = 1;
var outer = function() {
var inner = function() {
console.log(a); // undefined => inner 내부의 a
var a = 3;
};
inner();
console.log(a); // 1 => 전역
};
outer();
console.log(a); // 1 => 전역
3. this(정의, 활용방법, 바인딩, call, apply, bind)
(1) 상황에 따라 달라지는 this
1. this는 실행 컨텍스트가 생성될 때 결정됨( = this를 bind한다). 즉, this는 함수를 호출할 때 결정됨!
전역 공간에서의 this
전역 공간에서의 this는 전역 객체를 가리킴
브라우저 환경에서는 window, node 환경에서는 global을 가리킴
메서드로서 호출할 때 그 메서드 내부에서의 this
- 함수 vs 메서드
함수: 함수명(); => 함수는 그 자체로 독립적인 기능을 수행함
메서트: 객체.메서드명(); => 메서드는 자신을 호출한 대상 객체에 대한 동작을 수행함
this의 할당
// 1. 함수
// 호출 주체를 명시할 수 없기 때문에 this는 전역 객체를 의미함
var func = function (x) {
console.log(this, x);
};
func(1); // Window { ... } 1
// 2. 메서드
// 호출 주체를 명시할 수 있기 때문에 this는 해당 객체(obj)를 의미함
var obj = {
method: func,
};
obj.method(2); // { method: [Function: func] } 2
메서드 내부에서의 this: this에는 호출을 누가 했는지에 대한 정보가 담김!
var obj = {
methodA: function () { console.log(this) },
inner: {
methodB: function() { console.log(this) },
}
};
obj.methodA(); // this === obj
obj['methodA'](); // this === obj
obj.inner.methodB(); // this === obj.inner
obj.inner['methodB'](); // this === obj.inner
obj['inner'].methodB(); // this === obj.inner
obj['inner']['methodB'](); // this === obj.inner
함수로서 호출할 때 그 메서드 내부에서의 this
- 함수 내부에서의 this
- 어떤 함수를 함수로서 호출할 경우, this는 지정되지 않음
- 실행 컨텍스트를 활성화할 당시 this가 지정되지 않은 경우, this는 전역 객체를 의미
- 따라서, 함수로서 '독립적으로' 호출할 때는 this는 항상 전역객체를 가리킴!!
- 메서드의 내부함수에서의 this
- 예외는 없다! 메서드의 내부여도 함수로서 호출한다면 this는 전역 객체를 의미함
var obj1 = {
outer: function() {
console.log(this); // obj1
var innerFunc = function() {
console.log(this);
}
innerFunc(); // 전역
var obj2 = {
innerMethod: innerFunc
};
obj2.innerMethod(); // obj2
}
};
obj1.outer();
'TIL' 카테고리의 다른 글
TIL 8/20 (1) - 알고리즘 문제풀이 2 (0) | 2024.08.20 |
---|---|
TIL 8/19 - 알고리즘 문제풀이 (0) | 2024.08.19 |
TIL 8/14 - JavaScript 문법 종합반 3주차 (0) | 2024.08.14 |
TIL 8/13 - JavaScript 문법 종합반 2주차, 3주차 초반 (0) | 2024.08.13 |
TIL 8/12 - JavaScript 문법 종합반 1주차, 2주차 초반 (0) | 2024.08.12 |