자바스크립트 스코프란?

박재현·2024년 3월 8일
1

JavaScript 공부

목록 보기
12/14

1. 스코프란?

스코프는 자바슼릡트를 포함한 모든 프로그래밍 언어의 기본적인 개념으로 확실한 이해가 필요하다.

먼저 아래 실행 결과를 예상해보자.

//JS
var x = 'global';

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

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

기본적으로 변수는 전역 또는 코드블럭(if, for, white, try/catch 등등)이나 함수 내에 선언하며 코드 블럭이나 함수는 중첩될 수 있다.

위 예제에서 전역으로 선언된 x는 어디에서나 참조할 수 있지만, foo함수 내에서 선언된 변수 x는 함수 내부에서만 참조할 수 있다.

이런 규칙을 스코프라고 한다.


2. 스코프의 구분

자바스크립트에서 스코프를 구분해보면 2가지로 나눌 수 있다.

  • 전역 스코프 (Global Scope)
    코드 어디에서든지 참조 가능

  • 지역 스코프 (Local scope or Function-level scope)
    함수 코드 블록이 만든 스코프로 함수 자신과 하위 함수에서만 참조 가능

또 모든 변수는 스코프를 갖는데, 변수의 관점에서 스코프를 구분하면 2가지로 나눌 수 있다.

  • 전역 변수 (Global variable)
    전역에서 선언된 변수로 어디에서든 참조 가능

  • 지역 변수 (Local variable)
    지역(함수) 내에서 선언된 변수로, 그 지역과 하부 지역에서만 참조 가능

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

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


3. 자바스크립트 스코프의 특징

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

대부분의 C-family language는 블록 레벨 스코프(block-level scope) 를 따른다.
블록 레벨 스코프란 코드블록 {...} 내에서만 참조가능한 스코프를 의미한다.

// C

int main(void) {
  // block-level scope
  if (1) {
    int x = 5;
    printf("x = %d\n", x);
  }

  printf("x = %d\n", x); // use of undeclared identifier 'x'

  return 0;
}

위의 C언어 코드를 보면, if내에서 선언된 변수 x는 if문 코드 블럭 내에서만 참조가 가능하다.

하!지!만! 자바스크립트는 함수 레벨 스코프(function-level scope)를 따른다.

함수 레벨 스코프란 함수 코드 블록 내에서 선언된 변수는 함수 코드 블록 내에서만 유효하고, 함수 외부에서는 유호하지 않다는 것이다.

단! ES6부터 도입된 let 키워드를 사용하면 블록 레벨 스코프를 사용할 수 있다. (C언어 처럼 사용 가능하다)

// JS
var x = 0;
{
  var x = 1;
  console.log(x); // 1
}
console.log(x);   // 1

let y = 0;
{
  let y = 1;
  console.log(y); // 1
}
console.log(y);   // 0

4. 전역 스코프

전역에 변수를 선언하면, 이 변수는 어디서든지 참조할 수 있는 전역 스코프를 갖는 전역변수가 된다.

var 키워드로 선언한 전역 변수는 전역객체 window의 프로퍼티다.

// JS

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)이 없어서 위 코드처럼 전역에 변수나 함수를 선언하기 쉽다.

C언어의 경우 main 함수가 시작점이 되기 때문에, 대부분의 코드는 main함수 내에 포함되고, 전역변수를 선언하기 위해 의도적으로 main 함수 밖에 변수를 선언해야 한다.

// C

#include <stdio.h>

/* global variable declaration */
int g;

int main () {

  // local variable declaration
  int a, b;

  // actual initialization
  a = 10;
  b = 20;
  g = a + b;

  printf ("value of a = %d, b = %d and g = %d\n", a, b, g);

  return 0;
}

5. 비 블록 레벨 스코프

// JS

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

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

따라서 변수 x는 전역 변수이다.

여기서 만약 변수를 var가 아닌 let으로 선언하면 C언어처럼 블록 레벨 스코프가 되기 때문에, 코드블록 밖에서 사용이 불가능하다!


6. 함수 레벨 스코프

var a = 10;     // 전역변수

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

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

자바스크립트는 함수레벨 스코프를 사용한다.
따라서 변수 b는 var로 선언했지만, 함수 내부에서 선언했기 때문에 함수밖에서는 사용할 수 없는 지역변수이다.

var x = 'global';

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

foo();          // local
console.log(x); // global

전역 영역 에서는 전역 변수만 참조가 가능하나, 지녁 영역에서는(함수 내부) 전역변수와 지역변수 모두 참조가 가능하나, 변수명이 중복될 경우 지역변수를 우선으로 참조한다.

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

var x = 'global';

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

  function bar() {  
    console.log(x); // 2
  }

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

위 코드를 실행시키면 어떻게 될까?

foo 함수 내부에서 선언한 변수 x는, 해당 함수 내부에서 사용이 가능하다.

즉 foo 함수 내부에 선언된 bar함수는 본인의 기준에서는 외부이지만, foo함수 내부이 있기 때문에 foo함수 내부에서 선언된 변수 x를 참조할 수 있게 된다. (이것이용해서 클로저를 사용하는듯?)

따라서 해당 코드를 실행시켜보면 결과는 local - local - global이 나오게 된다.

var x = 10;

function foo() {
  x = 100;
  console.log(x);
}
foo();
console.log(x);

이 코드도 한번 살펴보자.

먼저 함수 밖에서 전역변수 x가 10으로 선언되었다.
그리고 foo 함수가 실행되면, 전역변수 x를 100으로 바꾸고 100이 찍힐거고, 그 다음 바뀐 100이 한번 더 나올거다.

여기서 중요한건, 내부 함수의 경우 전역변수는 물론이거니와 상위 함수에서 선언한 변수에 접근도 가능하고 변경도 가능하다는점 이다.

var x = 10;

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

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

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

foo함수에서 변수 x를 새로 선언했기에, 전역변수 x대신 해당 지역변수를 사용할것으로 예상된다.

따라서 100 - 1000 - 10이 차례대로 찍힐것 같은데 한번 실행시켜보자.

역시~? 중첩 스코프는 가장 인접한 지역을 우선으로 참조한다!

var foo = function ( ) {

  var a = 3, b = 5;

  var bar = function ( ) {
    var b = 7, c = 11;

// 이 시점에서 a는 3, b는 7, c는 11

    a += b + c;

// 이 시점에서 a는 21, b는 7, c는 11

  };

// 이 시점에서 a는 3, b는 5, c는 not defined

  bar( );

// 이 시점에서 a는 21, b는 5

};

함수 괄호를 잘 구분해보자.


7. 렉시컬 스코프

var x = 1;

function foo() {
  var x = 10;
  bar();
}

function bar() {
  console.log(x);
}

foo(); // 1
bar(); // 1

자바스크립튼느 렉시컬 스코프를 따르는데, 렉시컬 스코프를 다른 말로는 정적 스코프(Static scope)라고 한다.

이는 함수를 어디서 선언하였는지에 따라 상위 스코프를 결정하는 것이다.

따라서 bar함수는 foo함수 밖에서 선언했기 때문에(즉 전역에 선언), bar함수에서 참조할 수 있는 x는 전역변수인 x = 1이 되겠다.

다시한번 강조하지만, 렉시컬 스코프는 함수를 어디서 호출하는지가 아닌, 어디에 선언하였는지에 따라 결정된다!!

따라서 함수를 어디서 호출하였는지는 스코프 결정에 아무런 의미룰 주지 않는다.


8. 암묵적 전역

var x = 10; // 전역 변수

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

foo(); // 30

위 예제의 y는 선언하지 않은 식별자다.

따라서 y = 20;이 실행되면 참조 에러가 발생할것 처럼 보이나, 선언하지 않은 식별자에 값을 할당하면 전역 객체의 프로퍼티가 된다. (타입스크립트에서 이러면 빨간줄이 그어질거 같기도..?)

foo 함수가 호출되면 자바스크립트 엔진은 변수 y에 값을 할당하기 위해 선언된 변수인지 확인하지만, 선언된 변수 y를 찾을 수 없기 때문에 window.y = 20으로 해석해 프로퍼티를 동적으로 생성한다.

결국 y는 전역 객체의 프로퍼티가 되어 마치 전역 변수처럼 동작하는데, 이를 암묵적 전역(implicit global)이라고 한다.

// 전역 변수 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

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

var x = 10; // 전역 변수

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

foo(); // 30

console.log(window.x); // 10
console.log(window.y); // 20

delete x; // 전역 변수는 삭제되지 않는다.
delete y; // 프로퍼티는 삭제된다.

console.log(window.x); // 10
console.log(window.y); // undefined

또 y는 변수가 아닌 전역 객체의 프로퍼티이기 때문에 delete 션산자로 삭제도 가능하다.
(전역 변수는 불가능 함)


참고

profile
기술만 좋은 S급이 아니라, 태도가 좋은 A급이 되자

0개의 댓글

관련 채용 정보