앞서 함수를 알아봤습니다. 함수는 {}
이렇게 작성되는 코드블록을 가지고 있습니다. 사실 함수 뿐 아니라 이전에 살펴본 제어문도 코드블록을 가지고 있죠.
코드블록을 언급하는 이유는, 여기서 알아볼 스코프(scope)가 범위를 뜻하기 때문입니다.
스코프는 유효범위입니다. 지금껏 봐왔던 제어문과 함수의 코드블록 내에서 선언된 변수나 혹은 매개변수는 해당 제어문/함수의 코드블록 바깥에서는 접근하지 못합니다.
function add(x, y) {
return x + y;
}
add(1, 2);
console.log(x); // ReferenceError: x is not defined
함수에 전달된 매개변수는 함수 body 내부에서만 유효범위를 가지기 때문입니다. 그 안에서만 유효하기 때문에 바깥에서는 아무리 참조하려고 해도 가져올 수 없는 것이죠.
간단한 예제를 살펴봅시다.
let a = 'a in global';
function foo() {
let a = 'a in function';
console.log(a); // 'a in function'
}
foo();
console.log(a); // 'a in global'
똑같은 a
로 변수를 선언했지만 함수 내에서는 함수 내의 a
가, 외부의 console.log() 에서는 전역변수로 선언된 a
가 출력됩니다.
자바스크립트 엔진은 똑같은 이름의 변수가 2개 이상 있으면, 어떤 변수를 참조해야 할 것인지를 결정합니다. 변수는 식별자이기 때문에, 변수 이름은 중복될 수 없습니다. 따라서 결국 변수는 변수명과 스코프 두 가지 모두를 가지고 서로 다른 변수인지 판별하는 것입니다.
스코프는 전역(global)과 지역(local)로 나뉩니다. 전역에서 선언된 변수라면 전역 스코프를 가지고, 지역에서 선언된 변수는 지역 스코프를 갖게 됩니다.
간단하게 코드블록 {}
만으로 스코프를 나누어보겠습니다.
let a = 'global 1';
let b = 'global 2';
// local 1 scope
{
let c = 'local 1';
console.log(a, b, c); // 'global 1', 'global 2', 'local 1'
// local 2 scope
{
let b = 'local 2';
console.log(a, b, c); // // 'global 1', 'local 2', 'local 1'
}
}
console.log(a); // 'global 1'
console.log(b); // 'global 2'
console.log(c); // ReferenceError: c is not defined
이런 스코프를 지정할 때 ES6 문법인 let
과 const
를 사용하면 별 문제가 없습니다. 하지만 var
키워드를 사용해 변수를 선언하게 되면 함수 레벨 스코프에서만 우리가 원하는대로 작동하는 것을 볼 수 있습니다.
var a = 1;
{
var a = 2;
}
console.log(a); // 2
if(true) {
var a = 3;
}
console.log(a); // 3
for(var i = 0; i < 5; i++) {
var a = i;
}
console.log(a); // 4
function foo() {
var a = 999;
console.log(a); // 999
}
foo();
// 전역 변수가 변하지 않았음을 확인 가능
console.log(a); // 4
함수 내에서 선언한 변수만 지역변수로 취급되는 것을 확인할 수 있습니다. 반면 const
나 let
키워드는 블록 레벨 스코프를 지원합니다.
const a = 1;
{
const a = 2;
console.log(a) // 2
}
console.log(a); // 1
if(true) {
const a = 3;
console.log(a); // 3
}
console.log(a); // 1
for(let i = 0; i < 5; i++) {
const a = i;
}
console.log(a); // 1
function foo() {
const a = 999;
console.log(a); // 999
}
foo();
// 처음 선언한 변수 변하지 않음
console.log(a); // 1
const
로만 선언했기 때문에 만약 전역으로 선언한 a
가 값이 바뀌었다면 아마 상수를 바꿀 수 없다는 에러가 발생했을 것입니다. 하지만 에러가 발생하지 않고,
{}
코드 블록 내에서 선언된 모든 a
가 해당 블록 내의 값으로 할당되는 것을 확인할 수 있습니다.
렉시컬 스코프는 함수를 정의한 곳에 따라 상위 스코프를 결정합니다.
let a = 1;
// 전역함수
function foo() {
let a = 999;
bar();
}
// 전역함수
function bar() {
console.log(a); // 전역 변수 참조
}
위 예시는 함수를 모두 전역에 정의했습니다. 함수는 자신이 정의된 장소(전역/지역)를 기억해 상위 스코프의 변수를 참조합니다.
따라서 bar
함수가 어디서 호출되든 상관없이, bar
는 전역변수 a
를 참조하게 됩니다.
◾ 함수 👈 이전 글 보기