파크로그
article thumbnail

전역 변수

변수는 자신이 선언된 함수가 호출될 때 생성되는 함수 렉시컬 환경의 생명주기를 따라 소멸됩니다.

가비지 컬렉터(GC) 에 의해서 소멸되는 것인데요, 함수가 호출될 때 생성되는 함수 렉시컬 환경이 어느 곳이든지 참조를 하고 있지 않다면 GC 에 의해서 소멸됩니다.

 

그렇다면 전역 변수는 어떤 생명주기를 따르게 될까요?

var 키워드로 선언한 전역 변수는  전역 객체의 프로퍼티 가 되는데요, 그렇다면 전역 변수의 생명주기는 전역 객체의 생명주기와 일치할 것이고, 전역 객체의 생명주기는 코드를 실행하는 환경이 종료되야 종료되므로, 결국 끝까지 GC 에 의해 소멸되지 않게됩니다. 즉, 메모리 누수로 이어지게 됩니다.

 

전역 객체

  • 브라우저 환경의 모든 자바스크립트 코드는 하나의 전역객체 window 를 공유합니다.
  • 즉 여러 개의 script 태그로 분리해도, 다양한 모듈을 사용해도 하나의 전역객체 window 를 공유하는 것 입니다.
<script>
    var a = "전역변수";
</script>
<script>
    var b = "두 번째 전역변수";
    console.log(a); // 여기서 a 가 검색이 됩니다.
</script>

  • 전역변수를 방지하는 방법으로 함수 안에 변수를 선언하라 는 이야기도 어디선가~ 들어본 기억이있는데, 함수선언문 으로 함수를 선언하면, 함수 이름과 동일한 식별자 로 전역 객체안에 함수가 저장되어, 함수를 선언할 때에도 이를 고려해야 겠다는 생각이 들었습니다.
<script>
/*
  이 스크립트의 함수 선언문은 아래 스크립트의 함수처럼 
  var 키워드인 a 식별자에 함수이름이 a 인 
  함수리터럴로 선언하는 것과 동일하게 동작합니다.
*/
    function a() {
        var aInner = "변수";
    }; 
</script>
<script>
    var a = function a() {
      var aInner = "변수";
    };
</script>
  • 즉 함수선언문은 식별자 a 를 전역객체의 프로퍼티로 넣는것과 동일하기 때문에 전역변수를 쓰는 것과 같다는 느낌이 있습니다.

그렇다면, 전역객체에 프로퍼티를 넣지 않으면서, 코드를 실행하고 싶으면 어떻게 해야할까요.

함수를 선언하고 사용할때, 즉시 실행을 시킨다면 어느 식별자로도 함수를 저장하지않기 때문에 즉시실행함수를 활용해볼 수 있을 것 같습니다.

<script>
    (function a() {
      var aInner = "변수";
      console.log(aInner);
    })();
</script>

  • 전역 객체를 보면 a 함수를 어느 식별자로도 저장하지 않는 것을 확인할 수 있었습니다.
  • 이렇게 변수와 함수들을 모아 즉시실행함수로 감싸 하나의 모듈로 만들어 사용한다는 의미에서 모듈패턴 이라고도 합니다. 전역 변수를 억제할 수도 있고, 캡슐화 까지 구현할 수 있는 패턴입니다.
  • 즉시실행함수는 내부에서 함수, 변수들이 즉시실행함수의 스코프, 즉 독자적인 스코프를 갖게될텐데요, 이제 사용할 ES6 모듈이 이와 비슷하게 독자적인 모듈 스코프 를 제공합니다.
  • 따라서 모듈 안에서 var const let 어떤 키워드를 사용하던지 변수는 전역객체의 프로퍼티인 전역변수가 아니게 됩니다.
    • 브라우저에서 사용하는 <script> 태그에 type 을 module 로 준다면 모듈로 동작하게 됩니다. (ES6 모듈)
    • 즉 전역변수로 저장되지 않게 됩니다.
<script type="module">
    var a = "전역변수?";
    console.log(a);
</script>

전역객체에 a 식별자가 보이지 않습니다.

<script type="module">
    var a = "전역변수";
</script>
<script type="module">
    var b = "두 번째 전역변수";
    console.log(a); // 여기서 a 가 검색이 안되겠죠?
</script>

  • 지금까지 테스트는 브라우저 환경에서 script 태그를 활용해서 테스트를 해봤습니다.
  • node 환경에서는 각 js 파일들을 모듈화 하여 require 를 통해 모듈을 import 하고, module.exports 를 통해 export 하는데 각각의 파일이 type=”module” 을 쓴 것처럼 동작하는 것이라고 예측이 됩니다.

 

전역변수를 쓰면 안되는 이유?

그렇다면 변수가 전역 객체에 포함되지 않게만 사용하면 상관이 없는걸까요?

전역변수(또는 스코프가 넓은 변수) 의 문제점은 메모리 누수 뿐 만이 아니라 다양한 문제점 이 존재합니다.

 

1. 암묵적 결합

  • 코드 어디서든 참조할 수 있고, 만약 var 이나 let 키워드로 선언된 변수라면 재할당이 될 가능성도 존재합니다.
  • 이는 모든 코드가 변수를 참조하고 변경할 수 있는 암묵적 결합을 허용하게 되는 것이므로, 변수의 유효범위가 커지면 예측하기 어려운 상황이 벌어지게 되므로 변동가능성을 줄이는게 좋겠습니다.

2. 긴 생명주기

  • 변수가 선언되고, 최대한 빨리 소멸을 시키는 것이 메모리 리소스 측면에서 효율적입니다.
  • 전역 객체가 아니더라도 모듈이 살아있는 한 계속 사용되는 변수가 있다면 메모리 리소스 측면에서 비효율적이게 됩니다.
  • 따라서 변수의 스코프를 최대한 좁게 만드는 것이 좋겠다는 생각이 듭니다.

3. 스코프 체인 상에서 종점에 존재

  • 특정 스코프에서 변수를 사용하고자 검색을 한다면, 해당 스코프에 없다면 스코프 체인을 따라 변수를 찾게 됩니다.
  • 가장 상위스코프에 존재하는 변수라면, 변수를 검색할 때 가장 마지막에 검색되므로 검색 속도가 가장 느리게 됩니다.
  • 검색 속도의 차이가 그렇게 크진 않지만 속도차이는 존재하므로 지양 하는 편이 좋겠다는 생각이 듭니다.

 

그래서 앞으로?

  • 변수가 쓰여지는 스코프를 최대한 좁게 만들어야겠다는 생각이 들었습니다. constants 한 값이더라도, 무조건 상위에 선언하기보다는 해당 변수가 어느 스코프에서만 유효해야 하는 지를 고민해봐야겠네요.
  • 또한 변동 가능성이 있는 let 같은 키워드를 최대한 지양하고, const 로 선언 한 이후 필요하다면 let 으로 바꾸는 식의 코딩습관도 중요할 것 같다는 생각이 들었습니다.
  • 블록 레벨 스코프를 갖는 let, const 키워드와는 달리 함수 레벨 스코프를 갖는 var 키워드는 당연히 쓰지 않습니다 !

 

++) 추가로 알아본 것

  <script>
    const a = "전역변수?";
    let b = "?전역변수";
    var c = "전역변수!";
  </script>
  • const 나 let 키워드로 선언한 변수는 브라우저의 globalThis (window) 의 프로퍼티로 저장되지 않는 것도 볼 수 있었습니다.
    • 다만 디버거로 확인해보면 Script Scope 에 존재하게 됩니다.

const, let 으로 선언한 a, b 키워드는 전역객체에 없고, var 으로 선언한 c 는 전역객체의 프로퍼티로 존재하네요.
const, let 으로 선언한 a, b 키워드는 Scope 가 Script 이고, var 으로 선언한 c 키워드는 Scope 가 Global 이네요

  • 엄밀히 따지자면 JavaScript 는 Script Scope 를 갖는게 아니라고 합니다.
  • let const 는 ES6 에 새로 추가된 키워드죠, V8 엔진은 ES6 에 새로 추가된 키워드인 let, const, class 를 만나면 새로운 스타일의 렉시컬 글로벌 스코프를 만듭니다. 새로운 스타일일 뿐 Global 과 다른 스코프는 아니라고 합니다.
    • 독자적으로 Script 마다 스코프를 갖는다면, 아래 코드는 실행되면 안될겁니다.
<script>
  const a = "전역변수?";
  let b = "?전역변수";
  var c = "전역변수!";
</script>
<script>
  console.log(a);
  console.log(b);
  console.log(c);
</script>

  • 실행이 되는 걸 보니, const let 이라고 하더라도 전역변수로 선언되었다고 봐도 될 것같습니다. 어디까지나 모듈로 사용되지 않을 때 이야기지만요!

 

참고

profile

파크로그

@파크park

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!