Scope
(유효범위, 스코프)유효범위(
Scope
)는JavaScript
뿐만 아니라 모든 프로그래밍 언어에서 가장 기본적인 개념 중 하나로 반드시 알고 넘어가야 한다.하지만
JavaScript
의 유효범위는 다른 언어의 유효범위와 다르다.
프로그래밍 언어에서 유효범위는 어느 범위까지 참조하는 지를 뜻한다.
전역 스코프 (Global Scope
)
지역 스코프 (Local Scope
)
Scope
(유효범위)의 특징함수 단위의 유효범위 (function-level-scope
)
변수명 중복 허용
암묵적 선언 (Implied Globals
)
Lexical Scoping
(Static Scoping
)
function-level-scope
function-level-scope
란?
함수 코드 블럭 내에서 선언된 변수는 함수 코드 블럭 내에서만 유효하고, 함수 외부에서는 유효하지 않다는 것이다.
var x = 0;
{
var x = 1;
console.log(x); // 1
}
console.log(x); // 1 - 결론적으로 var로 선언한 x가 값이 1로 변경
let y = 0;
{
let y = 1;
console.log(y); // 1
}
console.log(y); // 0 - 결론적으로 let로 선언한 y는 함수 내부에서만 1
클로벌 영역에 변수를 선언하면, 이 변수는 어느 곳에서든지 참조할 수 있는 Global Scope
를 갖는 전역 변수가 된다.
var global = 'global';
function foo() {
var local = 'local';
console.log(global); // global
// Global 영역의 변수로 어디서든 사용 가능
console.log(local); // local
}
foo();
console.log(global); // global
console.log(local); // Uncaught ReferenceError: local is not defined
// Local 영역의 변수로 함수 내에서만 사용 가능
Implied Globals
)명시적으로 변수 앞에 var
를 붙여주지 않으면 암묵적 전역 변수가 된다.
하지만 암묵적 전역은 오류를 발생시키는 원인이 될 가능성이 크기 때문에, 반드시 var
, let
, const
키워드를 사용하여 변수를 선언한 다음 사용해야 한다.
하지만 오타나 문법지식의 미비로 인한 실수는 언제나 발생할 수 있기 때문에, 이를 위해 ES5
부터 Strict Mode
(엄격 모드)가 추가되었으며, ES6
에서 도입된 클래스와 모듈은 기본적으로 Strict Mode
가 적용된다.
Strice Mode
(엄격 모드)란?
Strice Mode
(엄격 모드)는JavaScript
언어의 문법을 좀 더 엄격히 적용하여 오류를 발생시킬 가능성이 높거나 자바스크립트 엔진의 최적화 작업에 문제가 될 수 있는 코드에 대해 명시적 에러를 발생시킨다.
ESLint
와 같은 도구를 사용해도Strice Mode
와 유사한 효과를 얻을 수 있다.
ESLint
도구는 정적 분석(Static Analysis
)기능을 통해 소스코드를 실행하기 전에 소스코드를 스캔하여 문법적 오류만 아니라 잠재적 오류까지 찾아내고 오류의 원인을 리포팅해주는 유용한 도구다.
ESLint
도구는Strice Mode
가 제한하는 오류는 물론 코딩 컨벤션을 설정 파일 형태로 정의하고 강제할 수 있기 때문에 더욱 강력한 효과를 얻을 수 있다.
function foo() {
x = 'x'; // ES6의 Strinc Mode 및 ESLint로 인해 오류 출력
var y = 'y';
}
foo();
console.log(x); // x
console.log(y); // Uncaught ReferenceError: y is not defined
// Local 영역의 변수로 함수 내에서만 사용 가능
Lexical Scoping
(Static Scoping
)자바스크립트는 함수가 선언된 시점에서의 유효범위를 갖는다.
예제 1
var number = 1234;
function printNumber() {
console.log(number);
}
function wrapper() {
number = 4321;
// printNumber 함수를 실행시키기 전에 number를 변경했기 때문에 값이 변경됨
printNumber();
}
wrapper(); // 4321
printNumber
함수를 실행하기 직전에number
의 값을 변경해주었으므로wrapper
함수를 실행시켰을 때에 값이 변경되어,1234
에서4321
가 출력된다.
예제 2
var number = 1234;
function printNumber() {
console.log(number);
}
function wrapper() {
var number = 4321;
console.log(number); // 4321
printNumber();
}
wrapper(); // 1234
wrapper
함수 안에서var
키워드를 사용하게 되면 결과가 달라진다.함수를 처음 선언하는 순간, 함수 내부의 변수는 자기 스코프로부터 가장 가까운 곳(상위 범위에서)있는 변수를 계속 참조하게 된다.
위에 예시에서는
printNumber
함수 안의number
변수는 선언 시 가장 가까운 전역변수number
를 참조하게 된다.
Scope Chain
)새롭게 정의된 스코프는 상위의 스코프에 접근할 수 있다.
스코프 체인은 Scope
의 가장 내부에서 Scope Chain
을 따라 바깥쪽으로 검색을 하게 된다.
Closure
)클로저는 함수가 선언된 환경의 (Lexical
) 스코프를 기억하여, 함수가 스코프 밖에서 실행될 때에도 이 스코프에 접근할 수 있게 하는 기술이다.
const counter = () => {
let count = 0;
// changeCounter는 inner 함수
// 객체를 리턴하고 있고, 객체 안에는 increase, decrease, show라는 inner 함수들을 저장
function changeCount(number) {
count += number;
}
return {
increase: function () {
changeCount(100);
},
decrease: function () {
changeCount(-10);
},
show: function () {
console.log(count);
}
}
};
const counterClosure = counter();
// counter를 실행하면, outer 함수 스코프를 기억하고 있는 클로저들이 담긴 객체를 반환
// counterClosure는 counter 함수 내부에 정의된 count나 changeCount에 접근 가능
counterClosure.increase();
counterClosure.show(); // 100
counterClosure.decrease();
counterClosure.show(); // 90
내부 함수는 외부 함수의 지역 변수에 접근 할 수 있는데, 외부 함수의 실행이 끝나서 외부 함수가 소멸된 이후에도 내부 함수가 외부 함수의 변수에 접근할 수 있다.
이러한 매커니즘을 클로저(
Closure
)라고 한다.
React Hook
에서의 클로저(Closure
)useState
의 작동 방식React
에서 함수형 컴포넌트의 상태 관리를 위해서는 컴포넌트 외부에 저장된 값을 사용하며 클로저(Closure
)를 통해 해당 값에 접근해서 상태를 비교하고 변경한다.
useState
는 컴포넌트 내부에서 값을 변경시키는 것이 아니라, 외부에 있는 값을 변경시키기 때문에 상태가 변경된 직후 컴포넌트가 가진 값은 이전의 값을 그대로 참조한다.
각 컴포넌트의 상태 정보는 배열 형태로 저장되기 때문에 상태를 변화시키는 hook
을 조건문이나 반복문 안에서 사용하면 잘못된 순서의 값을 참조하게 될 수 있다.
참고 사이트
aeong98.log - 스코프(Scope) 와 클로저(Closure)의 이해
암묵적 전역(Implicit Global), Strict Mode