코어 자바스크립트 2장을 정리한 내용이다.
2장은 자바스크립트의 실행 컨텍스트에 대한 내용이다.
실행 컨텍스트는 실행할 코드에 제공할 환경 정보들을 모아놓은 객체이다.
동일한 환경에 있는 코드들을 실행할 때 필요한 환경 정보들을 모아 컨텍스트를 구성 하고, 이를 콜 스택에 쌓아올렸다가, 가장 위에 쌓여있는 컨텍스트와 관련있는 코드들을 실행
실행 컨텍스트에 담기는 정보는 다음과 같다.
VariableEnvironment 내부는 다음 2가지로 구성되어 있다.
여기서 '호이스팅'이라는 개념을 확인할 수 있다. 마치 변수(정확히는 선언부)가 최상단으로 끌어올려진 것처럼 보이기 때문이다. (실제로 끌어올리지는 않는다. 그렇기 보이는 것일 뿐.)
function a() {
/* ... */
} // 함수 선언문. 함수명 a가 곧 변수명.
a(); // 실행 OK.
var b = function() {
/* ... */
}; // (익명) 함수 표현식. 변수명 b가 곧 함수명.
b(); // 실행 OK.
var c = function d() {
/* ... */
}; // 기명 함수 표현식. 변수명은 c, 함수명은 d.
c(); // 실행 OK.
d(); // 에러!
console.log(sum(1, 2)); // 함수 전체가 호이스팅되기 때문에 정상적으로 3이 출력된다.
console.log(multiply(3, 4)); // 선언부만 호이스팅됐기 때문에 multiply is not a function 메세지가 뜬다.
function sum(a, b) {
// 함수 선언문 sum
return a + b;
}
var multiply = function(a, b) {
// 함수 표현식 multiply
return a * b;
};
function hello() {
for (var i=0; i<12; i++) {
...
}
console.log(i) // 함수 스코프가 적용되어 접근가능
}
hello(); //12
------------------------------------------
function hello() {
for (let i=0; i<12; i++) {
...
}
console.log(i) // 블록 스코프가 적용되기 때문에 i에 접근 불가
}
hello(); //ReferenceError: i is not defined
var a = 1;
var outer = function() {
var inner = function() {
console.log(a); // undefined로 초기화된 지역변수 a 접근
var a = 3;
};
inner();
console.log(a); // 전역 변수 a 접근
};
outer();
console.log(a); // 전역 변수 a 접근
출력 결과는 undefined, 1, 1이다.
위 예시를 기반으로 보면 inner 함수 내부에서 a 변수를 선언했기 때문에 inner 함수에서는 전역 변수 a를 접근할 수 없다. 이를 변수 은닉화라고 한다.
자바스크립트에서의 은닉화란?
객체의 데이터와 메서드를 외부에서 직접 접근하지 못하도록 제한하는 개념이다.
은닉화는 객체의 내부 구현을 외부로부터 감추고, 객체와 관련된 동작을 외부에서는 호출만 할 수 있도록 만든다.
클로저(Closure)를 통해 은닉화를 구현할 수 있다.
클로저를 이용한 은닉화 예시
function Person() {
let age = 25; // 외부에서 접근할 수 없는 private 변수
this.getAge = function() {
return age;
};
this.setAge = function(newAge) {
age = newAge;
};
}
const john = new Person();
console.log(john.getAge()); // 25
john.setAge(30);
console.log(john.getAge()); // 30
이 예시에서 age 변수는 Person 함수 외부에서 직접 접근할 수 없으며, 오직 getAge와 setAge 메서드를 통해서만 접근 및 수정이 가능하다.
클로저는 반환된 내부함수가 자신이 선언됐을 때의 환경(Lexical environment)인 스코프를 기억하여 자신이 선언됐을 때의 환경(스코프) 밖에서 호출되어도 그 환경(스코프)에 접근할 수 있는 함수를 말한다.
즉, 클로저는 자신이 생성될 때의 환경(Lexical environment)을 기억하는 함수다
function outerFunc() {
var x = 10;
var innerFunc = function () { console.log(x); };
return innerFunc; // 클로저
}
/**
* 함수 outerFunc를 호출하면 내부 함수 innerFunc가 반환된다.
* 그리고 함수 outerFunc의 실행 컨텍스트는 소멸한다.
*/
var inner = outerFunc();
inner(); // 10
실행 컨텍스트의 관점에서 보면, 내부함수가 유효한 상태에서 외부함수가 종료하여 외부함수의 실행 컨텍스트가 반환되어도, 외부함수 실행 컨텍스트 내의 활성 객체(Activation object - 변수, 함수 선언 등의 정보를 가지고 있음)는 내부함수에 의해 참조되는 한 유효하여 내부함수가 스코프 체인을 통해 참조할 수 있다.
렉시컬 스코프는 함수를 어디에 선언하였는지에 따라 상위 스코프가 결정되는 것을 뜻한다.
var x = 1;
function foo() {
var x = 10;
bar();
}
function bar() {
console.log(x);
}
foo(); // 1
bar(); // 상위 스코프는 전역 스코프이기 때문에 1 출력
코드의 안정성을 위해 전역변수 사용을 최소화하는 것이 좋다.
실행 컨텍스트의 thisBinding에는 this로 지정된 객체가 저장된다.
실행 컨덱스트 활성화 당시에 this가 지정되지 않은 경우 this에는 전역 객체가 저장된다.