새소식

프론트엔드 공부/자바스크립트

스코프(Scope) 지역변수, 전역변수 / let, const, var 차이

  • -

참조 : mdn, poiemaweb, codestates

스코프(Scope)

 

스코프 - MDN Web Docs 용어 사전: 웹 용어 정의 | MDN

현재 실행되는 컨텍스트를 말한다. 여기서 컨텍스트는 값과 표현식이 **"표현"**되거나 참조 될 수 있음을 의미한다. 만약 변수 또는 다른 표현식이 "해당 스코프"내에 있지 않다면 사용할 수 없

developer.mozilla.org


JavaScript에서의 스코프는 "변수의 유효범위"로 사용됩니다.

컨텍스트는  표현식이 "표현"되거나 참조 될 수 있음을 의미한다. 만약 변수 또는 다른 표현식이 "해당 스코프"내에 있지 않다면 사용할 수 없다. 스코프는 또한 계층적인 구조를 가지기 때문에 하위 스코프는 상위 스코프에 접근할 수 있지만 반대는 불가하다.


함수자바스크립트에서 클로저 역할을 하기 때문에 스코프를 생성하므로 함수 내에 정의된 변수는 외부 함수나 다른 함수 내에서는 접근 할 수 없다. 예를 들어 다음과 같은 상황은 유효하지 않다.

function exampleFunction() {
    let x = "declared inside function";
    // x는 오직 exampleFunction 내부에서만 사용 가능.
    console.log("Inside function");
    console.log(x);
}

console.log(x);  // 에러 발생

그러나 다음과 같은 코드는 변수가 함수 외부의 전역에서 선언되었기 때문에 유효하다.

let x = "declared outside function";

exampleFunction();

function exampleFunction() {
    console.log("Inside function");
    console.log(x);
}

console.log("Outside function");
console.log(x);

스코프는 중첩이 가능

스코프는 중첩이 가능하며 안쪽 스코프에서는 바깥쪽 스코프를 사용할 수 있지만 반대로 바깥쪽에서는 안쪽 스코프를 불러올 수 없다.

가장 바깥쪽의 스코프는 특별히 전역변수로 부르며 어디서든 사용 가능하다.

  • 전역 스코프 (Global scope) 코드 어디에서든지 참조할 수 있다.
  • 지역 스코프 (Local scope or Function-level scope) 함수 코드 블록이 만든 스코프로 함수 자신과 하위 함수에서만 참조할 수 있다.
  • 전역 변수 (Global variable)전역에서 선언된 변수이며 어디에든 참조할 수 있다.
  • 지역 변수 (Local variable)지역(함수) 내에서 선언된 변수이며 그 지역과 그 지역의 하부 지역에서만 참조할 수 있다.

변수는 선언 위치(전역 또는 지역)에 의해 스코프를 가지게 된다. 즉, 전역에서 선언된 변수는 전역 스코프를 갖는 전역 변수이고, 지역(자바스크립트의 경우 함수 내부)에서 선언된 변수는 지역 스코프를 갖는 지역 변수가 된다.

전역 스코프를 갖는 전역 변수는 전역(코드 어디서든지)에서 참조할 수 있다. 지역(함수 내부)에서 선언된 지역 변수는 그 지역과 그 지역의 하부 지역에서만 참조할 수 있다.

※ 각 지역변수는 전역변수보다 더 높은 우선순위를 가진다.


스코프의 종류

스코프는 두 가지 종류가 있습니다.

하나는 블록 스코프(block scope)라고 부르며, 중괄호 { } 를 기준으로 범위가 구분됩니다.
(두번째 예제의 변수 i는 중괄호 안(for반복문)에서만 사용할 수 있습니다.)

if(true){
	console.log('here is block scope')
}
//---------------------------
for(let i = 0; i < 10; i++){
	console.log(i);
}
//---------------------------
{
	console.log('it works');
}

 

다른 스코프 종류로는 함수 스코프(function scope)가 있습니다. function 키워드가 등장하는 함수 선언식 및 함수 표현식은 함수 스코프를 만듭니다.

//함수로 둘러싼 범위

function getName(user){
	return user.name;
}

let getAge = function(user){
	return user. age;
}

 

화살표 함수는 블록 스코프로 취급됩니다. 함수 스코프가 아닙니다

//같은 함수여도 화살펴표 함수를 사용하면 블록 스코프로 취급됩니다.
let getAge = user => { 
	return user.age;
} 


let getAge =  function(user){
	return user. age;
}

 

자바스크립트의 스코프는 타 언어와는 다른 특징을 가지고 있습니다.

대부분의 C-family language는 블록 레벨 스코프(block-level scope)를 따른다. 블록 레벨 스코프란 코드 블록({…})내에서 유효한 스코프를 의미합니다. 여기서 “유효하다”라는 것은 “참조(접근)할 수 있다”라는 뜻입니다.

하지만 자바스크립트는 함수 레벨 스코프(function-level scope)를 따릅니다. 함수 레벨 스코프란 함수 코드 블록 내에서 선언된 변수는 함수 코드 블록 내에서만 유효하고 함수 외부에서는 유효하지 않다(참조할 수 없다)는 것입니다.

단, ECMAScript 6에서 도입된 let keyword를 사용하면 블록 레벨 스코프를 사용할 수 있습니다.


전역 스코프(Global scope)

전역에 변수를 선언하면 이 변수는 어디서든지 참조할 수 있는 전역 스코프를 갖는 전역 변수가 된다. var 키워드로 선언한 전역 변수는 전역 객체(Global Object) window의 프로퍼티이다.

var global = 'global';

function foo() {
  var local = 'local';
  console.log(global);
  console.log(local);
}
foo();

console.log(global);
console.log(local); // Uncaught ReferenceError: local is not defined

변수 global는 함수 영역 밖의 전역에서 선언되었다. 자바스크립트는 타 언어와는 달리 특별한 시작점(Entry point)이 없어서 위 코드와 같이 전역에 변수나 함수를 선언하기 쉽다.

비 블록 레벨 스코프(Non block-level scope)

if (true) {
  var x = 5;
}
console.log(x);

변수 x는 코드 블록 내에서 선언되었다. 하지만 자바스크립트는 블록 레벨 스코프를 사용하지 않으므로 함수 밖에서 선언된 변수는 코드 블록 내에서 선언되었다할지라도 모두 전역 스코프을 갖게된다. 따라서 변수 x는 전역 변수이다.

함수 레벨 스코프(Function-level scope)

var a = 10;     // 전역변수

(function () {
  var b = 20;   // 지역변수
})();

console.log(a); // 10
console.log(b); // "b" is not defined

함수 내에서 선언된 매개변수와 변수는 함수 외부에서는 유효하지 않다.

다음은 함수 내에 존재하는 함수인 내부 함수의 경우

var x = 'global';

function foo() {
  var x = 'local';
  console.log(x);

  function bar() {  // 내부함수
    console.log(x); // ?
  }

  bar();
}
foo();
console.log(x); // ?

내부함수는 자신을 포함하고 있는 외부함수의 변수에 접근할 수 있다. 이는 매우 유용하다. 클로저에서와 같이 내부함수가 더 오래 생존하는 경우, 타 언어와는 다른 움직임을 보인다.

함수 bar에서 참조하는 변수 x는 함수 foo에서 선언된 지역변수이다. 이는 실행 컨텍스트의 스코프 체인에 의해 참조 순위에서 전역변수 x가 뒤로 밀렸기 때문이다.

암묵적 전역

var x = 10; // 전역 변수

function foo () {
  // 선언하지 않은 식별자
  y = 20;
  console.log(x + y);
}

foo(); // 30

위 예제의 y는 선언하지 않은 식별자이다. 따라서 y = 20이 실행되면 참조 에러가 발생할 것처럼 보인다. 하지만 선언하지 않은 식별자 y는 마치 선언된 변수처럼 동작한다. 이는 선언하지 않은 식별자에 값을 할당하면 전역 객체의 프로퍼티가 되기 때문이다.

foo 함수가 호출되면 자바스크립트 엔진은 변수 y에 값을 할당하기 위해 먼저 스코프 체인을 통해 선언된 변수인지 확인한다. 이때 foo 함수의 스코프와 전역 스코프 어디에서도 변수 y의 선언을 찾을 수 없으므로 참조 에러가 발생해야 하지만 자바스크립트 엔진은 y = 20을 window.y = 20으로 해석하여 프로퍼티를 동적 생성한다. 결국 y는 전역 객체의 프로퍼티가 되어 마치 전역 변수처럼 동작한다. 이러한 현상을 암묵적 전역(implicit global)이라 한다.

하지만 y는 변수 선언없이 단지 전역 객체의 프로퍼티로 추가되었을 뿐이다. 따라서 y는 변수가 아니다. 따라서 변수가 아닌 y는 변수 호이스팅이 발생하지 않는다.

// 전역 변수 x는 호이스팅이 발생한다.
console.log(x); // undefined
// 전역 변수가 아니라 단지 전역 프로퍼티인 y는 호이스팅이 발생하지 않는다.
console.log(y); // ReferenceError: y is not defined

var x = 10; // 전역 변수

function foo () {
  // 선언하지 않은 변수
  y = 20;
  console.log(x + y);
}

foo(); // 30

※ 선언 없이 변수를 할당하지 마세요. 선언 없이 변수를 할당하면, 해당 변수는 var로 선언한 전역 변수처럼 취급됩니다.


 let, const, var 변수 선언 키워드의 차이 

  let const var
유효범위 블록 스코프 및 함수 스코프 블록 스코프 및 함수 스코프 함수 스코프
값 재할당 가능 불가능  가능
재선언 불가능 불가능 가능

let

 

let - JavaScript | MDN

let 명령문은 블록 스코프의 범위를 가지는 지역 변수를 선언하며, 선언과 동시에 임의의 값으로 초기화할 수도 있습니다.

developer.mozilla.org

let 명령문은 블록 스코프의 범위를 가지는 지역 변수를 선언하며, 선언과 동시에 임의의 값으로 초기화할 수도 있습니다.

let x = 1;

if (x === 1) {
  let x = 2;

  console.log(x);
  // expected output: 2
}

console.log(x);
// expected output: 1

let을 사용하면 블록 명령문이나 let을 사용한 표현식 내로 범위가 제한되는 변수를 선언할 수 있습니다. 이는 let이 var 키워드와 다른 점으로, var는 변수를 블록을 고려하지 않고 현재 함수 (또는 전역 스코프) 어디에서나 접근할 수 있는 변수를 선언합니다. 또한 let은 파서가 구문을 평가해야만 변수를 값으로 초기화(아래 참고)한다는 점도 var와 다릅니다.

const와 마찬가지로 let 역시 전역 범위 선언에 사용(최상위 스코프 선언)해도 window 객체에 새로운 속성을 추가하지 않습니다.

let으로 선언한 변수는 자신을 선언한 블록과 모든 하위 블록을 스스로의 스코프로 가집니다. 이런 점에서는 let var와 유사합니다. 그러나 둘의 중요한 차이는, var의 경우 스코프가 '자신을 선언한 블록'이 아니라, 자신의 선언을 포함하는 함수라는 점입니다.

function varTest() {
  var x = 1;
  if (true) {
    var x = 2; // 같은 변수!
    console.log(x); // 2
  }
  console.log(x); // 2
}

function letTest() {
  let x = 1;
  if (true) {
    let x = 2; // 다른 변수
    console.log(x); // 2
  }
  console.log(x); // 1
}

프로그램 최상위에서 사용할 경우 var는 전역 객체에 속성을 추가하지만 let은 추가하지 않습니다.

var x = 'global';
let y = 'global';
console.log(this.x); // "global"
console.log(this.y); // undefined

const

 

const - JavaScript | MDN

const 선언은 블록 범위의 상수를 선언합니다. 상수의 값은 재할당할 수 없으며 다시 선언할 수도 없습니다.

developer.mozilla.org

const 선언은 블록 범위의 상수를 선언합니다. 상수의 값은 재할당할 수 없으며 다시 선언할 수도 없습니다.

const number = 42;

try {
  number = 99;
} catch (err) {
  console.log(err);
  // expected output: TypeError: invalid assignment to const `number'
  // (Note: the exact output may be browser-dependent)
}

console.log(number);
// expected output: 42

선언된 함수에 전역 또는 지역일 수 있는 상수를 만듭니다. 상수 초기자(initializer)가 필요합니다. 즉 선언되는 같은 문에 그 값을 지정해야 합니다(이는 나중에 변경될 수 없는 점을 감안하면 말이 됩니다).

상수는 let 문을 사용하여 정의된 변수와 마찬가지로 블록 범위(block-scope)입니다. 상수의 값은 재할당을 통해 바뀔 수 없고 재선언될 수 없습니다.

 


var
(※ 사용을 지양합니다. let, const 쓰기!)

 

var - JavaScript | MDN

**var**문은 변수를 선언하고, 선택적으로 초기화할 수 있습니다.

developer.mozilla.org

어디에 선언이 되어있든 간에 변수들은 어떠한 코드가 실행되기 전에 처리가 됩니다. var로 선언된 변수의 범위는 현재 실행 문맥인데, 그 문맥은 둘러싼 함수, 혹은 함수의 외부에 전역으로 선언된 변수도 될 수 있습니다.

선언된 변수들의 값 할당은 할당이 실행될 때 전역변수(이것은 전역 오브젝트의 프로퍼티가 됩니다)처럼 생성이 됩니다. 선언된 변수들과 선언되지 않은 변수들의 차이점은 다음과 같습니다:

  • 선언된 변수들은 변수가 선언된 실행 콘텍스트(execution context) 안에서 만들어집니다. 선언되지 않은 변수들은 항상 전역변수 입니다.
  • 선언된 변수들은 어떠한 코드가 실행되기 전에 만들어집니다. 선언되지 않은 변수들은 변수들을 할당하는 코드가 실행되기 전까지는 존재하지 않습니다.
  • 선언된 변수들은 변수들의 실행 콘텍스트(execution context)의 프로퍼티를 변경되지 않습니다. 선언되지 않은 변수들은 변경 가능합니다. (e.g 삭제 될 수도 있습니다.)

var 호이스팅(hoisting)

변수 선언들 (그리고 일반적인 선언)은 어느 코드가 실행 되기 전에 처리하기 때문에, 코드 안에서 어디서든 변수 선언은 최상위에 선언한 것과 동등합니다. 이것은 변수가 선언되기 전에 사용 될 수 있다는 것을 의미합니다. 변수 선언이 함수 또는 전역 코드의 상단에 이동하는 것과 같은 행동을 "호이스팅(hoisting)"이라고 불립니다.

 

 

참조 : mdn, poiemaweb, codestates

Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.