자바스크립트에 개념에 대해 공부 하던 중 호이스팅이라는 단어가 나와서
호이스팅 메커니즘이 자바스크립트에서 어떻게 동작하고 호이스팅이 무엇인지 알기 위해 정리를 하겠습니다.
호이스팅(hoisting) 이란
호이스팅은 코드가 실행되기 전 변수 및 함수에 대한 선언이 해당 스코프의 맨 위로 이동되는
자바스크립트 매커니즘 입니다.
이것은 함수와 변수가 선언된 위치에 관계없이 해당 스코프가 전역인지 지역인지 여부에 관계없이 해당 범위의 맨 위로
이동이 됩니다.
Undifind vs ReferenceError
- 선언되지 않은 변수 사용시
console.log(typeof variable); // Output: undefined
Javascript에서는 선언 되지 않은 변수를 사용시 undefined 값이 할당되어 값이 할당되어 값의 타입도 undefined가 출력되는 것을 볼 수 있습니다.
- 선언되지 않은 변수 접근시
console.log(variable); // Output: ReferenceError: variable is not defined
선언되지 않은 변수에 대해 접근하려 한다면 ReffereError가 발생하게 된다.
호이스팅으로 인해 변수를 처리 할 때 javascript 동작이 변수 처리가 이상해진다는 것을 느낄 수 있습니다.
Hoisting 변수
다음의 그림은 자바스크립트의 life cycle이고 변수 선언 및 초기화가 발생하는 순서입니다.
Javascript를 사용하면 변수를 동시에 선언하고 초기화 할 수 있기 때문에 가장 많이 사용되는 패턴입니다.
var a = 100;
하지만 javascript는 백그라운드에서 어김없이 다음과 같은 선언한 변수를 초기화 한다는 것을 기억해야 됩니다.
이위에 언급했던 것처럼 무든 변수 및 함수 선언은 해당 스코프의 맨 위로 호이스트됩니다. 그렇기 때문에 코드가 실행 되기전에 변수 선언이 먼저 처리 된다는 것입니다.
반대로 선언되지 않은 변수는 이를 할당하는 코드가 실행 될 때 까지 존재하지 않습니다. 따라서 선언되지 않은 변수에 값을 할당한다면 할당이 실행될 때 임시적으로 전역 변수가 생성 됩니다. 즉 선언 되지 않은 모든 변수는 전역 변수 입니다.
이 동작을 이해하기 위해 다음과 같은 코드를 보면 알 수 있습니다.
function hoist() {
a = 20;
var b = 100;
}
hoist();
console.log(a);
/*
hoist()라는 스코프 밖에 있는 전역 변수이기 때문에 접근 가능합니다.
Output: 20
*/
console.log(b);
/*
해당 변수는 hoist()라는 스코프안에 선언되었고 그렇기 때문에
함수 스코프에 한정되어 있습니다 따라서 b라는 변수에 접근시 ReferenceError: b is not defined
라고 에러문을 출력합니다.
*/
ES5에서의 동작 원리
var
아래의 코드에 내용을 보시고 어떤 내용이 나올지 추측 해봅시다.
console.log(hoist);
var hoist = 'The variable has been hoisted.';
답은 undefined로 나오게 됩니다. 왜 ReferenceError가 아닌 undefined가 나왔을까요?
이유는 자바스크립트 내부에서 변수 선언을 호이스트 했기 때문에 실제 동작은 아래와 같이 동작합니다.
var hoist;
console.log(hoist); // Output: undefined
hoist = 'The variable has been hoisted.';
그럼 이제 아래의 코드가 동작을 한다면 어떻게 될까요?
function hoist() {
console.log(message);
var message='Hoisting is all the rage!'
}
hoist();
이것도 동일하게 undefined가 나오게 됩니다. 이것도 위와 동일하게 자바스크립트 내부에서 아래의 코드와 같이 인터프린터가 동작하면서 코드를 읽기 때문에 undefined가 나오게 됩니다.
function hoist() {
var message;
console.log(message);
message='Hoisting is all the rage!'
}
hoist(); // Ouput: undefined
이러한 Error를 피하기 위해 사요아기전에 선언하고 초기화를 해야합니다.
function hoist() {
var message='Hoisting is all the rage!'
return (message);
}
hoist(); // Ouput: Hoisting is all the rage!
ES6에서의 동작 원리
es5에서 es6로 버전이 업그레이드 되면서 javascript 표준이 변경이 되면서 선언과 초기화에 관련하여 어떻게 영향을 끼쳤는지 알아봅시다.
let
시작하기 전에 let
이라는 키워드가 function scope가 아닌 block scope라는 것을 인지하셔야 합니다. 이부분은 중요하지만 여기서는 별로 문제될 부분은 없을 것입니다. 간단하게 말해서 변수의 범위는 변수가 선언된 함수가 아니라 변수가 선언된 블록에 바인딩된다는 뜻입니다.
아래의 코드가 동작을 한다면 어떤 값을 출력을 할까요?
console.log(hoist);
let hoist = 'The variable has been hoisted.';
답은 var 키워드인 경우 undefined가 나왔지만 es6 let은 선언되지 않은 변수를 사용하면 undefined로 받아들이지 않기 때문에 인터프리터는 명시적으로 Reference Error를 내뱉습니다.
만약 변수를 먼저 선언하고 사용하게 된다면 아래와 같이 undefined를 리턴하게 됩니다.
let hoist;
console.log(hoist); // Output: undefined
hoist = 'Hoisted'
따라서 이런 오류를 피하기 위해서는 변수를 선언한 다음 변수를 값에 할당하고 사용해야 합니다.
const
const
는 변하지 않는 불변 변수를 허용하기 위해 도입되었습니다. 즉, 한 번 할당되면 값을 수정할 수 없는 변수입니다. 그리고 const도 let과 마찬가지로 선언시 블록 맨 위로 호이스팅하게 됩니다.
const 변수에 연결된 값을 재할당을 시도한다면 어떻게 될까요?
const PI = 3.142;
PI = 22/7; // Let's reassign the value of PI
console.log(PI); // Output: TypeError: Assignment to constant variable.
const 변수도 마찬가지로 선언하지 않고 사용시 아래와 같이 Reference Error를 리턴하게 됩니다.
console.log(hoist); // Output: ReferenceError: hoist is not defined
const hoist = 'The variable has been hoisted.';
주의해야 할 점은 선언과 초기화를 하지 않으면 SyntaxError를 리턴하게 된다는 것입니다.
const PI;
console.log(PI); // Ouput: SyntaxError: Missing initializer in const declaration
PI=3.142;
Hoisting function
javascript에서의 function은 다음과 같이 분류할 수 있습니다.
- function 선언
- function 표현식
function(함수) 선언
이것은 아래와 같은 형태이며 제일 상단까지 호이스팅되어 사용됩니다. 이제 javascript에서 hosting 메커니즘이 어떻게 동작하는지 이해가 하실 수 있을 것입니다.
hoisted(); // Output: "This function has been hoisted."
function hoisted() {
console.log('This function has been hoisted.');
};
function(함수) 표현식
함수 표현식은 호이스팅 되지 않기 때문에 선언 후 사용하셔야 합니다.
expression(); //Output: "TypeError: expression is not a function
var expression = function() {
console.log('Will this work?');
};
함수 선언과 함수 표현식을 혼합하여 사용한다면 어떻게 될까요?
expression(); // Ouput: TypeError: expression is not a function
var expression = function hoisting() {
console.log('Will this work?');
};
위에서 볼 수 있듯이 var expression이라고 호이스팅은 되지만 함수는 아니기 때문에 따라서 TypeError로 예외처리 합니다.
우선 순위
자바스크립트에 대해 변수를 사용할 때 아래의 사항을 주의하시고 작업하시는 것이 좋습니다.
- 변수 할당이 함수 선언보다 우선시 됩니다.
- 함수 선언은 변수 선언보다 우선시 됩니다.
- 함수 선언은 변수 선언에 대해서 호이스트 되지만 변수 할당에 대해서는 호이스트되지 않습니다.
이와같이 글로 이해가 되지 않는다면 코드로 한번 확인해봅시다.
1번과 같은 변수 할당이 함수 선언 보다 우선시 되는 것을 볼 수 있습니다.
var double = 22;
function double(num) {
return (num*2);
}
console.log(typeof double); // Output: number
2번과 같이 변수 선언보다는 함수 선언이 우선시 되는 것을 볼 수 있습니다.
var double;
function double(num) {
return (num*2);
}
console.log(typeof double); // Output: function
호이스팅 Class
자바스크립트 클래스도 다음과 같이 분류 할 수 있습니다.
- 클래스 선언
- 클래스 표현식
클래스 선언
함수와 마찬가지로 자바스크립트 클래스는 호이스트가 됩니다. 그러나 값을 구하기 전까지
초기화 되지 않은 상태로 유지되기 때문에 클래스를 사용하기 전에 클래스를 사용햐여 합니다.
var Frodo = new Hobbit();
Frodo.height = 100;
Frodo.weight = 300;
console.log(Frodo); // Output: ReferenceError: Hobbit is not defined
class Hobbit {
constructor(height, weight) {
this.height = height;
this.weight = weight;
}
}
위와 같이 class Hobbit은 호이스트가 되어 앞에 선언 되었지만 아무것도 없는 undefined 상태이므로 reference error가 뜹니다. 따라서 아래와 같이 사용해야 합니다.
class Hobbit {
constructor(height, weight) {
this.height = height;
this.weight = weight;
}
}
var Frodo = new Hobbit();
Frodo.height = 100;
Frodo.weight = 300;
console.log(Frodo); // Output: { height: 100, weight: 300 }
클래스 표현식
클래스 표현식도 함수 표현식과 마찬가지로 호이스팅 되지 않습니다.
아래의 코드는 클래스 표현식이 이름지정되지 않고 익명으로 사용된 예입니다.
var Square = new Polygon();
Square.height = 10;
Square.width = 10;
console.log(Square); // Output: TypeError: Polygon is not a constructor
var Polygon = class {
constructor(height, width) {
this.height = height;
this.width = width;
}
};
다음은 클래스 선언이 있는 표현식의 예입니다.
var Square = new Polygon();
Square.height = 10;
Square.width = 10;
console.log(Square); // Output: TypeError: Polygon is not a constructor
var Polygon = class Polygon {
constructor(height, width) {
this.height = height;
this.width = width;
}
};
다음과 먼저 초기화를 하지 않고 사용하게 되면 해당 클래스를 찾을 수 없기 때문에 TypeError를 리턴합니다.
올바른 방법은 아래와 같이 사용해야 됩니다.
var Polygon = class Polygon {
constructor(height, width) {
this.height = height;
this.width = width;
}
};
var Square = new Polygon();
Square.height = 10;
Square.width = 10;
console.log(Square); //output:{ height: 10, weight: 10 }
요약
- 자바스크립트 변수를 사용하기 전에 선언하고 초기화하는 것을 습관화 합시다.
- es6 let 및 const 를 사용 하는 동안 선언되지 않은 변수를 사용하면 변수가 실행 시 초기화되지 않은 상태로 남아 있기 때문에 참조 오류 가 발생한다.
참조 링크
https://www.digitalocean.com/community/tutorials/understanding-hoisting-in-javascript
'JavaScript' 카테고리의 다른 글
자바스크립트에서 제너레이터(Generator) (0) | 2021.12.08 |
---|---|
javascript에서 이터러블(iterable)과 이터레이터(iterator)란 (0) | 2021.11.25 |
javaScript에서의 Symbol이란 (0) | 2021.11.25 |
일급 함수와 고차 함수 (0) | 2021.11.20 |