호이스팅 , TDZ

김주형·2022년 7월 20일
0

스코프

scope는 전역 스코프와 지역 스코프로 나뉜다.

전역 스코프:코드의 가장 바깥 영역 (코드 전체에서 동작)
지역 스코프:함수 블럭 내에서 동작하는 스코프

변수를 선언하는 키워드는 const,let,var 3개가 있다.

**같은 스코프 내에서
var -> 중복 선언을 허용한다.
, 함수 레벨 스코프이다.


let , const -> 중복 선언을 허용하지 않는다.,
블록 레벨 스코프이다. **

스코프 체인

스코프가 계층적으로 연결된 것을 말한다.

var x = 'global x';
var y = 'global y';

function outer() {
  var z = 'outer z';

  console.log(x); // global x
  console.log(y); // global y
  console.log(z); // outer z

  function inner() {
    var x = 'inner x';
    console.log(x); // inner x
    console.log(y); // global y
    console.log(z); // outer z
  }
  inner();
}

outer();

console.log(x); // global x
console.log(z); // ReferenceError: z is not defined


1.inner()함수 자체로 지역 스코프를 가지고 있는다.
2.outer()함수 자체로 지역 스코프를 가지고 있는다.
3.그래서 변수를 참조할 때 지역 스코프에서 상위 스코프 방향으로 이동하면서 검색을 시작한다.
4.위 구문의 경우 inner()함수에서 x라는 지역변수가 있기 때문에 x를 콘솔에 출력해주고 y는 outer()함수에도 선언되지 않았기 때문에 전역변수 y를 참조한다. z는 inner()에는 없지만 outer()에는 선언 되었기 때문에 outer()에 선언된 지역변수 z를 참조한다.

렉시컬 스코프
자바스크립트 엔진은 코드를 실행하기에 앞서 렉시컬 환경을 생성한다. 렉시컬 환경은 실행 컨텍스트 부분에서 자세히 얘기해보고 여기서 체크해야 할 것은 자바스크립트가 함수의 상위 스코프를 결정하는 방식이다.

동적 스코프 -> 함수를 어디서 호출했는지에 따라서
렉시컬.정적 스코프 -> 함수를 어디서 정의했는지에 따라서

자바스크립트는 함수를 어디서 정의 했는지에 따라서 스코프체인을 만들기 때문에 렉시컬 스코프를 따르는 언어이다.

호이스팅

->JS는 소스코드를 실행하기 이전에 평가 단계를 거치는데 이때 실행 컨텍스트를 준비하고 변수,함수 선언문을 실행 컨텍스트에 넣고 필요한 메모리 공간들을 미리미리 할당하는 것.

->인터프리터가 변수와 함수의 메모리 공간을 선언 전에 미리 할당하는 것(MDN)

->주의할점은 스코프 내부에서 동작한다는 점이다.

cf)실행 컨텍스트란?
실행 가능한 코드를 형상화하고 구분하는 추상적인 개념이라고 정의한다. 좀 더 쉽게 말하자면 실행 컨텍스트는 실행 가능한 코드가 실행되기 위해 필요한 환경

자세한 내용은 콜 스택 부분에서 얘기하고 우선은 자바스크립트 엔진이 실행되기 위해서 필요한 변수,함수등일 미리 담아두는 공간이라고 생각하자.

var 키워드는
1.변수의 중복 선언을 허용한다.
2.함수 레벨 스코프이다.
3.호이스팅에 의해서 변수 선언문이 스코프의 선두로 끌어 올려진것처럼 동작한다. 즉 변수 선언문 이전에 참조 할 수는 있지만 할당문 이전에 변수를 참조하면 언제나 undefined를 반환한다.

console.log(foo); // 아직 할당하지 않아서 undefined

foo = 123; // 할당

console.log(foo); // 123

var foo; // 호이스팅에 의해서 선두에 선언 된것처럼 동작한다.

let 키워드
1.변수의 중복 선언이 금지된다.
2.블록 레벨 스코프이다.
3.var 키워드와 다르게 호이스팅이 발생하지 않는 것 처럼 동작한다.
(실제로는 동작하지만...)

let 키워드로 선언한 변수는 스코프의 시작 지점부터 변수 선언문까지 변수를 참조할 수 없다. 이를 TDZ라고 한다.

TDZ(Temporal Dead Zone)
일시적 사각지대
스코프의 시작 지점부터 초기화 시작 지점까지(변수 선언문 까지) 변수를 참조할 수 없는 구간을 의미한다.

console.log(foo); // ReferenceError : foo is not defined

let foo; // 변수 선언문에서 초기화 단계가 실행된다.
console.log(foo); // undefined

foo = 1; // 할당문에서 할당 단계가 실행된다.
console.log(foo); // 1

즉 위 구문은 주석에서도 설명했지만 foo라는 변수가 let키워드로 선언했을때 호이스팅이 발생하긴 하지만 let foo;(변수 선언문)를 만나기 전까지는 변수를 참조할 수 없는 TDZ(일시적 사각 지대)에 갇힌다고 말할 수 있다.

let 키워드가 호이스팅이 발생하는것을 알 수 있는 예제

let foo = 1;

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

위 구문에서는 {} 블록 스코프 내에서 foo 라는 변수가 호이스팅 되었다. 즉 console.log(foo)를 했을때 foo라는 변수가 있다는 것을 알고있지만 foo는 var과 다르게 선언과 동시에 undefined를 할당하지 않는다.
즉 foo 에는 현재 아무것도 없는데 console.log(foo)를 해버리니 에러가 발생하는 것이다.

const 키워드
1.const 키워드로 선언한 변수는 반드시 선언과 동시에 초기화해야 한다.
2.블록 레벨 스코프를 가진다.
3.호이스팅이 발생하지 않은것처럼 동작한다.
4.재할당이 금지된다.
5.const 키워드에 원시값을 할당한 경우 값의 변경은 불가능하지만 객체의 경우 변경이 가능하다. (재할당 하지 않아도 변경 가능하기 때문에)

함수 선언문과 함수 표현식에서 호이스팅 방식의 차이

// 함수 선언문
function getName() {
  console.log(`name`);
}

// 함수 표현식 
var name = function () {
  console.log(`name`);
};

// 화살표 함수
const get_name = () => {
	console.log('name')
}

함수 선언문의 경우에 함수가 정의되기 이전에 호출이 가능하다. 함수의 선언 위치와 상관없이 코드 내 어느 곳에서든지 호출이 가능하다.
함수 선언,초기화,할당이 한번에 이루어진다.

var res = square(5);

function square(number) {
  return number * number;
}

함수 표현식

var res = square(5);

var square = function (number) {
  return number * number;
};

TypeError: square is not a function

함수 표현식의 경우 함수 호이스팅이 아니라 변수 호이스팅이 발생한다.
runtime에 해석되고 실행된다.

즉 일단 함수 표현식이면 var square = ~ 뒤에 구문이 변수든지 함수든지 var square이기 때문에 변수의 호이스팅 규칙을 따른다. 그럼 실행 컨텍스트에서는 square가 undefined로 초기화 되어 있을텐데(var)이기 때문에 이걸 함수로 호출해버리니 당연히 함수가 아니라는 에러가 나오는 것이다.

실행 컨텍스트와 콜 스택

실행 컨텍스트
함수가 실행되는 환경
함수 -> 변수 매개변수 내장함수 this

JS는 싱글스레드 언어
하나의 호출스택 Call Stack으로 언어를 실행한다.

처음에는 글로벌 실행컨텍스트가 스택에 쌓이고
그 이후에 함수에 해당하는 실행컨텍스트가 쌓인다.
실행컨텍스트 단위로 콜스택에 쌓여서 코드가 실행된다.

스코프 체인 = 실행컨텍스트 체인
렉시컬 환경 = 렉시컬 스코프

  • Environment record : 변수와 값
  • Outer environment record : 위쪽 변수,함수

실행컨텍스트의 종류 3가지
Global execution context (전역 실행 컨텍스트) = Global Object(GO)
-this object
-window object

Function execution context = Activation Object(AO)
Eval execution context

GO:빌트인 객체 , BOM , DOM , 전역변수
AO:함수선언,매개변수,변수

1.생성단계와
GO,AO,this 형성
스코프체인 : 변수쉐도잉발생
이 때문에 호이스팅이 가능하다.
값이 들어가 있지 않는 초기값(var은 선언과 초기화 , let,const는 선언만)

2.실행단계로 나누어진다.
GO,AO,this
-값이 할당
-This는 함수호출패턴 또는 lexical scope에 따라 값이 정해짐

실행컨텍스트 함수가 실행되는 환경

실행컨텍스트를 기반으로 콜스택에 쌓여서 자바스크립트가 실행된다.

스코프 체인, 변수 은닉화

스코프 체인은 해당 코드의 유효 범위(in scope) 안에 있는 변수를 정의하는 객체의 체인, 리스트다.

앞에서도 몇 번 언급했기 때문에 쉽게 얘기하면 어떤 함수 내에서 변수를 참조할때 해당 스코프 내에서 변수가 없다면 상위 스코프로 올라가서 변수를 탐색하는데 이걸 리스트로 연결한 자료구조이다.

변수 은닉화

1.즉시 실행함수

(function () {
  let a = 'a';
});

즉시 실행되는 함수는 정의되는 즉시 반환값만 남기고 사라지기 때문에 함수 내부에서 선언한 변수들을 즉시실행함수 외부에서 참조할 수 없다.

2.클로저

클로저를 통해 특정 변수에 접근을 가능하게 해서 변수를 은닉시킬 수 있다.

콘솔에 찍힐 b 값을 예상해보고, 어디에서 선언된 “b”가 몇번째 라인에서 호출한 console.log에 찍혔는지, 왜 그런지 설명해보세요.
주석을 풀어보고 오류가 난다면 왜 오류가 나는 지 설명하고 오류를 수정해보세요.


let b = 1;

function hi() {
  const a = 1;
  console.log(a);

  let b = 100;

  b++;

  console.log(a, b);
}

// console.log(a);
// 여기서 a를 호출하게되면 전역에서 참조할 a 값이 없기 때문에 a는 hi()의 지역스코프이다.
// 즉 외부에서 참조할 수 없다.
// a is not defined 오류 메시지를 호출하게 된다.

console.log(b); // 1 전역에서 호출한 b이기 때문에

hi(); // 1 101

즉 hi()를 호출했을때 a,b의 생명주기는 블록 스코프이지만 전역변수로 선언한 b의 생명주기는 전역이기때문에 소멸되지 않는다.

profile
프론트엔드 개발 지망생입니다.

0개의 댓글