JS | 스코프, 전역변수와 지역변수

JOY·2024년 9월 3일
0

JS

목록 보기
8/18
post-thumbnail

✅ scope

식별자가 유효한 범위
식별자의 선언 위치에 따라 다른 코드가 식별자를 참조할 수 있는 유효 범위가 결정됨
js 엔진이 식별자를 검색할 때 사용하는 규칙

✅ 식별자 결정

identifier resolution: js 엔진이 이름이 같은 두 식별자 중 어느 식별자를 참조해야할지 결정하는 것
코드의 문맥context를 고려_코드가 어디서 실행되고 주변에 어떤 코드가 있는지에 따라 동일한 코드도 다른 결과를 만듦

var x = "global";

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

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

스코프 개념이 없으면 위 코드에서 x 변수가 전역, foo 함수 내에 모두 존재해 충돌할 것임

✅ 스코프 종류

변수는 자신이 선언된 위치에 따라 자신의 스코프가 결정됨 (렉시컬 스코프)
전역에 선언된 변수는 전역 스코프_전역변수 -> 어디서든지 참조 가능
지역에 선언된 변수는 지역 스코프_지역 변수 -> 자신의 지역 스코프, 하위 지역 스코프에서 참조 가능 (함수, 코드블록(let,const))

✅ 스코프 체인

스코프는 함수의 중첩 의해 계층적 구조를 가짐

outer 함수가 만든 지역 스코프는 inner 함수가 만든 지역 스코프의 상위 스코프, outer 함수의 지역 스코프의 상위 스코프는 전역 스코프
-> 모든 지역 스코프의 최상위 스코프는 전역 스코프

변수 참조 시, js 엔진은 변수를 참조하는 코드의 스코프에서 시작스코프 체인을 통해 상위 스코프 방향으로 이동하며 선언된 변수를 검색함(identifier resolution)

스코프 체인은 실행 컨텍스트의 렉시컬 환경을 단방향으로 연결한chaining 것임

▫ 스코프 체인에 의한 함수 검색

// 전역 함수 
function foo() {
  console.log("global function foo");
}

function bar() {
  // 중첩 함수
  function foo() {
    console.log("local function foo");
  }

  foo(); // local function foo
}

bar();

✅ 블록 레벨 스코프 vs 함수 레벨 스코프

블록 레벨 스코프 : 모든 코드 블록(함수, if, for, while, try/catch ...) 내에서 선언된 변수는 지역변수로 코드 블록 내에서만 유효함 _const, let
함수 레벨 스코프 : 함수 내에 선언된 변수는 지역변수로 함수 내에서만 유효함, 함수가 아닌 곳에서 선언된 var은 전역변수가 됨

✅ 렉시컬 스코프

스코프를 결정하는 방식

  • 동적 스코프: 어디서 호출했는지에 따라 스코프 결정
    함수가 호출되는 시점에 동적으로 스코프 결정
  • 정적 스코프(렉시컬 스코프): 어디서 정의했는지에 따라 스코프 결정
    동적 스코프처럼 스코프가 동적으로 변하지 않고, 함수 정의가 평가될때 정적으로 결정됨
    js를 비롯한 대부분의 프로그래밍 언어가 렉시컬 스코프를 따름
    -> 함수의 스코프는 언제나 자신이 정의된 스코프
    함수 객체는 내부적으로 [[Environment]] 내부 슬롯에 자신의 스코프 정보를 저장해(정의된 환경을 기억)하여 함수가 호출될 때 참조할 수 있게 함 _클로저의 원리

변수는 선언에 의해 생성 -> 할당을 통해 값을 가짐 -> 언젠가는 소멸

변수는 자신이 등록된 스코프가 소멸(메모리 해제)될 때까지 유효함
누군가 스코프를 참조하고 있으면 스코프는 소멸하지 않고 생존하게 됨

변수에 생명 주기가 없다면 한 번 선언된 변수는 프로그램을 종료하지 않는 한 영원히 메모리 공간을 점유함

✅ 지역변수 생명 주기

지역 변수의 생명 주기는 함수의 생명주기와 일치해
함수가 호출돼 실행되는 동안에 유효함 === 지역변수는 지역 스코프를 가짐

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(); // undefined 
console.log(x); // "global"    전역변수 x까지는 지역변수의 스코프가 유효하지 않음 

✅ 전역 변수 생명 주기

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

전역 코드는 명시적 호출 없이 로드되자마자 실행되고, 더 이상 실행할 코드가 없으면 종료됨

✅ 전역 변수의 문제점

  • 암묵적 결합: 모든 코드가 전역 변수 참조하고 변경 가능
  • 긴 생명 주기: 메모리 리소스를 오랜 기간 소비
  • 스코프 체인 상의 종점에 존재: 전역 변수의 검색 속도가 가장 느림
  • 네임 스페이스 오염: js는 하나의 전역 스코프를 공유해 같은 이름의 전역변수,전역함수로 예상치 못한 결과 초래

✅ 전역 변수 사용 억제 방법

  1. 즉시 실행 함수로 감싸기
    모든 코드를 즉시실행함수로 감싸 모든 변수를 즉시실행함수의 지역변수로 만듦
    즉시실행함수 호출이 종료되면 내부에 있던 모든 변수가 소멸됨(일회성)
(function () {
  var foo = 100; // 즉시 실행 함수의 지역 변수
  // ...
})();
console.log(foo); // ReferenceError: foo is not defined
  1. 모듈 패턴 사용
    즉시실행함수로 변수와 함수를 감싸 하나의 모듈로 캡슐화함
/**
* Counter 변수에는 즉시 실행함수로 생성된 클래스 느낌의 객체가 값으로 할당되어 있음
* 즉시 실행 함수 내부에서 선언되어 있는 변수는 private 한 성질을 가져, 외부에서 참조 불가능
* 반환하는 public 한 성질의 메서드로 외부에서 특정 기능에만 접근할 수 있게 함 
*/
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
  1. 모듈 (ES6)
    파일 자체의 독자적인 모듈 스코프를 제공
    모듈 내에서 var로 선언한 변수는 전역변수, window객체의 프로퍼티가 아님

참고 자료

profile
모든 일에 진심을 담아서

0개의 댓글