[예시1]
function sum(a, b) {
return a + b
}
var sumOneAndTwo = sum(1, 2)
// 출력값 : 3
매우 간단하고 호이스팅을 테스트 해보기 좋은 코드다. 위의 코드는 언뜻 보기에도 아무 문제가 없이 실행될 것이고 a, b 값에 각각 1과 2가 전달되었으므로 3을 반환할 것이다. 예상 결과 값을 반환하며 sumOneAndTwo 변수에는 3이 담기게 된다.
함수를 선언한 뒤에 함수를 실행하는 코드를 작성해서 함수를 호출하는 것이 일반적인데, 자바스크립트에서는 다른 방법으로 함수를 선언하고 호출할 수 있다.
[예시2]
var sumOneAndTwo = sum(1, 2)
function sum(a, b) {
return a + b
}
// 출력값 : 3
[java 예시1]
System.out.println(num);
int num = 1;
자바에서 java 예시1 같이 선언하면 에러가난다.
하지만 자바스크립트에서는 선언이 된 부분을 문서의 첫번째로 옮기기 때문에
[js예시1]
console.log(num);
var num =1;
이 코드를 실행하면 아래와 같이 동작한다는 것이다.
[js예시2]
var num;
console.log(num);
var num = 1;
위의 두 코드들은 결과값이 모두 같게 나온다.
console.log 의 차이는 있어도 이미 이 코드들은 호이스팅이 되어있기 때문에 실제 결과값은 모두 동일하게 나오는것을 확인할 수 있다.
우선 TDZ를 알기전 미리 알아야될 부분을 미리 설명하겠습니다.
TDZ는 아래의 내용으로 모든것을 설명할 수 있습니다.
javascript에서의 변수는 위의 사진처럼 선언, 초기화, 할당이라는 3가지 단계의 걸쳐서 생성됩니다.
선언 단계(Declaration phase) : 변수를 실행 컨텍스트의 변수 객체에 등록하는 단계를 의미합니다. 이 변수 객체는 스코프가 참조하는 대상이 됩니다.
초기화 단계(Initialization phase) : 실행 컨텍스트에 존재 하는 변수 객체에 선언 단계의 변수를 위한 메모리를 만드는 단계 입니다. 이 단계에서 할당된 메모리에는 undefined로 초기화 됩니다.
할당 단계(Assignment phase) : 사용자가 undefined로 초기화된 메모리의 다른 값을 할당하는 단계 입니다.
지금까지 우리는 var 혹은 let/const를 그냥 사용하였지만 사실은 위 3가지 단계를 거쳐서 생성되는 것 이었습니다.그리고 var와 let/const의 차이는 이 3가지 단계의 순서에 차이가 존재합니다.
위 사진은 var 키워드 변수의 라이프 사이클 입니다. var 키워드 변수는 변수 선언전에 선언 단계와 초기화 단계를 동시에 진행합니다.
그래서 javascript는 실행 컨텍스트 변수 객체의 변수를 등록하고 메모리를 undefined로 만들어 버립니다.
그렇기 때문에 변수를 선언하기 전에 호출을 해도 undefined로 호출이 되는 호이스팅이 발생하는 것 입니다.
그렇다면 let의 라이프 사이클을 한번 봐보도록 하겠습니다.
let으로 선언된 변수는 var 키워드와는 다르게 선언단계와 초기화 단계가 분리되어서 진행이 됩니다.
그렇기 때문에 실행 컨텍스트에 변수를 등록했지만, 메모리가 할당이 되질 않아 접근할 수 없어 참조 에러(ReferenceError)가 발생하는 것 이고,이것을 보고 우리가 호이스팅이 되질 않는다!! 라고 오해할 수 밖에 없었던 것 입니다.
즉, let 또한 선언전, 실행 컨텍스트 변수 객체에 등록이 되어 호이스팅이 되지만,
이 TDZ 구간에 의해 메모리가 할당이 되질 않아 참조 에러(ReferenceError) 발생하는 것 입니다.
실행 컨텍스트를 보기 앞서, Stack과 Queue에 대해 간략히 알아보겠습니다.
Stack, Queue
Stack: LIFO (Last In First Out)
Queue: FIFO (First In First Out)
Stack에서는 가장 마지막에 들어온 d,c,b,a 순으로 데이터를 꺼낼 수 있습니다.
Queue에서는 제일 먼저 들어온 a,b,c,d 순으로 데이터를 꺼낼 수 있습니다.
하나의 실행 컨텍스트를 구성할 수 있는 방법으로는 다음과 같습니다
전역 공간
evel() 함수
함수
// ------------------- (1)
var a = 1;
function outer() {
function inner() {
console.log(a);
var a = 3;
// --------------- (2)
}
inner(); // -------- (3)
console.log(a);
// ----------------- (4)
}
outer(); // ---------- (5)
console.log(a);
// ------------------- (6)
실행 순서 : (1) → (5) → (3) → (2) → (4) → (6)
[참고사진]
실행 컨텍스트를 생성할 때, VariableEnvironment에 정보를 먼저 담은 후 이를 그대로 복사해서 LexicalEnvironment를 만들고 이후에 LexicalEnvironment를 주로 활용하게 됩니다. 실행중에도 변경사항이 반영되지 않으며 초기 상태를 유지합니다.
내부 구성 요소 : 현재 컨텍스트 내의 식별자들에 대한 정보 + 외부 환경 정보가 선언 시점의 스냅샷으로 저장
내부 구성 요소 : VariableEnvironment와 동일하지만 변경 사항이 실시간으로 반영
가장 당연하고 우선된 둘의 차이는 문법의 차이다.
함수 선언식 : 함수 선언식은 일반적인 함수 선언을 말한다. 함수 선언식은 아래와 같은 문법으로 선언하고 호출할 수 있다.
사실상 함수 선언문이라고 부르는 게 맞을 것이다. 하지만 함수 표현식과 비교를 하다보니 함수 선언문을 함수 선언식이라고도 부르는 것 같다.
/ 함수 선언문
function sayHi() {
console.log('Hi!');
}
// 함수 호출
sayHi();
// 함수 선언문
let sayHello = function () {
console.log('Hello!');
}
// 함수 호출
sayHello();
두번째 차이는 스코프의 차이다.
스코프(Scope)란?
Scope. 자바스크립트에서 스코프는 우리말로 유효 범위라고 해석하는데, 유효 범위로 번역하는 글도 많이 있지만 그냥 한글 그대로로 스코프라는 용어를 상대적으로 더 많이 사용하는 것 같다.
아무튼 말그대로 "어떤 값이 영향을 미칠 수 있는 범위" 를 스코프라고 말하며 크게는 전역 범위(Global Scope)와 지역범위(Local Scope) 두 가지로 나뉜다.
* var변수와 함수 선언문은 -> 함수 스코프(function scope)를 가진다.
* let과 const 변수 -> 블록 스코프(block scope)를 가진다.
따라서, 함수 선언문은 함수 스코프를 가진다.
{
function sayHi() {
console.log('Hi!');
}
}
sayHi();
function myFunction() {
function sayHello () {
console.log('Hello!');
}
sayHello();
}
myFunction();
sayHello(); // 출력값 : Error
함수 표현식은 어떤 키워드로 선언한 변수에 할당하는지에 따라 스코프가 달라진다.
{
var sayHello = function () {
console.log('Hello!');
}
}
sayHello();
{
let sayHello = function () {
console.log('Hello!');
}
}
sayHello(); // Error
함수 선언문은 호이스팅이 일어나고
함수 표현식은 호이스팅이 일어나지 않는다.
다시 말하면 호이스팅은 '선언문'만 끌어올리기 때문에 함수 선언문과, 함수 표현식은 호이스팅의 차이가 있는 것이다.
sayHi();
function sayHi() {
console.log('Hi!');
}
sayHello(); // Error
let sayHello = function () {
console.log('Hello!');
}
출처 : https://bigtop.tistory.com/40