✍️ 스코프에 대해서 조금 더 알아보자.
Scope는 우리말로 번역하면 ‘범위’라는 뜻이다. 즉, 스코프란 ‘변수에 접근할 수 있는 범위’라고 할 수 있다.
참조 대상 식별자(변수, 함수명 등 식별할 수 있는 유일한 이름)를 찾아내기 위한 규칙이며, 자바스크립트는 이 규칙대로 식별자를 찾는다.
대부분의 프로그래밍 언어는 블록 레벨 스코프(Block-level scope)를 따르지만 자바스크립트는 함수 레벨 스코프(Function-level scope)를 따른다.
식별자 결정 과정 : 어떠한 변수를 참조할 것인지 결정하는 과정
👉 1) 함수 레벨 스코프(Function-level scope)
함수 내에서 선언된 변수는 함수 내에서만 유효하며 함수 외부에서는 참조할 수 없다. 즉, 함수 내부에서 선언한 변수는 지역 변수이며 함수 외부에서 선언한 변수는 모두 전역 변수이다.
2) 블록 레벨 스코프(Block-level scope)
모든 코드 블록(함수, if 문, for 문, while 문, try/catch 문 등) 내에서 선언된 변수는 코드 블록 내에서만 유효하며 코드 블록 외부에서는 참조할 수 없다. 즉, 코드 블록 내부에서 선언한 변수는 지역 변수이다.
1) 전역 스코프 (Global scope)
코드 어디에서든지 참조할 수 있다. 변수가 함수 외부나 중괄호{}
외부에 선언되었다면, 전역 스코프에 정의된다고 한다.
2) 지역 스코프 (Local scope or Function-level scope)
함수 코드 블록이 만든 스코프로 함수 자신과 하위 함수에서만 참조할 수 있다.
1) 전역 변수 (Global variable)
전역에서 선언된 변수이며 어디에든 참조할 수 있다.
2) 지역 변수 (Local variable)
지역(함수) 내에서 선언된 변수이며 그 지역과 그 지역의 하부 지역에서만 참조할 수 있다.
var lscope = 'global';
function fscope(){
console.log(lscope);
}
fscope(); // global
lscope
변수를 불러오는 경우lscope
를 찾아 온다.var lscope = 'global';
function fscope(){
var lscope = 'local';
console.log(lscope);
}
fscope(); // local
lscope
변수를 불러오는 경우fscope
라는 함수 안에 lscope
변수가 선언 되어있기 때문에 console.log(lscope)
는 자기 자신에게 가까운 쪽에서 선언되어있는 것을 가리키게 되어 local
을 출력한다.block-level scope란 code block({ … })내에서 유효한 scope를 의미한다.
자바스크립트는 function-level-scope를 사용하는데, 함수 코드 블럭 내에서 선언된 변수는 함수 코드 블럭 내에서만 유효하고 함수 외부에서는 유효하지 않다(참조할 수 없다)는 것이다.
❗️ CMAScript 6에서 도입된 let
keyword를 사용하면 block-level scope를 사용할 수 있다.
// var 키워드 사용 ▼
var x = 0;
{
var x = 1;
console.log(x); // 1
}
console.log(x); // 1
// let 키워드 사용 ▼
let y = 0;
{
let y = 1;
console.log(y); // 1
}
console.log(y); // 0
자바스크립트는 함수가 선언된 시점에서의 유효범위를 갖는다. 이러한 유효범위의 방식을 정적 유효범위(static scoping), 혹은 렉시컬(lexical scoping)이라고 한다.
var i = 5;
function a(){
var i = 10;
b();
}
function b(){
document.write(i);
}
a();
변수 i
는 전역변수이다.
함수 a
는 변수 i
를 지역변수로 가지고 있으며 다시 함수 b
를 호출한다.
함수 b
는 변수 i
를 출력한다.
1) 함수 a
를 호출한다. → 함수 내부 i
값=10 이되며, 그 후 함수 b
를 출력한다.
2 ) i
는 먼저 함수 b
내부에 i
지역변수를 찾는다.
3 ) 지역변수가 없다면 전역 변수를 찾는다.
이때, b
를 호출하고 있는 함수 a
의 지역변수 VS 함수b
가 현재 정의된 시점에서의 전역변수 중 어느것을 따라갈까?
→ 답은 i = 5 이다.
함수 b
가 선언된 시점에서 i
의 전역변수가 사용된다. 함수b
가 호출된 시점은 영향을 주지 못한다.
즉 '사용될 때' 가 아닌 '정의될 때' 사용된다. 이것을 정적 유효범위, 렉시컬 스코핑 이라고 한다.
글로벌 영역에 변수를 선언하면 이 변수는 어느 곳에서든지 참조할 수 있는 global scope를 갖는 전역 변수가 된다. 전역 변수는 전역 객체(Global Object) window의 프로퍼티이다.
// x.js
function foo() {
// var i = 0;
i = 0;
// ...
}
// y.js
for (var i = 0; i < 5; i++) {
foo();
console.log(i);
}
x.js
와 y.js
, 2개의 파일로 분리된 JS 코드를 불러온다. 두 개의 파일 안에는 의도하지않은 변수 i
가 중복으로 존재한다.
HTML에서 이 2개의 자바스크립트 파일을 로드하면 변수 i는 중복된다.
x.js
의 변수 i
는 var
키워드를 사용하지 않았으므로 암묵적으로 전역 변수화 되었고, y.js
의 변수 i
는 전역변수이다.
이때 자바스크립트는 변수명의 중복이 허용되므로 에러메시지를 발생시키지는 않지만, 무한 반복 상태에 빠질 수 있게된다.
코드가 길어지면 변수명의 중복이 발생하기 쉬워지게되고, 예기치 못한 이상 동작의 원인이 되기 쉬우며 오류를 해결하는 데에도 시간이 많이 들게 된다.
❓ 암묵적 전역 변수란
:var
키워드를 생략한 변수는 암묵적으로 전역 변수가 되며, 이것을 암묵적 전역 변수(implicit global)라고 한다.
✍️ 이처럼 개발자의 의도와는 상관없이 동작하는 (1) '전역변수' 그리고 (2) '암묵적 전역 변수' 는 오류 발생 원인이 될 가능성이 크다.
따라서, 전역변수를 반드시 사용하여야 할 이유가 없다면 (1) '지역변수' 를, 그리고 (2) 'let
' 을 사용하는 것이 좋다. 또한 변수의 범위인 스코프는 좁을수록 좋다.
📌 추가)
호이스팅은 변수 선언이 스코프의 선두로 끌어 올려진 것처럼 동작하는 자바스크립트 고유의 특징을 말한다.
reference
devyj-scope
JS-scope
JS-static scoping
medium-scope