코어 자바스크립트 #02 (실행 컨텍스트, 호이스팅, 스코프 체인)
남들이 봤을 때도 딱 알아보기 쉽게 정리하고 싶은데
(그래야 내가 나중에 다시 보기에도 쉽고)
정리가 너무 조잡한 거 같네.. ㅠㅠ
스코프, 스코프 체인 개념은 다시 봐도 어렵다 헿^^
실행 컨텍스트
실행 컨텍스트execution context : 실행할 코드에 제공할 환경 정보들을 모아놓은 객체
스택 : 출입구가 하나뿐인 깊은 우물 같은 데이터 구조, 저장할 수 있는 스택이 넘칠 때 에러가 남
(ex. 데이터를 a, b, c, d 순으로 저장했다면 d, c, b, a 순으로 꺼냄, 후입 선출)
이것이 스택오버플로우의 그 '스택'!!
큐 : 파이프처럼 양쪽에 입구와 출구가 있는 구조
(ex. 데이터를 a,b,c,d 순으로 저장했다면 a, b, c, d 순으로 꺼냄, 선입선출)
동일한 환경에 있는 코드들을 실행할 때 필요한 환경 정보들을 모아 컨텍스트를 구성하고, 이를 콜 스택call stack에 쌓아올렸다가, 가장 위에 쌓여있는 컨텍스트와 관련 있는 코드들을 실행하는 식으로 전체 코드 환경과 순서를 보장한다.
※ 동일한 환경이란? 하나의 실행 컨텍스트를 구성할 수 있는 방법으로 전역공간, eval() 함수, 함수 등이 있음. 자동으로 생성되는 전역공간과 eval을 제외하면 흔히 실행 컨텍스트를 구성하는 방법은 함수를 실행하는 것뿐이다.
어떤 실행 컨텍스트가 활성화될 때 자바스크립트 엔진은 해당 컨텍스트에 관련된 코드들을 실행하는 데 필요한 환경 정보들을 수집해서 실행 컨텍스트 객체에 저장한다.
(이 객체는 자바스크립트 엔진이 활용할 목적으로 생성할 뿐 개발자가 코드를 통해 확인할 수는 없음. )
- VariableEnvironment : enviromentRecord(snapshot) / outerEnvironmentReference(snapshot)
현재 컨텍스트 내의 식별자들에 대한 정보 + 외부 환경 정보, 선언 시점의 LexicalEnvironment의 스냅샷snapshot으로 변경사항은 반영되지 않음 - LexicalEnvironment : environmentRecord / outerEnviromentReference
처음에는 VariableEnvironment와 같지만 변경 사항이 실시간으로 반영됨. - ThisBinding : this 식별자가 바라봐야 할 대상 객체
VariableEnvironment
VariableEnvironment에 담기는 내용은 LexicalEnvironment 와 같지만 최초 실행 시에 스냅샷을 유지한다는 차이가 있다. VariableEnvironment에 정보를 먼저 담은 다음, 이를 그대로 복사해서 LexicalEnvironment 를 만들고, 이후에는 LexicalEnvironment 를 주로 활용한다.
LexicalEnvironment
문서에는 '어휘적 환경', '정적 환경'으로 많이 등장하지만 이보다는 백과사전에서 단어를 검색하면 단어에 대한 설명이 나오는 것과 비슷한 '사전적인' 느낌으로 이해하면 좋다. 즉, "현재 컨텍스트 내부에는 a, b, c와 같은 식별자들이 있고 그 외부 정보는 D를 참조하도록 구성돼있다"라는 컨텍스트를 구성하는 환경 정보들을 사전에서 접하는 느낌으로 모아놓은 것이다.
environmentRocord와 호이스팅
environmentRecord에는 현재 컨텍스트와 관련된 코드의 식별자 정보들이 저장된다. 컨텍스트를 구성하는 함수에 지정된 매개변수 식별자, 선언한 함수가 있을 경우 그 함수 자체, var로 선언된 변수의 식별자 등이 식별자에 해당한다. 컨텍스트 내부 전체를 처음부터 끝까지 쭉 훑어 나가며 순서대로 수집한다. 변수 정보를 수집하는 과정을 모두 마쳤더라도 아직 실행 컨텍스트가 관여할 코드들은 실행되기 전의 상태인데, 코드가 실행되기 전임에도 자바스크립트 엔진은 이미 해당 환경에 속한 코드의 변수명들을 모두 알고 있게 되는 셈이다. 이 말은,
⇒ '자바스크립트 엔진은 식별자들을 최상단으로 끌어올려놓은 다음 실제 코드를 실행한다'라고 생각할 수 있다
(실제로 끌어올리는 것은 아닌 가상의 개념)
'끌어올리다' 라는 의미의 호이스팅hoisting 이 여기에서 나온 것.
※ 전역 실행 컨텍스트는 변수 객체를 생성하는 대신 자바스크립트 구동 환경이 별도로 제공하는 객체, 즉 전역 객체 global object를 활용한다. 전역 객체에는 브라우저의 window, Node.js의 global 객체 등이 있습니다. 이들은 자바스크립트 내장 객체native object가 아닌 호스트 객체host object로 분류된다.
호이스팅 규칙
변수 : 선언부와 할당부를 나누어 선언부만 끌어올림 (변수의 할당부는 그자리에 둠)
함수 선언문 : 함수 전체를 끌어올림
함수 표현식 : 변수 선언부만 끌어올림
함수 선언문과 함수 표현식
함수 선언문function declaration : function 정의부만 존재하고 별도의 할당 명령이 없는 것, 반드시 함수명이 정의되어 있어야 함, 기명 함수 표현식
함수 표현식function expression : 정의한 function을 별도의 변수에 할당하는 것(=함수를 다른 변수의 값으로써 '할당'), 함수명 없어도 됨, 상대적으로 안전, 익명 함수 표현식
function a () { } // 함수 선언문. 함수명 a가 곧 변수명.
a(); // 실행 OK
var b = function () { } // (익명) 함수 표현식. 변수명 b가 곧 함수명.
b(); // 실행 OK
var c = function d () { } // 기명 함수 표현식. 변수명은 c, 함수명은 d.
c(); // 실행 OK
d(); // 에러!
※기명 함수 표현식 주의점 : 외부에서는 함수명으로 함수를 호출할 수 없음! 함수명은 오직 함수 내부에서만 접근할 수 있음!
override : 동일한 변수명에 서로 다른 값을 할당할 경우 나중에 할당한 값이 먼저 할당한 값을 덮어씌움
스코프, 스코프 체인, outerEnvironmentReference
스코프scope : 식별자에 대한 유효범위
스코프 체인scope chain : '식별자의 유효 범위'를 안에서부터 바깥으로 차례로 검색해나가는 것
그리고 이것을 가능케 하는 것이 LexicalEnvironment의 두 번째 수집자료인 outerEnvironmentReference 이다.
outerEnvironmentReference 는 현재 호출된 함수가 선언될 당시의 LexicalEnvironment를 참조한다. '선언'이라는 행위가 일어날 수 있는 시점이란 콜 스택 상에서 어떤 실행 컨텍스트가 활성화 된 상태일 때뿐이다. 어떤 함수를 선언(정의)하는 행위 자체가 하나의 코드에 지나지 않으며, 모든 코드는 실행 컨텍스트가 활성화 상태일 때 실행되기 때문이다.
예를 들어 a함수 내에 b함수, b 함수 내에 c 함수를 선언한 경우, 함수 C의 outerEnvironmentReference 는 함수 B LexicalEnvironment를 참조하는 식이다. 이렇게 계속 찾아 올라가면 마지막엔 전역 컨텍스트의 LexicalEnvironment가 있을 것이고, 각 outerEnvironmentReference는 오직 자신이 선언된 시점의 LexicalEnvironmen만 참조하고 있으므로 가장 가까운 요소부터 차례대로 접근하고, 다른 순서로 접근하는 것은 불가능하다. 이런 구조적 특성 덕분에 여러 스코프에서 동일한 식별자를 선언한 경우에는 무조건 스코프 체인 상에서 가장 먼저 발견된 식별자에만 접근 가능하게 된다.