개발 알다가도 모르겠네요

전역 변수의 문제점 본문

학습일지/모던 자바스크립트 Deep Dive

전역 변수의 문제점

이재빵 2022. 8. 6. 11:02
728x90

변수도 생명 주기(life cycle) 가 있음.

  • 변수는 선언에 의해 생성 → 할당을 통해 값을 갖음 → 언젠가는 소멸
  • 변수에 생명주기가 없다면 한번 선언된 변수는 프로그램을 종료하지 않는 한 영원히 메모리 공간을 점유하게 된다.

 

지역변수 생명 주기

지역 변수의 생명 주기는 함수의 생명 주기와 일치

  • 즉, 함수가 호출되어 실행되는 동안에만 유효.
function foo() {
  var x = "local";
  console.log(x); // local
  return x;
}

foo();
console.log(x); // ReferenceError: x is not defined  << 🔎

 

지역 변수 호이스팅 스코프

지역 변수의 호이스팅은 지역 변수의 선언이 지역 스코프의 선두로 끌어 올려진 것처럼 동작

  • 즉, 지역 변수는 함수 전체에서 유효.
var x = "global";

function foo() {
  console.log(x); // undefined << 🔎
  var x = "local";
}

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

/*
foo 함수 내부에 선언된 x 변수는, foo 함수 호출 시 이미 undefined로 초기화 되어 있음 (변수 선언과정이 이미 끝남, 값 할당과정은 X)
따라서, console 출력문을 만나는 시점에는 아직 x에 값이 할당되기 이전이기 때문에 undefined 를 출력
중요한 것은 최상단에 전역변수 x까지는 스코프가 유효하지 않다는 것, 즉 함수 내부에서 선언된 지역변수는 함수 내에서만 스코프를 가짐
*/



전역 변수 생명 주기

var 키워드 로 선언한 전역 변수의 생명 주기는 전역 객체의 생명 주기와 일치

전역 객체 (global object)

코드가 실행되기 이전 단계에(즉, 런타임 이전) 자바스크립트 엔진에 의해 어떤 객체보다도 먼저 생성되는 특수한 객체
브라우저에서는 "window", Node.js 에서는 "global 객체"를 의미
ES11에서 "globalThis" 로 전역 객체를 가리키는 식별자를 통일
표준 빌트인 객체(Object, String, Number, Function, Array ...)와 환경에 따른 호스트 객체(브라우저의 Web API 또는 Node.js의 호스트 API), 
var 키워드로 선언한 전역 변수와 전역 함수를 프로퍼티로 가짐.
  • 전역 코드는 명시적인 호출 없이 실행.
    • 전역 코드는 함수 호출과 같이 전역 코드를 실행하는 특별한 진입점이 없음.
    • 로드되자마자 곧바로 해석(= 평가)되고 실행된다.
    • 함수는 함수 몸체의 마지막 문 or 반환문이 실행되면 종료하지만, 전역 코드는 반환문이 없으므로 마지막 문이 실행되어 더 이상 실행할 코드가 없을 때 비로소 종료.
    • var 키워드 로 선언한 전역 변수는 전역 객체의 프로퍼티 가 됨.

 

전역변수 문제점

  • 암묵적 결합(implicit coupling)
    • 코드 내 어디서든 참조하고 할당할 수 있는 변수를 사용하겠다는 것
    • 변수의 유효범위가 크면 클수록 코드의 가독성은 나빠지고, 의도치 않게 상태가 변경될 수 있는 위험도를 증가시킴.
  • 긴 생명주기
    • 전역 변수는 프로그램이 종료하기 전까지 메모리 공간에 할당되어 해제되지 않음.
    • 즉, 메모리 리소스를 오랜 기간 소비.
    • var 키워드로 선언한 변수 경우, 변수의 중복 선언을 허용한다는 것을 의미 → 변수 이름이 중복될 가능성이 높고 이는 의도치 않은 재할당이 이뤄질 가능성을 유발
  • 스코프 체인 상에서 종점에 존재
    • 변수를 검색할 때 전역 변수가 가장 마지막에 검색(= 식별자 해결)된다는 것을 의미
    • 즉, 전역 변수의 검색 속도가 가장 느림.
  • 네임스페이스 오염
    • 자바스크립트의 가장 큰 문제점 중 하나는 파일이 분리되어 있다 해도 하나의 전역 스코프를 공유.
    • 즉, 다른 파일 내에서 동일한 이름의 전역 변수나 전역 함수는 같은 스코프 내에 존재할 경우 예상치 못한 결과를 초래.



전역 변수의 사용을 억제하는 방법

전역 변수를 반드시 사용해야 할 이유를 찾지 못한다면 지역 변수를 사용해야 한다. > 즉, 변수의 스코프는 좁을수록 좋다.

 

1. 즉시 실행 함수 사용

  • 즉시 실행 함수가 호출이 종료되는 순간 내부에 있던 모든 변수를 소멸. ( 일회성 )
  • 모든 코드를 즉시 실행 함수 로 감싸면 모든 변수는 즉시 실행 함수의 지역 변수가 됨.
(function () {
  var foo = 100; // 즉시 실행 함수의 지역 변수
  // ...
})();

console.log(foo); // ReferenceError: foo is not defined

 

2.네임스페이스 객체 사용

  • 네임스페이스는 객체
// 전역 네임스페이스 객체
var MYAPP = {};

// 네임스페이스 내부에 또 다른 네임스페이스 계층적 구조 생성
MYAPP.person = {
  name: "WI",
  age: 100,
};

console.log(MYAPP.person.name); // 'WI'

 

3.모듈 패턴 사용

  • 클래스(class) 를 모방해서 관련이 있는 변수와 함수를 모아 즉시 실행 함수로 감싸 하나의 모듈을 만드는 방법
  • 자바스크립트의 강력한 기능인 클로져(closure) 를 기반으로 동작
  • 전역 변수의 억제는 물론 캡슐화(encapsulation) 까지 구현할 수 있다는 장점 → 정보 은닉(information hiding) 가능
/*
Counter 변수에는 즉시 실행함수로 생성된 클래스 느낌의 객체가 값으로 할당되어 있다.
반환하는 public 한 성질의 메서드들은 외부에서 참조 가능
반환하지 않고 즉시 실행 함수 내부에서 선언되어 있는 변수는 private 한 성질을 가져, 외부에서 참조 불가능
*/
var Counter = (function () {
  // private 변수
  var num = 0;

  // 외부로 공개할 데이터나 메서드 프로퍼티를 추가한 객체를 반환
  return {
    increase() {
      return ++num;
    },
    decrease() {
      return --num;
    },
  };
})();

console.log(Counter.num); // undefined << 🔎 ( private 변수는 외부로 노출되지 X )
console.log(Counter.increase()); // 1
console.log(Counter.increase()); // 2
console.log(Counter.decrease()); // 1
console.log(Counter.decrease()); // 0

 

 

4.ES6 모듈

  • ES6 모듈은 파일 자체의 독자적인 모듈 스코프를 제공
  • 모듈 내에서 var 키워드로 선언한 변수 는 더는 전역 변수가 아니며 window 객체 의 프로퍼티도 아니게 됨.
  • 모던 브라우저 에서는 ES6 모듈을 사용할 수 있음.
ES6 모듈

IE를 포함한 구형 브라우저에서는 동작하지 않는다.
트랜스파일링이나 번들링이 필요하기 때문에, 아직까지는 Webpack 등의 모듈 번들러를 일반적으로 사용

'학습일지 > 모던 자바스크립트 Deep Dive' 카테고리의 다른 글

생성자 함수  (0) 2022.08.09
let & const  (0) 2022.08.06
스코프  (0) 2022.07.07
실행 컨텍스트  (0) 2022.07.06
함수  (0) 2022.07.05