선언자와 Scope, Hoisting

Haz·2024년 1월 21일
0

개발여행기

목록 보기
14/32

공부를 하면서 헷갈렸거나 확실히 머릿속에서 정립되지 않은 부분들을 따로 공부해 정리해봤다.

기초부터 완성까지, 프런트엔드 - 이재성|한정 지음

이 책에 내용들이 잘 정리되어있고 이해하기도 쉬워서 참고했다! 언어에 대한 부분은 물론, 네트워크나 다른 부분에 대해서도 함께 이해하기 좋은 책이었다. 도서관에서 발견한 책인데 역시 도서관 최고!

Scope & Hoisting

프로그래머스에서 알고리즘 문제를 풀다가 const 선언자로 변수를 할당했음에도 객체나, 배열의 경우 값이 추가되거나 삭제되는 걸 발견했다. 이상했다.

const 선언자로 값이 할당된 변수는 값을 재할당하려고 하면 당연히 에러가 발생해야하는 것 아냐?

하지만 조금만 생각해보면 당연히 되는 일이었다. 결국 값을 할당하는 건 = 연산자의 일이기 때문에 배열의 push() 메소드나 pop() 같은 메소드는 값을 재할당하는 게 아니기 때문이다. 객체의 경우에도 동일했다.

const object = {};

// 아래 두 경우는 object 내부 값을 변경하지만, object 자체를 재할당하는 건 아니다.
object.name = 'declarement';
object.declarer = 'const';

// 아래의 경우는 object 값을 재할당하므로 에러가 발생한다.
object = {
  name: 'declarement',
  declarer: 'const
}

이상하다고 생각할 수 있지만 엄연히 두 경우는 다른 경우이므로 2번째 경우에만 에러가 발생했다.

이에 대한 연장선 상에서 스코프와 호이스팅에 대해서도 궁금해졌다. 이전까지 스코프의 종류에는 두 가지가 있다고 생각했다. 전역 스코프와 렉시컬 스코프. 두 가지가 있는 건 맞았다. 그러나 "전역""렉시컬"은 같은 범위 내에서 상반되는 개념이 아니었다. 잘못 이해하고 있었던 거다.

전역 스코프와 호이스팅

사실상 전역 스코프라는 건 존재하지 않았다. 그렇게 생각하게 된 이유는 단 하나, var 선언자 때문이었다.

그렇다면 어떻게 스코프가 구별하면 될까? 바로 "함수 스코프""블록 스코프"로 구별한다.

함수 스코프는 말 그대로 함수를 선언하면 함수 내에 있는 모든 변수가 함수 스코프에 포함된다. 즉, 어떻게 보면 전역 스코프라고 생각했던 내용은 함수 스코프에 포함될 수 있는 내용이다. var 선언자로 선언된 함수는 함수 스코프를 따르기 때문에 만약 조건문 안에 있다고 하더라도, 조건문 외부에서 호출이 가능하기 때문이다.

function example() {
  console.log(variant); // undefined
  if(variant !== null) {
  	var variant = 0;
  }
  console.log(variant); // 0
}

이런 식으로 말이다. variant는 분명 조건문 안에서 선언되었음에도 호이스팅되어 함수 스코프로 끌어올려진다. "전역" 또한 익명 즉시실행함수로 볼 수 있으니까 대략적으로 함수 스코프를 전역 스코프라고 멋대로 이해해버린 거다.

var 선언자로 선언된 변수라 해서 무조건 전역적으로 호이스팅되는 건 아니다. ❗함수 스코프❗ 내에서만 호이스팅되며 전역 변수일 경우에만 전역적으로 호이스팅이 된다.

var 선언자가 변수를 호이스팅시키는 이유는 선언과 초기화가 동시에 이뤄지기 때문이다. 함수 레벨 최상단으로 변수를 끌어올려 선언 후 값이 할당되지 않은 채 초기화된다. 그래서 예시처럼 undefined로 값이 할당된 것처럼 만드는 오류가 발생한다.

블록 스코프와 호이스팅

만약 위 코드에서 variantvar 선언자가 아닌 let, const로 선언됐다면 첫번째 console.log에서 undefined가 찍히는 요상한 결과가 나오지는 않았을 것이다.

let, const 두 선언자는 블록 스코프를 준수하기 때문이다. 블록 스코프는 변수의 유효 범위를 블록(중괄호) 단위로 제한하기 때문에 제멋대로 호이스팅되는 함수 스코프의 문제를 해결한다.

이 두 선언자는 선언과 초기화가 분리되어있다. var 선언자와 동일하게 선언은 스코프 최상단으로 끌어올려져 실행되지만, 초기화는 내가 원하는 위치에서 할 수 있다. 그래서 초기화 이전에 접근할 경우 Reference Error가 발생하고, 이 구간을 TDZ(Temporal Dead Zone)이라고 부른다.

렉시컬 스코프

렉시컬 스코프는 함수, 블록 스코프와는 다른 개념으로 언어 차원의 개념이다. 프로그래밍 언어의 스코프는 대부분 동적, 렉시컬 두 가지 스코프 방식으로 동작하는데 동적 스코프는 런타임 도중에 함수 호출 시 결정되고, 렉시컬 스코프는 변수와 함수 위치에 기반해 결정된다. 자바스크립트는 이 렉시컬 스코프를 따르는 언어로 함수 스코프와 블록 스코프 역시 렉시컬 스코프의 규칙에 따라 스코프의 경계가 결정된다.

한 줄 요약

함수 스코프와 블록 스코프는 스코프의 단위이며, 렉시컬 스코프는 이 스코프들의 범위를 결정하는 규칙이다.

profile
나도 재밌고, 남들도 재밌는 서비스 만들어보고 싶다😎

0개의 댓글