스코프란 자바스크립트 엔진이 참조의 대상이 되는 식별자(Identifier)를 검색할 때 사용하는 규칙의 집합 이다. 즉, 어떤 변수를 사용하거나 함수를 호출하려고 할 때 해당하는 식별자로 사용하는데, 그 식별자를 검색하는 메커니즘이라고 이해하면 된다.
프로그래머가 코드를 짤 때, 변수 및 함수/블록 스코프를 어디에 작성하였는가에 따라 정해지는 스코프 를 렉시컬 스코프라고 한다. “렉시컬(Lexical)” 이라는 명칭이 붙은 이유는 자바스크립트 컴파일러가 소스코드를 토큰(Token)으로 쪼개서 의미를 부여하는 렉싱(Lexing) 단계에 해당 스코프가 확정되기 때문이다. 다시 쉽게 말하면, 변수 혹은 함수/블록이 어디에 써있는가를 보고 그 스코프를 판단하면 된다.
현재 스코프에서 식별자를 검색할 때 상위 스코프를 연쇄적으로 찾아나가는 방식 을 말한다. 실행 컨텍스트를 배웠다면 생성될 때마다 LexicalEnvironment가 만들어지고 그 안에 outer 참조 값이 있다는 것을 알 것이다. 바로 이 outer 참조 값이 상위 스코프의 LexicalEnvironment를 가리키기 때문에 이를 통해 체인처럼 연결되는 것이다.
즉, 다음과 같은 과정으로 스코프 체인을 검색한다.
null
일 때까지 계속하고 찾지 못한다면 에러를 발생시킨다.function doSomething(a) {
b = a + doSomethingElse(a*2);
console.log(b*3);
}
function doSomethingElse(a) {
return a - 1;
}
var b;
doSomething(2); // 15
이 코드에서 변수 b와 함수 doSomethingElse()는 doSomething()이 어떤 작업을 하는지 보여주는 ‘비공개’ 부분이라고 할 수 있다. 변수 b와 doSomethingElse()에 ‘접근’할 수 있도록 내버려 두는 것은 불필요할 뿐 아니라 ‘위험’할 수 있다. ```javascript function doSomething(a) { function doSomethingElse(a) { return a - 1; } var b; b = a + doSomethingElse(a2); console.log(b3); }
doSomething(2); // ```
이제 b와 doSomethingElse()는 외부에서 접근할 수 없어서 더는 바깥의 영향을 받지 않고, 오직 doSomething()만이 이들을 통제한다.
또한 위와 같이 숨기는 것은 예상치 못한 변숫값의 겹쳐쓰기를 방지한다.
var = 2;
(function foo() {
var a = 3;
console.log(a); // 3
})();
console.log(a); // 2
위 코드는 별다를 바 없어 보일 수 있지만, 이 코드에서 함수는 보통의 선언문이 아니라 함수 표현식으로 취급된다.
- 위 함수는 익명함수로 사용할 수 있지만, 몇가지 단점이 있다.
- 익명 함수는 스택 추적 시 표시할 이름이 없어서 디버깅이 어려울 수 있다.
- 이름 없이 함수 스스로 재귀 호출을 하려면 불행히도 폐기 예정인 arguments.callee 참조가 필요하다.
- 이름은 보통 쉽게 이해하고 읽을 수 있는 코드 작성에 도움이 되는데, 익명 함수는 이런 이름을 생략한다.
- 그래서 보통 사용되는 함수 이름이 IIFE
- Immediately, Invokely, Function, Expression
for (var i=0; i<10; i++) {
console.log(i);
}
var foo = true;
if (foo) {
var bar = foo * 2;
bar = something(bar);
console.log(bar);
}
for (var i=0; i<10; i++) {
console.log(i);
}
console.log(i);
try {
undefined(); // illegal operation to force an exception!
}
catch (err) {
console.log(err); // work!
}
console.err(err); // ReferenceError: `err` not found
예제에서 보듯, 변수 err은 오직 catch 문 안에서만 존재하므로 다른 곳에서 참조하면 오류가 발생한다.
var foo = true;
if (foo) {
let bar = foo * 2;
bar = something(bar);
console.log(bar);
}
console.log(bar); // ReferenceError
let
을 사용한 선언문은 속하는 스코프에서 호이스팅 효과를 받지 않는다.