자바스크립트의 호이스팅, 함수와 함수의 종류

Dohyeon Kong·2024년 3월 22일
0

JavaScript🟡

목록 보기
2/13

실행 컨텍스트(Execution Context)

실행 컨텍스트실행할 코드에 제공할 환경 정보들을 모아놓는 객체를 의미한다.
-> 객체란 실세계의 사물이나 개념을 소프트웨어 세계 내 추상화한 것을 의미한다.

JS는 동일한 환경에 있는 정보들을 모은 실행 컨텍스트를 콜스택에 쌓아올린 후 실행하여 코드의 환경과 순서를 보장할 수 있게 된다.

  • 스택(Stack)이란? 출입구가 하나뿐인 깊은 우물 같은 데이터 구조를 의미하며 비어있는 스택에 순서대로 a, b, c, d를 넣는다고 가정하면 꺼낼때는 d, c, b, a의 순서로 꺼내게 된다.
    => 스택을 사용하는 이유는 스택의 대표적인 특징인 (FILO(선입후출) : First in Last Out)가 순서를 보장해주고, 콜스택 내부에 쌓인 실행 컨텍스트의 정보를 통해 환경을 보장할 수 있다.
    (환경은 전역 변수 혹은 함수 내부의 환경이 될 수 있다.)
    => 콜스택(Call Stack)이란 자바스크립트 코드가 실행되며 생성되는 실행 컨텍스트(Execution Context)를 저장하는 자료구조를 의미한다.

  • 하나의 실행 컨텍스트를 구성할 수 있는 방법으로 전역공간과 eval() 함수, 함수 등이 있으며 자동으로 생성되는 전역공간과 eval()함수를 제외하면 실행 컨텍스트를 구성할 수 있는 방법은 함수를 실행하는 것뿐이다.
    (ES6에서 블록에 의해서도 새로운 실행 컨텍스트가 실행된다)

  • eval()함수는 문자로 표현된 JavaScript코드를 실행하는 함수를 의미한다.
// 실행 컨텍스트와 콜스택
var a = 1;
function outer(){
 	function inner(){
      console.log(a);
      var a = 5;
    }
  	inner();
  	console.log(a);
}
outer();
console.log(a);

실행 컨텍스트(Execution Context)와 콜스택의 흐름

  • (1) 콜스택엔 전역 컨텍스트를 제외하곤 다른 컨텍스트가 없기에 전역 컨텍스트와 관련된 코드를 진행한다.

  • (2) 전역 컨텍스트와 관련된 코드를 진행 중 outer()함수를 호출함으로써 outer에 대한 환경 정보를 수집하여 outer 실행 컨텍스트를 생성한 후 콜스택에 담는다.
    콜스택의 맨 위에 outer 실행 컨텍스트가 있기 때문에 전역 컨텍스트와 관련된 코드실행은 일시 중단하고 outer 실행 컨텍스트와 관련된 코드, 즉 outer 함수 내부의 코드들을 순차로 실행한다.

  • (3) inner 함수의 실행 컨텍스트가 콜스택 가장 위에 담기면 outer 컨텍스트와 관련된 코드의 실행을 중단하고 inner 함수 내부의 코드를 순서대로 진행한다.

  • (4) inner 함수 내부에 a 변수에 값 5를 할당하고 나서 inner 함수의 실행이 종료되면서 inner 실행 컨텍스트가 콜스택에 제거된다. 제거된 후 아래에 있던 outer 컨텍스트가 콜스택의 맨위에 존재하게 되고 중단했던 outer() 함수를 실행한다.

  • (5) a변수의 값이 출력되면 outer 함수의 실행이 종료되어 outer 실행 컨텍스트가 콜스택에서 제거되고, 콜스택에는 전역 컨텍스트만 남아 있게 된다.

  • (6) 실행을 중단했던 전역 컨텍스트를 실행한다. a변수의 값을 출력하고 나면 전역공간에 더는 실행할 코드가 남아 있지 않아 전역 컨텍스트도 제거되어 콜스택에 아무것도 남아있지 않는 상태로 종료된다.

실행 컨텍스트(Execution Context) 내부 정보

  • VariableEnvironment
  • LexicalEnvironment
  • ThisBinding

VariableEnvironment와 LexicalEnvironment는 현재 컨텍스트 내 식별자들에 대한 정보 + 외부 환경 정보, 선언 시점의 스냅샷으로, VariableEnvironment는 변경사항이 반영되지 않는 초기 상태를 유지, LexicalEnvironment는 변경사항이 반영된다.

VariableEnvironment와 LexicalEnvironment의 구성은 매개변수명, 변수의 식별자, 선언한 함수의 함수명 등을 수집하는 environmentRecord와 바로 직전 컨텍스트의 LexicalEnvironment 정보를 참조하는 outerEnvironmentReference로 구성된다.

스냅샷(SnapShot) : 사진을 찍듯이 특정 시간에 데이터 저장 장치의 파일 시스템을 포착해 별도의 파일이나 이미지로 저장 및 보관하는 기술을 의미한다.

ThisBinding : 식별자가 바라봐야 할 대상 객체


var 호이스팅(Hoisting)

  • 호이스팅은 코드가 실행하기 전에 변수 선언/함수 선언이 해당 스코프의 최상단으로 끌어올리는 현상을 의미한다.
  • var 변수/함수의 선언만 위로 끌어올려지며 할당은 끌어 올려지지 않는다.
console.log(num);
var num = 10;

// 실행결과 : undefined

// 실제 호이스팅에 의해 다음 코드처럼 작동함
var num; // 호이스팅으로 선언이 최상위로 올라가 있음
console.log(num);
num = 10; // 할당은 원래 자리에 있음

let 호이스팅(Hoisting)

  • var와 마찬가지로 let 선언은 맨 위로 끌어올려진다.
  • undefined(정의되지 않음)으로 초기화되는 var와 다르게 let의 키워드는 초기화되지 않는다. 선언 이전에 let 변수를 사용하려고 시도한다면 Reference Error(참조 오류)가 발생한다.

실행 컨텍스트에 따른 호이스팅된 변수값

  • outerEnvironmentReferenece는 해당 함수가 선언된 위치의 LexicalEnvironment를 참조한다.
  • 코드 상에서 어떤 변수에 접근하려고 하면 현재 컨텍스트의 LexicalEnvironmnet를 탐색해서 발견되면 그 값을 반환하고, 발견하지 못할 경우 다시 outerEnvironmentReferenece에 담긴 LexicalEnvironment를 탐색하는 과정을 거친다.
  • 전역 컨텍스트의 LexicalEnvironment까지 탐색해도 해당 변수를 찾지 못하면 undefined를 반환한다.

TDZ(Temporal Dead Zone)

TDZ가 생긴 이유🤔

  • var를 사용해서 변수를 선언하고 변수가 함수나 전역 스코프의 맨위로 호이스팅된다. => 그 결과, 변수를 선언하기 전에 변수를 접근할 수 있다. (undefined)
  • let, const와 함께 Teporal Dead Zone(TDZ)가 등장했다.
  • TDZ는 변수들이 선언되기 전에는 접근하지 못하게 해주었다.

TDZ를 알아야 하는 이유🤔

  • 선언 전에 변수를 식별하고 액세스하는 것을 방지해 준다.
  • 예를 들어 변수 값이 불일치하는 경우 같은 변수 호이스팅과 관련된 잠재적인 문제를 파악할 수 있어 안정적이고 예측 가능한 코드를 작성할 수 있다.

TDZ(Temporal Dead Zone)은 let, const, class로 선언된 변수가 실제로 선언되고 초기화되기 전까지의 코드 영역을 의미한다.
이 구간에서 해당 변수에 접근하려고 하면 'ReferenceError'가 발생한다.

console.log(x); //TDZ 발생 : Reference Error, x가 초기화되기 전까지 'x'에 접근할 수 없다.
let x = 10; // TDZ 끝
console.log(x); // 10

함수(function)

  • 어떤 일을 수행하는 명령들을 하나로 묶어서 이름을 부여하고, 필요할 때마다 그 함수를 호출해서 사용할 수 있는 프로그래밍 방법 중 하나다.
  • 반복적으로 사용되는 일을 함수로 만들어 놓으면 일의 순서나 방법을 다시 기억할 필요없이 편리하게 사용할 수 있다.
  • 동일한 입력값에 동일한 결과값이 나오는 실행 블록문을 의미한다.

함수를 사용하는 이유🧐

  • 코드의 재사용성 : 동일한 코드 블록을 여러 곳에서 호출하여 사용하며 코드의 불필요한 중복을 피하고 유지보수성 증가한다.
  • 모듈화 : 프로그램을 더 작은 단위로 나눌 수 있다. 프로그램의 복잡성을 줄이고 코드를 논리적으로 구성한다.
    import : export를 통해 내보내는 진 것을 가져다가 사용하는 것을 의미한다.
    export : 자신이 만든 모듈의 함수, 객체, 변수를 다른 모듈에서 가져다가 사용할 수 있도록 내보낼 때 사용한다.
  • 유지보수성 : 코드를 논리적으로 분할하고 구성할 수 있어 이해와 수정이 편리하다.
  • 코드의 가독성 : 함수의 이름을 통해 작업 목록을 직관적으로 이해할 수 있으며, 코드를 읽기 쉽고 이해하기 쉽도록 만든다.
  • 테스트 용이성 : 동일한 입력에 대해 항상 동익한 출력을 보장하기 때문에 특정 함수의 테스트 케이스 작성에 용이하다.

함수 선언문, 함수 표현식, 화살표 함수

  • 함수 선언문이란 function키워드를 사용해서 이름이 있는 함수를 선언하고 정의한다. 해당 함수는 호이스팅에 의해 스코프 내 어느곳이든 호출이 가능하다. 식별자 뒤에 소괄호를 붙인다.
// 기본틀
function 식별자(){
}
// 예제코드
function gugudan(){
  for(let i = 1; i <= 9; i++){
    console.log('3 * ${i} = ${3 * i}');
  }
}
  • 함수 표현식이란 함수를 변수에 할당하는 방식으로 function 키워드를 사용해 변수에 할당할 수 있는 식을 의미한다. 호이스팅이 발생하지만 함수가 할당되기 전까지는 호출할 수가 없다.
  • 함수 표현식은 함수에 식별자가 있으면 네이밍 함수(naming function), 없으면 익명함수(anonymous function)로 다시 구분한다.
  • 함수 표현식은 선언문과 달리 function 키워드 뒤에 오는 식별자로 호출하지 않고, 할당한 변수명으로 호출한다. 그렇지 않으면 ReferenceError : naming is not defined 에러가 발생한다.
//함수표현식 사용 방법
const 변수명 = function(){}; // 익명함수
const 변수명 = function 식별자(){}; // 네이밍 함수
// 예제코드 네이밍 함수
const gugudan = function gugudan(){
  for(let i = 1; i <= 9; i++){
  	console.log('3 * ${i} = ${3 * i}');
  }
}; // 변수에 할당하기 때문에 ;(세미콜론)을 붙여준다.
gugudan(); // 함수 호출
// 예제코드 익명함수 
const gugudan = function(){
  for(let i = 1; i <= 9; i++){
  	console.log('3 * ${i} = ${3 * i}');
  }
}; // 변수에 할당하기 때문에 ;(세미콜론)을 붙여준다.
gugudan(); // 함수 호출
// 식별자로 함수를 호출할 경우 에러 발생하는 예제 코드
const gugudan = function naming(){
  for(let i = 1; i <= 9; i++){
  	console.log('3 * ${i} = ${3 * i}');
  }
}; // 변수에 할당하기 때문에 ;(세미콜론)을 붙여준다.
naming(); // 함수 호출
  • 화살표 함수란 function키워드를 생략하고 => 기호를 사용하여 함수를 더욱 간결하게 표현하는 방식으로 ()와 return을 생략할 수 있다.
  • 익명함수로만 정의할 수 있기 때문에 함수를 호출하려면 정의된 함수를 변수에 할당하는 방법(함수표현식)을 사용해야 한다.
//화살표 함수 표현식 방법
()=>{};
// 예제코드 화살표 함수
const gugudan = () => {
 for(let i = 1; i <= 9; i++){
	console.log('3 * $ {i} = ${3 * i}'); 	
 }
};
gugudan();

함수 선언문 VS 함수표현식 및 화살표 함수의 차이점

함수 선언문으로 선언된 함수는 함수 호이스팅이 발생한다. 런타임 이전에 함수 객체가 생성되므로 함수가 정의도기 전에 함수를 호출할 수 있다.

함수 표현식으로 선언된 함수는 변수 호이스팅이 발생한다. 그렇기 때문에 undefined로 초기화가 되며 함수 표현식으로 선언한 함수는 함수가 정의되기 전에 함수를 호출할 수 없다.

profile
천천히, 꾸준히, 그리고 끝까지

0개의 댓글