Scope & Hoisting

GY·2021년 11월 3일
0

[JS] 개념 정리

목록 보기
28/32
post-thumbnail
post-custom-banner

🧇 Hoisting

끌어올리다, 들어올리다

변수의 선언을 끌어올리는 것을 말한다.
선언부는 끌어올리고, 선언된 변수에 대한 값 할당은 코드가 실행되는 시점에 진행된다.

자바스크립트에서 모든 변수 선언문은 스코프 내에서 최상위로 Hoisting된다.

예제 1

console.log(a); //undefined
const a = 1;

이 예시에서 에러는 나지 않는다. undefined만 출력될 뿐이다.
실제 코드 실행 과정을 보자.

const a;
console.log(a);
a = 1;

코드를 실행하기 전 변수를 선언하는 부분은 최상위로 이동했다.
단, 할당 구문은 작성한 시점과 동일하게 실행된다.


예제 2


function foo() {
  console.log(a);
   let a = 2;
}

foo();

이 코드의 실행과정을 보자.

function foo() {
  let a;
  console.log(a);
  a = 2;
}

foo();

변수 a를 선언한 구문만 함수 스코프 내에서 최상위로 끌어올려졌다.

🥐 Function Expression (함수 표현식)

표현식?
자바스크립트 interpreter가 계산하여 값을 구할 수 있는 자바스크립트 구절을 말한다.
이러한 값을 표현하는 것을 리터럴이라고 한다.

함수 표현식은 function 키워드로 정의한 함수를 변수에 할당한다.
함수 표현식으로 함수를 작성하는 경우, 항상 실행문의 위치와 함수를 대입하는 구문의 위치가 중요하다.

실행문을 표현식보다 위에 두는 경우

d();//Uncaught TypeError: d is not a function

const d = function () {
  console.log("run function d");
}; 

const d; 변수 선언문은 호이스팅되지만, d에 함수를 대입(할당)하는 구문은 실행문 보다 밑에 있기 때문에 에러가 발생한다.

const d = function () {
  console.log("run function d");
};

d();//"run function d"

변수 선언문이 호이스팅되고, 함수 대입이 된 다음 실행되므로 정상적으로 실행된다.

🥐 Function Declaration (함수 선언식)

함수선언식으로 작성된 함수는 항상 최상위로 Hoisting되어 실행문의 순서가 중요하지않기 때문에 편리하다.
function의 정의만 존재할 뿐 별도의 할당 명령이 없다.

j();

function j() {
  console.log("j");
}

j();

함수 실행문이 어디에 있어도 정상적으로 실행된다.



🥐 let, const는 Hoisting이 되지 않는다? (TDZ)

let과 const는 언뜻 보면 var과 달리 호이스팅이 되지 않는 것 같다.
예시를 보자.

  1. var 호이스팅
console.log(x);
//undefined
var x = 3;

var로 선언한 변수는 호이스팅되어 선언되었고, 값은 코드 실행시점에 아직 할당되지 않았기 때문에 undefined가 뜬다.

  1. let, const 호이스팅
console.log(x);
// Uncaught ReferenceError: x is not defined
let x = 3;

undefined가 아닌 is not defined가 뜬다.
x 변수 자체가 선언이 되지 않았음을 볼 수 있다.

한 가지 예시를 더 보자.

x = 3; 
// Uncaught ReferenceError: Cannot access 'x' before initialization
let x = 1;

선언되기 전에 x에 접근할 수 없다는 에러가 뜬다.
이는 x가 호이스팅되어 최상위에서 선언되지 않았기 때문에 이에 접근하여 값을 할당할 수 없다는 것이다.

그럼 let, const는 호이스팅이 되지 않는걸까?

hoisting이 되지 않는 것처럼 보일 수는 있지만, 엄밀히 말하면 hoisting이 되기는 한다.

이게 무슨 말이냐면...

Temporal Dead Zone (TDZ)

let, const 역시 마찬가지로 LexicalEnvironment에 변수 정보를 미리 수집해 놓는다.
즉, 호이스팅이 된건 맞다.
단, let과 const는 코드가 실행되기 전까지는 이 변수 정보에 액세스 할 수 없어 호이스팅이 되지 않은 것처럼 보인다.
이렇게 액세스 할 수 없는 단계를 TDZ, Temporal Dead Zone에 있다고 표현한다.

🧇 Scope

스코프는 코드의 접근 범위를 결정한다.

function foo() {
  const a = 1;

  console.log(a);
}

foo();

이 코드의 실행과정을 보자.

  1. foo 함수 생성
  2. foo 함수 실행
  3. a 변수 선언하고 1이라는 값을 해당 변수에 할당
  4. console.log(a)실행
  5. 선언된 변수 a를 찾고, 할당된 값 1 출력
  6. foo 함수의 실행문 종료
  7. 더 이상 실행할 실행문이 없으므로 프로그램 종료

a변수는 함수레벨에서 스코프가 설정된다. 같은 스코프에 있는 console.log(a)는 a의 값을 찾아 정상적으로 출력이 가능하다.

🥐 Scope Chain

스코프는 계층 구조로 형성되어 있다.
가장 최상위 Scope는 Global Scope이며, 그 하위에 함수 생성을 기준으로 Scope가 형성된다.

상위 스코프에서 하위 스코프의 정보는 접근할 수 없다.
오직 하위 scope에서 상위 scope의 정보를 접근할 수 있다.

자바스크립트는 실행문의 위치를 기준으로 하위 Scope부터 시작해 원하는 값을 찾을 떄까지 상위로 탐색한다.


예시 1

const a = 1;

function foo() {
  const a = 2;
  console.log(a); 
}

foo(); // 2

console.log(a)가 실행되었을 때, foo함수 scope내부에서 a 변수를 탐색한다.
2라는 값이 할당된 변수 a를 찾으면 이 값을 출력한다.

예시 2

const a = 1;

function foo() {
  const a = 2;
  console.log(a); // 2
}

foo();
console.log(a); // 1

foo 함수 내부에 있는 console.log(a)는 같은 함수 스코프에 있는 a=2를 찾아 출력한다.
전역 스코프에 있는 console.log(a)는 같은 전역 스코프에 있는 a=1을 찾아 출력한다.
전역 스코프에 있는 console.log(a)는 foo함수 내부의 a값에 접근할 수 없지만,
foo함수 내부에 있는 console.log(a)는 해당 스코프에 a가 없다면 상위스코프로 올라가 a = 1을 찾을 수 있다.

예시 3

function foo() {
  const a = 2;
  console.log(a);// 2
}

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

전역 스코프에 있는 console.log(a)는 하위 스코프인 foo함수 스코프에 접근할 수 없기 때문에, 전역 스코프에 a값이 없다면 에러가 난다.

🥐 Global Scope

전역 스코프는 최상위 스코프이기 때문에 어떤 스코프에서도 접근할 수 있다.
따라서 데이터가 함부로 변경되거나 수정되지 않도록 주의해 사용해야 한다.



🧇 예제로 좀 더 살펴보자.

🥐 예제 1

function foo() {
  const a = 5;

  for (let i = 0; i < a; i++) {
    console.log(a);
  }

  console.log(i); // error
}

foo();

console.log(i)는 i가 선언되고 할당된 for문의 바깥에 있다. 즉, 상위 스코프에 있기 때문에 하위 스코프에 접근해 i의 값을 출력할 수 없다.


🥐 예제 2

const a = 1;

function bar() {
  function foo() {
    console.log(a); // 1
  }

  foo();
}

bar();

🥐 예제 3

const a = 1;

function bar() {
  function foo() {
    console.log(a); // 2
  }

  a = 2;

  foo();
}

bar();

🥐 예제 4

function foo() {
  const a = 1;

  function bar() {
    a = 2;
  }

  console.log(a); // 1
  bar();
}

foo();

🥐 예제 5

const x = 1;

function foo() {
  if (x > 1) {
    const x = 2;
  }

  console.log(x);//1
}

foo();

업데이트
2020.02.23

Reference

  • 모던 자바스크립트 Deep Dive
profile
Why?에서 시작해 How를 찾는 과정을 좋아합니다. 그 고민과 성장의 과정을 꾸준히 기록하고자 합니다.
post-custom-banner

0개의 댓글