변수와 매개변수, 함수 등의 생존기간을 정하여 유효범위를 결정하는 것을 의미한다. 예를 들어 특정 변수는 유효범위를 넘어서면 참조될 수 없다.
var global = 'global';
function foo() {
var local = 'local';
console.log(global); // global
console.log(local); // local
}
foo();
console.log(global); // global
console.log(local); // local is not defined
JS에서는 코드가 나타나는 즉시 해석되어 특정 스코프 내에서 선언되지 않으면 모두 전역 스코프의 특징을 가지게 된다.
JS는 기본적으로 Non block-level scope를 가지고 있다.
var x = 0;
{
var x = 1;
console.log(x); // 1
}
console.log(x); // 1
let y = 0;
{
let y = 1;
console.log(y); // 1
}
console.log(y); // 0
블록 레벨 스코프를 갖는다면 변수 x
는 블록 외부와 내부의 값이 다를 것이다. 하지만 JS에서는 블록 레벨 스코프를 갖지 않기 때문에 위 코드와 같은 결과가 나타난다. 따라서 ECMAScript 6에서 도입된 let
을 사용하여 변수를 블록 레벨 스코프로 지정해주는 것이 좋다.
함수도 {}
로 감싸져 있지만, 조건문/ 반복문과는 구분해야 한다. JS에서 조건문/ 반복문는 Non block-level scope를 가지지만, 함수는 Function-level scope를 가진다. 즉, 함수는 Non Block-level scope가 아니다
var x = 'global';
function foo() {
var x = 'local';
console.log(x);
}
foo(); // local
console.log(x); // global
만약에 JS의 함수가 조건문이나 반복문과 같이 동작한다면 변수 코드의 출력은
local
local
이 됐을 것이다. 하지만 JS에서 함수는 Function-level scope를 가지기 때문에 함수 안에서의 변수 x
와 밖에서의 변수 x
는 다른 변수이다.
함수 안에 함수가 중첩되어 존재할 수도 있다. 이 때도, Function-level scope를 철저하게 따른다.
var x = 10;
function foo(){
var x = 100;
console.log(x);
function bar(){ // 내부함수
x = 1000;
console.log(x); // 1000
}
bar();
}
foo();
console.log(x); // 10
foo
함수를 실행하면 먼저 100이 저장된 x
를 출력한다. foo
함수 내부의 boo
함수를 정의하고 실행한다. boo
함수 내부에 x
에는 1000이 저장되어 있어 이 함수 내부에서의 x
값은 1000이다.x
에는 처음에 선언했던 대로 10이 저장되어 있다.함수가 중첩되어 있으면 내부 함수는 상위 스코프에 따라 변수 사용이 달라진다. 상위 스코프를 결정하는 방식은 두 가지가 있다.
- 함수가 호출된 위치에 따라 결정(동적 스코프)
- 함수가 선언된 위치에 따라 결정(렉시컬/정적 스코프)
JS를 비롯한 많은 언어들이 두번째 방법인 렉시컬 스코프를 따른다.
var x = 1;
function foo() {
var x = 10;
bar();
}
function bar() {
console.log(x);
}
foo(); // 1
bar(); // 1
bar
함수는 전역에서 선언되었기 때문에 foo
함수 내부에서 호출되더라도 x
값은 1이다.