암묵적 전역

MyeonghoonNam·2024년 10월 24일

최근 자바스크립트에 대해 보다 기본기를 갖추고 싶어 틈틈이 딥다이브 복습을 진행하고 있다.

내가 작성했던 코드 중 ESLint 설정 없이 for문을 작성했을 때 변수 선언문에서 i = 0과 같이 변수 선언 키워드를 제외하는 실수를 했던적이 있다.

for (i = 0; ; ) {
  // ...
}

이 경우 ESLint 설정을 통해 바로 문제를 해결할 수 있었지만 만약 운좋게 ESLint를 바로 설정하지 않고 이 코드가 여러 코드에 묻혀 버렸다면 무슨 부작용이 발생할 수 있었을까? 에 대한 의문이 생겼다.

이 의문은 결국 자바스크립트 엔진이 선언 키워드 없이 선언한 식별자들에 대해 값을 할당하는 경우 어떻게 동작하는가?라는 궁금증으로 이어졌다.

function foo() {
  x = 77;
}

foo();

console.log(x); // ??

위 코드를 실행하여 결론부터 살펴보자면 아래와 같이 자바스크립트 엔진은 동작한다.

  1. foo 함수의 x 식별자에 대해 참조하기 위해 스코프 체인을 탐색한다.
  2. foo 함수의 지역 스코프에서 x 식별자에 대한 정보를 찾을 수 없어 상위 스코프인 전역 스코프 까지 탐색하지만 여전히 x 식별자에 대한 정보를 찾을 수 없다.
  3. 어떠한 스코프에서도 x 식별자에 대한 정보를 찾을 수 없어 동적으로 전역 객체에 새로운 프로퍼티를 동적 생성하는데 이 때 생성된 프로퍼티 키/값으로 {x: 77}을 가진다.
  4. 최종적으로 console.log(x)를 통해 77이라는 값이 참조된다.

처음에는 4번의 과정에서 나는 참조 에러(ReferenceError)가 발생할 줄 알았지만 아니였다. 이렇게 에러가 발생하지 않고 4번처럼 동작하는 과정을 암묵적 전역이라고 한다.

다른 예시를 통해 브라우저와 node.js에서의 암묵적 전역의 동작 방법이 궁금해졌다.

// 브라우저
var a = "welcome";
let b = "hello";
c = "world";
const d = "!";

console.log(a); // welcome
console.log(b); // hello
console.log(c); // world
console.log(d); // !

console.log(window.a); // welcome
console.log(window.b); // undefined
console.log(window.c); // world
console.log(window.d); // undefined
// Node.js
var a = "welcome";
let b = "hello";
c = "world";
const d = "!";

console.log(a); // welcome
console.log(b); // hello
console.log(c); // world
console.log(d); // !

console.log(global.a); // undefined => diff
console.log(global.b); // undefined
console.log(global.c); // world
console.log(global.d); // undefined

브라우저에서는 식별자 선언 키워드 없이 선언하고 할당된 식별자는 var 키워드로 선언한 것처럼 동작한다.

전역 객체 window에 프로퍼티가 등록되는 것을 알 수 있다. 반면에 let, const는 전역 객체 window에 프로퍼티가 등록되지 않았기에 undefined가 호출됨을 알 수 있다.

Node.js의 경우 var 키워드로 선언한 식별자는 let, const와 마찬가지로 전역 객체 global에 프로퍼티로 동적 생성되지 않고 선언 키워드 없이 선언한 식별자에 대해서만 동적으로 프로퍼티가 생성된 것을 살펴볼 수 있다.

이처럼 암묵적 전역이 브라우저와 node.js에서 다르게 동작한다는 점에 주의하자.

다시 처음에 내가 실수했던 코드를 통해 문제점을 인지해보자.

for (i = 0; ; ) {
  // ...
}

위의 for문이 실행되는 위치가 깊어지면 깊어질수록 결국 상위 스코프를 탐색하고 최악의 경우 암묵적 전역에 의한 전역객체의 프로퍼티를 참조하는 얘기가 된다.

이러한 런타임 이전의 개발 단계에서의 실수를 줄이기 위해 우리는 ESLint를 생활화하고 let, const 선언 키워드를 활용해 각 단계의 스코프를 쉽게 파악할 수 있도록하여 예상치 못한 부작용 발생을 방지하자.

profile
꾸준히 성장하는 개발자를 목표로 합니다.

0개의 댓글