JavaScript | 스코프와 호이스팅
👨💻 스코프와 호이스팅
오늘은 자바스크립트 안에서 스코프라는 용어와 호이스팅이라는 용어에 대해 공부하는 시간을 가져보도록 하겠습니다.
Scope(스코프) 단어 그자체를 직역하면 범위라는 의미를 가지고 있습니다. 그럼 자바스크립트에서 말하는 Scope는 어떤 범위를 의미하는 것일까요?
프로그래밍에서는 변수나 함수에 이름을 부여하여 의미를 갖도록 하고있습니다. 만약 이름이 없다면 변수나 함수는 그저 메모리 주소에 지나지 않게되겠죠. 그래서 프로그램은 "이름: 값"의 대응표를 만들어 사용합니다. 이 대응표의 이름을 가지고 코드를 보다 쉽게 이해하고, 또 이름을 통해 값을 저장하고, 다시 가져와 수정합니다.
초기 프로그래밍 언어는 대응표를 프로그램 전체에서 하나로 관리했었는데, 여기에서 중복된 이름들 간에 이름 충돌의 문제가 발생했습니다.
(전통적인 방식인 var 변수형 타입을 이용하여 선언했다면, 문법적으로 오류가 발생하지 않고 두 번째 나오는 변수가 첫 번째 변수를 덮어쓰여지게 됩니다. 즉 중복된 변수들간에 충돌이 일어난 상황입니다.)
// 호이스팅 문제 발생!
var test = "this is test";
var test = "I am test too!";
(아래의 소스코드는 이러한 중복된 이름으로인한 충돌을 해결한 블록레벨 변수를 사용하여 아래와 같은 상황이 발생하면 문법적으로 오류를 발생합니다. )
// 잘못된 소스코드
let test = "this is test";
let test = "I am test too!";
그래서 충돌을 피하기 위해여 각 언어마다 "스코프"라는 규칙을 만들어 정의를 하였고 여기서 자바스크립트(ES6)는 함수레벨과 블록레벨 의 렉시컬 스코프 규칙을 따르고 있습니다.
[스코프 레벨]
자바스크립트는 전통적으로 함수 레벨 스코프를 지원해왔고, 얼마 전까지만 해도 블록 레벨 스코프는 지원하지 않았습니다. 대표적으로 함수 레벨 스코프인 var만 사용해 왔었죠. 하지만 가장 최신 명세인 ES6(ECMAScript 6)부터 블록 레벨 스코프를 지원하기 시작했고 여기서 나온것이 let 과 const 입니다.
[함수 레벨 스코프 & 블록 레벨 스코프]
자바스크립트에서 var키워드로 선언된 변수나, 함수 선언식으로 만들어진 함수는 함수 레벨 스코프를 갖습니다. 즉, 함수 내부 전체에서 유효한 식별자가 된다는 이야기죠.
함수 내부 전체에서 유효하다는 의미는 반대개념인 블록개념과 비교하여 이해하면 쉽습니다.
블록레벨 스코프들은 블록안에서만 유효한데, 이를 쉽게 설명한것이 지역변수 입니다. 지역변수는 특정 블록안에서만 사용할 수 있는 변수인데요, 해당 지역(블록)을 벗어나면 소멸하여 상요할 수 없는것이 특징입니다. 하지만 함수레벨 스코프를 가지는 함수들은 특정 함수내에서 선언이 되었다면 그 장소가 if문 안이든 While문 안이던간에 그 지역(블록)을 벗어나도 해당 변수를 재 사용하는것이 가능합니다.
[렉시컬 스코프]
그렇다면 렉시컬 스코프란 무엇인가?
렉시컬 스코프(Lexical scope)는 보통 동적 스코프(Dynamic scope)와 많이 비교합니다.
동적 스코프는 프로그램의 런타임 도중의 실행 컨텍스트나 호출 컨텍스트에 의해 결정되고,
렉시컬 스코프에서는 소스코드가 작성된 그 문맥에서 결정된다. 현대 프로그래밍에서 대부분의 언어들은 렉시컬 스코프 규칙을 따르고 있습니다.
[호이스팅 이란]
호이스팅(Hoisting)을 번역하면 들어올려 나르기, 끌어 올리기로 해석됩니다. 자바스크립트에서 말하는 호이스팅도 비슷한 의미로 사용되고 있는데요 아래의 내용을통해 이게 무슨 이야긴지 살펴보도록 하겠습니다.
자바스크립트 함수는 실행되기 전에 함수 안에 필요한 변수값들을 모두 모아서 유효 범위의 최상단에 선언합니다.
- 자바스크립트 파서(Parser)가 함수 실행 전 해당 함수를 한 번 훑습니다.
- 함수 안에 존재하는 변수/함수 선언에 대한 정보를 기억하고 있다가 생행시킵니다.
- 유효범위: 함수 블록 "{}" 안에서 유효
즉, 함수 내에서 아래쪽에 존재하는 내용 중 필요한 값들을 위로 끌어올리는 것입니다. 이러한 모습때문에 호이스팅이라고 부르고있는 것이죠. 단, 실제로는 소스코드가 위로 끌어올려지는 것은 아니며, 자바스크립트 Parser 내부적으로 끌어올려서 처리하는 것이고 실제 메모리 상에서는 변화가 없습니다.
두개의 예시를 통해 호이스팅에 대해 이해를 해보도록 하겠습니다.
function foo() {
a = 2;
var a;
console.log(a);
}
foo();
위의 소스코드를 실행하면 우리가 한눈에 보기에도 문법적으로 문제가 없어보이고, 실행결과 또한 예측한대로 2라는 값이 제대로 출력이 되어질겁니다.
그럼 소스코드상의 순서를 조금 바꿔 아래의 소스코드를 실행시켜 보겠습니다.
function foo() {
console.log(a);
var a = 2;
}
foo();
무언가 선언하지도 않은 변수를 먼저 사용하고 있는 모습에서 부터가 문법적으로 문제가 있어보이는데요.
하지만 var 변수는 앞서 이야기했던 함수 스코프이기 때문에 함수내에서 선언한것은 선언 위치를 기준으로 위에든 아래던간에 사용이 가능합니다.
그렇다면 이 소스코드도 똑같이 2라는 결과값이 나올까요? 대답은 아니오 입니다. 위의 결과로는 undefined 가 출력이 되어지는데요. 이 값은 자바스크립트에서 미리 정의된 상수값이며 의미로는 "선언이 되어있지 않음" 을 의미합니다. 분명 var a = 2 라고 초기화를 했는데 왜 선언이 되어있지 않다고 하는지에 대하여 알아보도록 하겠습니다.
자바스크립트 엔진은 코드를 인터프리팅 하기 전에 그 코드를 먼저 컴파일합니다. var a = 2를 하나의 구문으로 생각하지 않고 다음의 두개 구문으로 분리하여 봅니다.
- var a; (선언)
- a = 2; (할당=초기화)
변수 선언 단계와 초기화 단계를 나누고, 선언 단계에서는 그 선언이 소스코드의 어디에 위치하든 해당 스코프의 컴파일 단계에서 처리해 버리는 것입니다. 때문에 이런 선언단계가 스코프의 꼭대기로 호이스팅("끌어올림")되는 작업이라고 볼 수 있는 것이죠.
[호이스팅 대상]
이러한 호이스팅이 발생되는 대상에는 무엇이 있을까요?
호이스팅은 var변수 선언과 함수선언문에서만 일어납니다.
- var면수/함수의 선언만 위로 끌어 올려지며, 할당은 끌어 올려지지 않는다.
- let/const 변수 선언과 함수 표현식에서는 호이스팅이 발생하지 않는다.
(var 보단 let 과 const를 사용하자)-
함수 표현식이란 익명함수등을 이용한 함수사용을 의미합니다.
-
[호이스팅의 문제]
호이스팅이 자주 발생하게 된다면 개발자가 코드를 해석하는 가독성이 떨어지게 되고 이는 유지보수의 어려움으로 이어지게 됩니다.
참고사이트
'Javascript > 이론' 카테고리의 다른 글
JavaScript | 클로저란 무엇인가? (0) | 2020.06.06 |
---|