- 식별자가 유효한 범위
- 식별자 자신이 선언된 위치에 의해 다른 코드가 자신을 참조할 수 있는 유효 범위
var x = 'global';
function foo() {
var x = 'local';
console.log(x); // local
}
foo();
console.log(x); // global
위의 x 는 식별자 이름은 같지만 스코프는 다르다.
자스 엔진은 식별자를 결정할 때 "식별자를 검색할 때 사용하는 규칙"이 있는데 이 때 코드의 문맥과 환경을 고려한다.
원래 식별자의 이름은 유일해야하는데 스코프를 통해서 식별자의 이름의 충돌을 방지할 수 있다.
function foo(){
var x=1;
var x=2;
console.log(x);//2
}
foo();
function bar(){
let x=1;
let x=2; // SyntaxError: Identifier 'x' has already been declared
}
함수가 중첩되면 스코프도 계층적인 구조를 가지게 되는데, 자스 엔진은 변수를 참조하는 코드의 스코프에서 시작해서 상위 스코프의 방향으로 이동하며 선언된 변수를 식별자 해결(Identifier Resolution) 한다.
식별자 해결이 뭐징? : 사용할 변수/함수를 결정하는 것을 어렵게 말햇네
// 전역 함수
function foo() {
console.log('global function foo');
}
function bar() {
// 중첩 함수
function foo() {
console.log('local function foo');
}
foo(); // ①
}
bar();
① 에서 호출한 foo 함수는 전역에서 선언한 foo 함수가 아니라 bar 내부에서 선언한 foo 함수이다.
① 에서 foo() 를 호출하면 현재 실행중인 실행컨텍스트(bar)의 렉시컬 환경에서 foo 를 먼저 검색한다. 있으면 그걸 참조하고 없으면 외부 렉시컬에서 찾기를 반복한다.
이게 식별자를 검색하는 규칙임 😇
지역은 함수 몸체 내부를 뜻하고 지역 스코프를 만든다. = 함수에 의해서 생성된 스코프이다.
var 같은 경우에는 블록 레벨 스코프를 인정하지 않고 함수 레벨 스코프를 생성한다.
var a=10;
function foo(){
if(true){
var a=20;
var b=30;
console.log(a);//20
}
console.log(b);//30
}
console.log(a);//20
console.log(b);//Reference Error 남
15장에서 제대로 다루기 때문에 가볍게 개념만 이해하고 넘어가야겠다.
우선 블록은 중괄호로 감싼 여러개의 구문이다. {요 사이에 것들}
ES6의 let, const 가 블록 레벨 스코프를 이룬다.
let a=10;
function foo(){
let b=20;
function bar(){
let c=30;
console.log(a);//10
console.log(b);//20
console.log(c);//30
}
console.log(a);//10
console.log(b);//20
console.log(c);//참조 오류
}
console.log(a);//10
console.log(b);//참조 오류
console.log(c);//참조 오류
동적 스코프: 함수가 호출되는 시점에 상위 스코프가 결정되는 방식
정적 스코프: 함수가 정의되는 시점에 상위 스코프가 결정되는 방식
자바스크립트는 렉시컬 스코프이다. 함수가 언제 호출되는지와 관계 없이 함수가 정의 되는 시점의 상위스코프를 기억하는 것이다.
var x=1;
function foo(){
var x=10;
bar();
}
function bar(){
console.log(x);
}
foo();//1
bar();//1
위의 예제에서 볼 수 있듯이 bar 의 상위 스코프는 foo 함수가 아닌 전역 스코프이다. 그렇기 때문에 전역에서 할당된 1이 두 번 출력된다.
방심하면 충분히 헷갈릴 수 있는 개념이기 때문에 꼭 잘 생각하자