어떠한 변수에 접근 가능한 유효 범위이자, Javascript 엔진이 참조 대상이 되는 식별자(Identifier)를 검색하는 규칙의 집합
var
키워드는 함수 레벨 스코프, let
/const
는 블록 레벨 스코프를 따른다.
var
: function scopelet
& const
: block scopeJavaScript의 범위(scope)는 JavaScript에 대한 변수의 접근 가능성을 결정하는 코드의 현재 컨텍스트를 나타낸다. 두 가지 유형의 범위는 로컬(지역)
및 전역
이다.
외부
에서 선언된 변수.내부
에 선언된 변수.🌲 function-scope : var
var species = "human"; // 전역변수 선언 function transform() { var species = "monkey"; // 지역변수 선언(함수 scope 내) console.log(species); } console.log(species); // human (전역변수로 인식) transform(); // monkey (함수 내 지역변수로 인식) console.log(species); // human (전역 변수로 인식)
❗️ var키워드로 선언된 변수 는 항상 함수 범위이다. (함수를 별도의 범위가 있는 것으로 인식한다).
따라서 이 로컬 범위 변수는 전역 범위에서 액세스할 수 없다.
🌲 block-scope : let / const
기능 블록
, if문
, for문
및 while루프
를 비롯한 모든 종류의 블록에서 새로운 로컬 범위가 생성
// 예시 1 if (true) { const message = 'Hello'; // 블록 범위(if문) 내 const 선언 } console.log(message); // ReferenceError: message is not defined // 예시 2 --- if 문 if (true) { // "if" block scope const message = 'Hello'; console.log(message); // 'Hello' } console.log(message); // throws ReferenceError // 예시 3 --- for 문 for (const color of ['green', 'red', 'blue']) { // "for" block scope const message = 'Hi'; console.log(color); // 'green', 'red', 'blue' console.log(message); // 'Hi', 'Hi', 'Hi' } console.log(color); // throws ReferenceError console.log(message); // throws ReferenceError // 예시 4 --- while 문 while (/* condition */) { // "while" block scope const message = 'Hi'; console.log(message); // 'Hi' } console.log(message); // => throws ReferenceError
❗️
const
,let
은 블록범위 내에서 유효하기 때문에 블록범위 밖에서 message를 호출하면 undefined 이다.
// 예시 3 let species = "human"; // 전역 변수 선언 if (fullMoon) { let species = "monkey"; // 블록 범위(if문 중괄호 내) 변수 선언 console.log(`It is a full moon. Lupin is a ${species}.`); // monkey } console.log(`It is not a full moon. Lupin is a ${species}.`); // human
똑같은 경우에
var
를 사용하면 다른 결과가 나온다var species = "human"; if (fullMoon) { // 블록 범위(if문) 내 새로운 변수 값을 선언한다 var species = "monkey"; console.log(`It is a full moon. Lupin is a ${species}.`); // monkey } console.log(`It is not a full moon. Lupin is a ${species}.`); // monkey
❗️ var키워드로 선언된 변수 는 항상 함수 범위만 인식하고 블록 범위는 인식하지 않기 때문에,
if문 안의 변수는 새로운 값을 할당한 것으로 인식한다.(동일한 범위에서 동일한 변수를 다시 할당한다고 인식)
var a = 100; console.log(a)
var
👉 변수 선언
a = 100
👉 100이라는 값을 a에 할당하여 값 초기화
console.log(a)
👉 사용
변수가 선언되고 초기화되기 전에 사용하려고 하면 undefined.
console.log(a) // undefined (선언하기 전에 사용부터 했기 때문에 값이 undefined) var a = 100; // 위 코드를 자바스크립트가 해석하는 순서 var x; // var hoisting console.log(x); // 할당된 값이 없어서 undefined x = 100; // 이제서야 값 할당
function run() { // function scope const message = 'Run, Run, Run!'; if (true) { // block scope const friend = 'Bubba'; console.log(message); // 'Run, Run, Run!' } console.log(friend); // Error } run();
함수 scope(
run()
) 안에 블록 scope (if문
) 이 중첩되어 있다면,
겉에 감싸고 있는 함수 scope를 outer scope라고 하며,
안에 있는 if문 블록 scope를 inner scope 라고 한다.let, const 는 블록범위 변수이기 때문에, if문 블록scope 안에서는 외부 변수에 접근이 가능하지만, 외부에서는 블록scope 안의 변수에 접근할 수 없다.
function outerFunc() { // the outer scope let outerVar = 'I am from outside!'; function innerFunc() { // the inner scope console.log(outerVar); // 'I am from outside!' } return innerFunc; } const inner = outerFunc(); inner();
내부 함수 범위(
innerFunc()
)는 외부 함수 범위(outerFunc()
)에 포함된다.
자바스크립트는 내부 함수 범위(innerFunc()
)에서 외부 함수 범위(outerFunc()
)에 있는 변수를 읽어들인다. 이게 가능한 이유는 렉시컬 환경(lexical scoping) 때문이다.
Lexical scoping
이란, 중첩된 함수 scope 내에 선언된 변수들에 대해, 내부에서 외부 범위의 변수에 접근 가능하도록 결정하는 환경적 범위를 말한다.즉, 내부 함수는 외부 함수 범위에 접근할 수 있다. 내부 함수는 외부 함수의 범위를 동일하게 구성하고 있으며, 렉시컬 환경에 의해, 외부 함수에 선언된 변수에 접근 가능하다. (클로저)
함수를 어디서 호출하는지가 아니라 어디에 선언하였는지에 따라 결정되는 것을 말한다.
즉, 함수를 어디서 선언하였는지에 따라 상위 스코프를 결정한다는 뜻이며, 가장 중요한 점은 함수의 호출이 아니라 함수의 선언에 따라 결정된다는 점이다.
다른 말로, 정적 스코프(Static scope) 라 부르기도 하다.
// 렉시컬스코프 예시 var x = 1; // global function first() { var x = 10; // local second(); } function second() { console.log(x); } first(); // 1 second(); // 1
값이 10, 1이 아니라 1, 1로 나온 이유는?
자바스크립트에서는 위와 같은 코드를 작성할 때, 이미 실행 단계에서 코드들의 스코프를 결정한다.
즉, 함수 second()의 상위 스코프가 무엇인지에 따라 결정된다.자바스크립트는 렉시컬 스코프(Lexical Scope)를 따르므로 함수를 선언한 시점에 상위 스코프가 결정된다.
즉, 함수를 어디에서 호출하였는지는 스코프 결정에 아무런 의미를 주지 않는다는 말이다.
그렇기 때문에, second() 함수가 first() 함수 안에서 호출된 것과 상관없이 second() 함수는 global 범위에 선언되어 있으므로, global 범위에 있는 변수 x의 값 1이 두 번 출력된 것이다.
현재 스코프에서 식별자를 검색할 때, 상위 스코프를 연쇄적으로 찾아나가는 방식.
실행 컨텍스트의 LexicalEnviroment 프로퍼티에서 outer 참조값을 통해 상위 스코프로 체인처럼 연결됨.
스코프 체인(Scope Chain)은 일종의 리스트로서, 전역 객체와 중첩된 함수의 스코프의 레퍼런스를 차례로 저장하고, 의미 그대로 각각의 스코프가 어떻게 연결(chain)되고 있는지 보여주는 것을 말한다.
즉, 자기 자신의 스코프(scope)를 제외한 자신과 가장 가까운 변수 객체의 모든 스코프들을 스코프 체인이라 할 수 있다.