스코프와 스코프 체인
스코프(Scope)와 스코프 체인(Scope Chain)
스코프(Scope)
는 식별자에 대한 유효한 범위를 의미합니다.
이러한 스코프를 안쪽에서부터 바깥쪽으로 차례대로 검색해나가는 것을 스코프 체인(Scope Chain)
이라고 합니다.
그리고 이것을 가능하게 하는 것이 바로 렉시컬 환경(Lexical Environment)
의 Outer Environment Reference
입니다.
환경 레코드의 중첩 구조
모든 환경 레코드는 Outer Environment Reference
를 가지고 있습니다.
이것은 현재 환경 레코드의 바깥쪽 환경 레코드를 참조하는 역할을 합니다.
예제를 통해 살펴보겠습니다.
var a = 1;
var outer = function () {
var inner = function () {
console.log(a);
var a = 3;
};
inner();
console.log(a);
};
outer();
console.log(a);
Outer Environment Reference
를 객체로 표현하여 나타내면 아래와 같습니다.
Outer Environment Reference는 실제로 존재하는 객체가 아니며, 유저가 직접
접근할 수 없습니다.
이해를 돕기 위해 객체로 표현하였습니다.
시작
전역 컨텍스트가 활성화 됩니다.
GLOBAL = {
OuterEnvironmentReference: null,
EnvironmentRecord: {
a: undefined,
outer: undefined
}
}
var a = 1; var outer = function ()
전역 컨텍스트의 EnvironmentRecord
에 a와 outer가 저장됩니다.
GLOBAL = {
OuterEnvironmentReference: null,
EnvironmentRecord: {
a: 1,
outer: function (){}
}
}
outer()
outer 함수 실행 컨텍스트가 활성화 됩니다.
GLOBAL = {
OuterEnvironmentReference: null,
EnvironmentRecord: {
a: 1,
outer: function (){}
}
};
outer = {
OuterEnvironmentReference: GLOBAL,
EnvironmentRecord: {
inner: undefined
}
}
inner()
inner 함수 실행 컨텍스트가 활성화 됩니다.
GLOBAL = {
OuterEnvironmentReference: null,
EnvironmentRecord: {
a: 1,
outer: function (){}
}
};
outer = {
OuterEnvironmentReference: GLOBAL,
EnvironmentRecord: {
inner: function (){}
}
};
inner = {
OuterEnvironmentReference: outer,
EnvironmentRecord: {
a: undefined
}
}
console.log(a);
inner 함수 실행 컨텍스트에서 a를 찾습니다.
현재 활성 상태인 inner 함수의 EnvironmentRecord
에 a가 발견되었으나, 값이 할당되지 않았기 때문에 undefined
가 출력됩니다.
GLOBAL = {
OuterEnvironmentReference: null,
EnvironmentRecord: {
a: 1,
outer: function () {},
},
};
outer = {
OuterEnvironmentReference: GLOBAL,
EnvironmentRecord: {
inner: function () {},
},
};
inner = {
OuterEnvironmentReference: outer,
EnvironmentRecord: {
a: undefined,
},
};
var a = 3;
inner 함수 실행 컨텍스트의 EnvironmentRecord
에 a가 저장됩니다.
GLOBAL = {
OuterEnvironmentReference: null,
EnvironmentRecord: {
a: 1,
outer: function () {},
},
};
outer = {
OuterEnvironmentReference: GLOBAL,
EnvironmentRecord: {
inner: function () {},
},
};
inner = {
OuterEnvironmentReference: outer,
EnvironmentRecord: {
a: 3,
},
};
}
(inner 함수 종료)
inner 함수 실행 컨텍스트가 콜 스택에서 제거됩니다. 바로 아래의 outer 실행 컨텍스트가 다시 활성화 됩니다.
엔진은 7번줄로 이동합니다.
GLOBAL = {
OuterEnvironmentReference: null,
EnvironmentRecord: {
a: 1,
outer: function () {},
},
};
outer = {
OuterEnvironmentReference: GLOBAL,
EnvironmentRecord: {},
};
console.log(a);
outer의 EnvironmentRecord
에서 a를 찾습니다.
현재 활성 상태인 outer 함수의 EnvironmentRecord
에는 a가 없기 때문에 바깥쪽 환경인 전역 컨텍스트로 이동하여 a를 찾습니다.
전역 컨텍스트의 EnvironmentRecord
에 a가 있으므로 1이 출력됩니다.
GLOBAL = {
OuterEnvironmentReference: null,
EnvironmentRecord: {
a: 1,
outer: function () {},
},
};
outer = {
OuterEnvironmentReference: GLOBAL,
EnvironmentRecord: {},
};
}
(outer 함수 종료)
outer 함수가 종료됩니다. outer 함수가 콜 스택에서 제거되고 바로 아래의 전역 컨텍스트가 다시 활성화 됩니다.
엔진은 10번줄로 이동합니다.
GLOBAL = {
OuterEnvironmentReference: null,
EnvironmentRecord: {
a: 1,
outer: function () {},
},
};
console.log(a);
전역 컨텍스트의 EnvironmentRecord
에서 a를 찾습니다.
전역 컨텍스트의 EnvironmentRecord
에 a가 있으므로 1이 출력됩니다.
GLOBAL = {
OuterEnvironmentReference: null,
EnvironmentRecord: {
a: 1,
outer: function () {},
},
};
종료
전역 컨텍스트가 콜 스택에서 제거됩니다.
전역변수와 지역변수
전역변수는 전역 스코프에서 선언된 변수를 의미하며, 어디서든 접근이 가능합니다.
예제에서는 전역 스코프에서 선언한 a와 outer가 전역변수에 해당합니다.
outer 내부에서 선언한 inner와 inner 내부에서 선언한 a는 지역변수에 해당합니다.
전역변수를 지양해야하는 이유는 뭘까요?