study

변수 Hoisting 과 var , let , const 키워드

kim-hasa 2022. 3. 4. 00:32
Hoisting이란 ?

 

변수 선언문이 코드의 맨 위로 끌어 올려진 것처럼 동작하는 자바스크립트 고유의 특징. 스코프 단위로 동작한다.

console.log(item);	// undefined

var item;		// 변수 선언문

console.log가 실행되는 시점에 , item 변수는 선언되어있지 않았으므로 에러가 뜰 것 같지만 , 실제로는 에러가 발생하지 않고 undefined가 실행된다. 변수 선언은 소스코드가 실행되는 시점이 아니라 , 그 이전 단계에서 이미 실행되기 때문이다.

console.log(item);	// undefined

var item;		// 변수 선언문
item = 10;		// 값 할당

console.log(item);	// 10

다만 할당은 이야기가 다르다. 할당은 소스코드가 실행이 되는 시점에 실행된다. 그래서 위와 아래의 console.log의 값이 다른 것이다.

console.log(item);	// undefined

var item = 10;		// 변수 선언문 + 할당

console.log(item);	// 10

위 경우는 어떨까 ?? 선언문과 할당이 같이 있는 경우이다. 이 경우에는 선언되면서 할당되기 때문에 모두 10이 나올 것 같지만 , 실제로 자바스크립트는 선언과 할당을 나누어서 진행한다. 그래서 선언이 되고 undefined로 초기화가 된 후에 특정 소스코드가 실행 될 때 10이 할당이 되는 것이다.

console.log(item);

item = 10;
var item;

console.log(item);

그렇다면 이 경우에는 어떨까 ?? 선언이 아래 있을 뿐만 아니라 할당마저 선언보다 먼저 있다. 이 경우도 마찬가지로 위는 undefined, 아래는 10이 나오게 된다. 마찬가지로 선언이 맨 위로 끌어올려져서 실행이 되고 , undefined로 초기화 된 후에 소스코드를 만나면 10이 할당이 된다. 그 이후 아래 출력이 이루어진다.

 

var

 

ES5 에서는 변수를 선언할 방법이 유일하게 var 뿐이었다. 

 

var의 특징

1. 중복 선언 가능

var x = 1;
var y = 1;

var x = 100;
var y;

console.log(x);	// 100
console.log(y); // 1

x 변수와 y 변수가 모두 중복 선언되었다. 이때 , 에러가 발생하지 않는다. 그래서 동일한 이름이 있다는 것을 모르고 중복 선언한 경우 의도치 않게 먼저 선언된 변수의 값이 변경되는 사태가 발생할 수 있다.

 

2. 함수 레벨 스코프

var 키워드는 오로지 함수의 코드 블록만을 지역 스코프로 인정한다. 즉 , 함수 외부에서 var 키워드를 선언하면, 코드 블록 내에서 선언해도 모두 전역변수가 되어버린다.

var x = 1;

if(true) {
	var x = 10;
}

console.log(x); // 10
--------------------------------------------------

var i= 10;

for(var i=0; i<5; i++){
	console.log(i);		// 0 1 2 3 4
}

console.log(i);	// 5

 

var의 Hoisting

 

맨 위 Hoisting을 설명할 때 다루었다. 다만 , ES6 에 와서는 let과 const가 추가되었다. 이 때의 경우는 어떤식으로 달라지는지 확인해 보겠다.

 

let

 

ES6에서 새롭게 추가된 변수 선언 키워드이다. var과의 차이점을 중심으로 비교해보겠다.

1. 변수 중복 선언 금지

var에서는 중복으로 선언해도 에러가 나지 않았지만 , let에서는 이름이 같은 변수를 중복 선언하면 에러가 발생한다.

var foo = 123;
var foo = 456;	// 문제없음

let boo = 123;
let boo = 456;	// 문제 발생

console.log(window.boo);	// undefined

또한 , let 키워드로 선언한 전역 변수는 전역 객체의 프로퍼티가 아니다. 그래서 window.boo 와 같이 접근할 수 없다.

 

2. 블록 레벨 스코프

var은 함수 레벨 스코프를 따르지만 , let은 모든 코드 블록을 지역 스코프로 인정하는 블록 레벨 스코프를 따른다.

let foo = 1;	// 전역 변수

{
	let foo = 2;	// 지역 변수
    	let bar = 3;	// 지역 변수
}

console.log(foo);	// 1
console.log(bar);	// bar is not defined

let 키워드는 블록 레벨 스코프를 따르므로 , 코드 블록 내의 선언된 foo 변수와 bar 변수는 지역변수이다. 그래서 전역에서는 bar 변수를 참조할 수 없다.

let i = 10;

// ----- 함수 레벨 스코프
function foo(){
    let i = 100;
    
    // ----- 블록 레벨 스코프
    for(let i=1; i<3; i++){
    	console.log(i);		// 1, 2
    }
    // ----- 블록 레벨 스코프
    console.log(i);	// 100
}

// ----- 함수 레벨 스코프
foo();

console.log(i);	// 10

이 경우도 마찬가지로 스코프가 블록 레벨 스코프 기준으로 나누어져 있기 때문에 , 다른 결과가 나오게 된다.

 

let의 Hoisting

var과 달리 let 키워드로 선언한 변수는 변수 호이스팅이 발생하지 않는 것처럼 동작한다.

console.log(foo);	// error
let foo;

let 키워드로 선언한 변수를 변수 선언문 이전에 참조하면 , 참조 에러가 발생하게 된다.

그 이유는 var키워드는 런타임 이전에 선언단계와 초기화 단계가 한번에 발생한다. 즉 , 선언 단계에서 변수를 등록하고 초기화 단계에서 undefined로 초기화 한다. 그 이후 할당을 하는 방식이다.

하지만 let 키워드는 선언단계와 초기화 단계가 한번에 발생하지 않는다. 런타임 이전에 선언단계가 실행되지만 , 변수 초기화는 변수 선언문에 도착했을 때 이루어진다. 그래서 초기화 단계가 실행되기 전에 변수에 접근하려고 하면 참조 에러가 발생한다.

console.log(foo);	// error 발생 , 선언 단계

let foo;
console.log(foo);	// undefined , 초기화 단계

foo = 1;
console.log(foo);	// 1 , 할당 단계

 

const

const 키워드는 상수를 설명하기 위해 사용하지만 , 반드시 상수만을 위해서 사용하지는 않는다. const의 특징은 대부분 let과 동일하지만 , 차이점이 존재한다.

 

1. const 키워드로 선언한 변수는 반드시 선언과 동시에 초기화해야 한다.
const foo;	// Error
{
    console.log(foo);	// Error
    const foo = 1;
    console.log(foo);	// 1
}

console.log(foo);	// Error

또한 블록 레벨 스코프를 가진다.

 

2. 재할당 금지
const foo = 1;	
foo = 2;	// Error

var, let 키워드로 선언한 변수는 재할당이 자유로우나 const 키워드로 선언한 변수는 재할당이 금지된다.

 

3. const 키워드로 선언한 변수에 객체를 할당한 경우 , 값을 변경할 수 있다.
const person = {
	name : 'Lee;
};

person.name = 'Kim';

console.log(person);	// {name: "Kim"}

즉 , const 키워드는 재할당을 금지할 뿐 "불변"을 의미하는 것은 아니다. 변수 선언에는 기본적으로 const를 사용하고 , 재할당이 필요한 경우 let을 사용하는 것이 좋다. ES6를 사용하는 경우 , var 키워드는 사용하지 않는다. 일단 const를 사용하고 , 재할당이 필요한 경우 let을 사용하는 것이 좋다.