코어 자바스크립트를 읽고 스터디 세션에서 공유를 위해 정리한 글입니다.
📌 실행 컨텍스트(execution context)는
실행할 코드에 제공할 정보들을 모아놓은 객체
다.
- JS는 어떤 실행 컨텍스트가 활성화 되는 시점에
변수를 끌어올리고(hoisting), 외부 환경 정보를 구성하고, this 값을 설정하는 등
의 동작을 수행한다.- 클로저를 지원하는 대부분의 언어에 비슷한 개념이 적용되어 있다.
📌 여기서 ‘동일한 환경’, 즉 실행 컨텍스트를 구성할 수 있는 방법
- 전역공간
- eval() 함수
- 함수
- 블록
{}
→ ES6 부터…
/* -------------------------------------------------------------------------- */
/* (1) */
/* -------------------------------------------------------------------------- */
var a = 1;
function outer() {
function inner() {
console.log(a); // undefined
var a = 3;
}
inner(); // ------------------------ (2)
console.log(a); // 1
}
outer(); // -------------------------- (3)
console.log(a); // 1
📌 전역 컨텍스트와 다른 컨텍스트의 차이점에 대해 이야기해 봐라.
- 함수 호출이나 eval을 이용해 생성되는 컨텍스트가 아니라 JS파일 실행 시 자동으로 생성되기 때문에 arguments가 존재하지 않는다.
- 전역 공간을 둘러싼 외부 스코프가 존재할 수 없어 스코프 체인상에는 전역 스코프 하나만 존재한다.
📌 실행 컨텍스트가 수집하는 정보
VariableEnvironment
: 현재 컨텍스트 내의 식별자들에 대한 정보 + 외부 환경 정보
(선언 시점의 LexicalEnviroment의 스냅샷으로 변경 사항은 반영되지 않음)LexicalEnvironment
: 초기에는VariableEnvironment
과 같지만 변경 사항이 실시간으로 반영된다.ThisBinding
: this 식별자가 바라봐야 할 대상 객체
활성화된 실행 컨텍스트의 수집 정보
📌 저자가 생각하는 Lexical의 의미…(참고만 하세요)
Lexical Environment의 한글 번역이 다양함. ‘어휘적, 정적 환경 등…’ → 어휘적은 사전적 표상적이지 않아 의미가 와닿지 않음. 정적은 시질적으로 환경이 계속 변하기 때문에 적절한 번역이 아니라고 생각함.
그래서 저자는사전적인
이 어울리는 의미라고 생각함.
컨텍스트를 구성하는 환경 정보들을 사전에서 접하는 느낌으로 모아놓은 환경이기 때문에…
→ 책에서는 여러 번역이 존재하는 한글이 아닌, 타인과의 커뮤니케이션을 원할히 할 수 있는 원어를 사용할 것임.
현재 컨텍스트와 관련된 코드의 식별자 정보들이 저장
된다.즉, 코드가 실행되기 전임에도 불구하고 JS엔진은 해당 환경에 속한 코드의 변수명을 모두 알고있게 된다.
JS 엔진은 식별자들을 최상단으로 올려놓은 다음 실행한다.
라고 설명하는 이해들 돕기위해 가상 개념인 호이스팅으로 대체해도 해도 이해에는 무리가 없을 것이다…📌 전역 실행 컨텍스트
- 변수 객체를 생성하는 대신 런타임 환경이 별도로 제공하는 객체(전역 객체)를 사용한다.
- 브라우저: window, Node.js: global 객체 등…
- 내장 객체(native object)가 아닌 호스트 객체(host object)로 분류된다.
function a(x) {
// 수집 대상 1(매개변수)
console.log(x); // (1)
var x; // 수집 대상 2(변수 선언)
console.log(x); // (2)
var x = 2; // 수집 대상 3(변수 선언)
console.log(x); // (2)
}
a(1);
호이스팅 개념을 적용하지 않았을 때의 예상되는 출력 값
(1) → 1
(2) → undefined (변수 선언 후 값을 할당하지 않았기 때문)
(3) → 2
📌 주의
이제부터 실제 동작을 대체해 설명하기 위한호이스팅 개념을 적용한 코드로
동작을 설명 함. 실제 엔진에서 이러한 코드적 변환을 거치지는 않는다.
function a() {
var a = 1; // 수집 대상 1(매개변수 선언)
console.log(a); // (1)
var a; // 수집 대상 2(변수 선언)
console.log(a); // (2)
var a = 2; // 수집 대상 3(변수 선언)
console.log(a); // (3)
}
📌 arguments란?
- 실행 컨텍스트 생성 시점에 함께 만들어지는 정보 중 하나
function a() {
var a; // 수집 대상 1의 변수 선언 부분
var a; // 수집 대상 2의 변수 선언 부분(메모리 변수 공간에 이미 식별자가 존재함 무시)
var a; // 수집 대상 3의 변수 선언 부분(메모리 변수 공간에 이미 식별자가 존재함 무시)
a = 1; // 수집 대상 1의 변수 선언 부분
console.log(a); // (1)
console.log(a); // (2)
a = 2; // 수집 대상 3의 변수 선언 부분
console.log(a); // (3)
}
// 호이스팅 전
function a() {
console.log(b);
var b = "bbb";
console.log(b);
function b() {
}
console.log(b);
}
// => 예상 결과: undefined나 error | bbb | 함수 b
// 호이스팅 후
function a() {
var b;
var b = function b() { }
console.log(b);
b = "bbb";
console.log(b);
console.log(b);
}
// => 실제 결과: 함수 b | bbb | bbb
function a() { // 함수명 a
}
const b = function () {
}
const c = function d() { // 변수명은 c, 함수명은 d
}
c(); // 실행 OK!
d(); // 에러
- 기명 함수 표현식은 외부에서 함수명으로 호출할 수 없다. 함수명으로는 함수 내에서만 접근 가능.
- 기명 함수 표현식의 용도: 과거에는 익명 함수 표현식은 undefined, unnamed라는 값이 나왔다. → 그래서 기명 함수 표현식이 디버깅에 이점이 있었다.
그러나 요즘 브라우저들은 모두 익명 함수의 name 프로퍼티에 변수명을 할당해준다.
// 호이스팅 전
console.log(sum(1,2));
console.log(multiply(3,4));
function sum(a,b){
return a + b;
}
var multiply = function (a,b) {
return a * b;
}
// 호이스팅 후
var sum = function sum(a,b){
return a + b;
}
var multiply;
console.log(sum(1,2)); // 3
console.log(multiply(3,4)); // multiply is not a function
multiply = function (a,b) {
return a * b;
}
❗ 함수 선언식의 위험성
- 함수 선언식은 초보 개발자의 접근성에는 좋을 수도 있다…
- 그러나 협업에 있어서 위험할 수 있다.(극단적 예시)
console.log(sum(10,3));
function sum(a,b){
return a+b;
}
// 10만년 전까지 시니어 개발자 수달이 여기까지 코드를 짜고 라인 2에서 원하는 결과(5)를 잘 얻어냈다.
// 10만년 후 현재 신입 개발자 해달이 코드를 짜기 시작했는데,
// 기존의 sum이 존재하는지 모르고 코드라인 10만에 문자열로 식을 이쁘게 보여주는 함수(sum)를 짰다.
// 코드라인 10만
function sum(a,b){
return `${a} + ${b} = ${a+b}`;
}
console.log(sum(10000, 20000)); // 10000 + 20000 = 30000
// 함수가 호이스팅 되어 라인 2의 결과도 해달이 구현한 함수의 내용대로 동작한다...
// 디버깅하기도 쉽지 않다.
스코프란 식별자에 대한 유효범위이다.
- ES5 까지는 전역 공간을 제외하면 오직 함수에 의해서만 스코프가 생성된다.
- ES6에서는 블록에 의해서도 스코프가 생성된다().
- var가 아닌 let, const, class, strict mode에서의 함수 선언 등에 대해서만 범위로서의 역할을 수행할 수 있다.
- 이 둘을 구분하기 위해 블록 스코프, 함수 스코프라는 용어 사용.
var a = 1;
var outer = function () {
var inner = function () {
console.log(a); // undefined
var a = 3;
}
inner();
console.log(a); // 1
}
outer();
console.log(a); // 1
변수 은닉화
라고 한다.