본문 바로가기

Javascript

[Javascript] IIFE(즉시 실행 함수 표현)

즉시 실행 함수 표현(IIFE, Immediately Invoked Function Expression)**은 정의되자마자 즉시 실행되는 Javascript Function - MDN

 

(function () {
    statements
})();

간혹 여러 프레임워크나 모듈 내부 구경하다보면 위와 같은 형태의 함수들을 보셨을겁니다.

위와 같은 함수 호출을 IIFE라고 부르는데, 어떻게 가능한지, 왜 쓰는지 그동안 의문이었는데, 이번 글을 통해 한번 알아보고자 합니다.

 

 

Function Declaration

먼저 자바스크립트에서 함수를 어떻게 선언하고 사용하는지 알아봅시다.

 

1. 함수 선언식(Function Statement) -> 실행

 

newYear(2023) // 'Hello 2023
function newYear(year){
	console.log(`Hello ${year}`)
}

가장 기초적인 사용법이네요! JS에서 처음 함수를 접할시 볼법한 사용법입니다.

컴파일러 입장에선 다음과 같습니다.

1. newYear함수의 선언식을 포착 -> 호이스팅을 통한 메모리 할당

2. newYear의 실행을 포착 -> 메모리에서 newYear함수 로딩 후 실행

 

2.함수 표현식(Function Expression) -> 실행

var newYear = function(year){
	console.log(`Hello ${year}`)
}
newYear(2023) // 'Hello 2023

화살표 함수(arrow function) 이용하시는 분들은 많이 써보셨을듯한 사용입니다.

컴파일러 입장에선 다음과 같습니다.

1. newYear 호이스팅

2. newYear 변수에 익명 함수에 대한 데이터를 할당

3. newYear 실행 

 

 

Getting to IIFE

지금까지 함수의 실행에 대해 살펴보았는데, 공통적으로 모두 실행은 별도로 이루어졌습니다. 그러면 선언과 동시에 실행은 할 수 없을까요?

 

표현식 사용 시, 함수 자체가 아닌, 함수의 실행 결과를 반환하면 될 것 같습니다.

var newYear = function(year){
	console.log(`Hello ${year}`)
}(2023) // Hello 2023

됩니다!!, 일단 IIFE의 정의를 만족했습니다만, 하나 아쉬운 점이 있습니다.

위 과정에선, newYear 변수가 사실상 의미가 없기 때문에 없애고 싶군요. 한번 지워봅시다.

 

function(year){ //error...
	console.log(`Hello ${year}`)
}(2023)

함수의 name attribute가 부재하다고 나오는군요..

그럼 함수의 이름을 적으면 되지 않을까 할 수 있지만, 그렇다면 해당 표현은 선언식이 되어버립니다.

선언식은 실행 문맥(execution context)의 생성 프로세스 때 파싱 후, 실행 프로세스 때는 스킵되기에, ()<-를 통한 호출을 하여도 애당초 넘겨져 버립니다.

왜냐면 함수의 실행은 실행 문맥의 실행 프로세스 때 야기되기 때문입니다. (뭔가 말장난 같습니다만.. 아닙니다)

function newYear(year){
	console.log(`Hello ${year}`)
}(2023)

//no error , but no execution

 

여기서 좀 우회책이 필요할 것 같습니다.

 

먼저 다음과 같은 표현을 봅시다.

912123123123; // valid expression

다른 언어에선 바로 에러 메세지 띄울 수 있겠으나, JS에선 올바른 문법입니다.

물론 메모리 할당 과정이 없긴 하지만, 문제 될 것이 없는 표현식이죠.

 

위 statement를 다르게 말하면 다음과 같이 표현할 수 있을 것 같습니다.

 

912123123123을 ???에 할당

 

그렇다면 말이죠, 저희가 하려는 작업을 표현식 비스무리하게 표현하여 실행할 수 있지 않을까요?

결국 지금 저희가 하려는 일에 에러가 뜨는 이유는, 저희 프로그램과 컴파일러 중간에 syntax parser가 저희가 하려는 작업이 선언식이라 예측하기에, 함수 표현식 형태로 만든다면, 우회할 수 있을것 같습니다.

 

그리고 그에 대한 가장 간단한 해결책은  "()"로 감싸는 행위입니다.

 

(function (year){
	console.log(`Hello ${year}`)
}(2023))

//Hello 2023

 

"()" 로 감싸는 행위에 대해 생각해 봅시다.

return (2*4)/2

1-(2+3)

(2+5)*(3+1)

"()" 안은  표현식이어야 하기에, syntax parser가 "(" 문자를 보는 순간 ")"문자가 발견되기 전까지 내용을 표현식으로 생각합니다. 따라서 위와 같은 IIFE가 가능해지는 것이지요.

 

 

Why IIFE?

그럼 IIFE를 왜 사용할까요?

여러 프레임워크나, 모듈 분석해보면 거의 공통적으로 index 부분에 다음과 같은 표현이 있습니다.

(function (global, factory) {
	......
})(this, factoryFunction)

umd라고 모든 환경에서의 모듈을 작성하는 방법인데 관련 글은 추후에 올리도록 하겠습니다.

 

중요한건 왜 사용하는 것이지요.

 

먼저 IIFE을 담고 있는 파일의 실행을, 컴파일러 관점에서 봅시다.

 

1. 전역 실행 문맥(Global Execution Context , 이하 GEC)생성

2. GEC를 실행 문맥 스택에 담음

3. GEC 실행 페이스 시, IIFE함수 실행

4. IIFE 함수 실행에 따른 실행 문맥 생성

5. 생성된 실행 문맥을 실행 문맥 스택에 담음

6. IIFE 함수 실행 문맥 실행 페이스 시작

 

위 같은 과정에서, 실행 문맥이 생성될 때, 고유의 변수 환경, scope가 생성됩니다.

 

즉,  IIFE의 내부에 있는 모든 변수들은 IIFE의 scope 안에서  존재하기에, 외부 환경과 분리됩니다.

 

예시를 볼까요?

 

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <script src='./test.js'></script>
  <script src='./iifes.js'></script>
</body>
</html>
//test.js

const name = 'dovi';
//iife.js

var name = 'justin'
console.log(name)

 

보시다시피, 에러가 뜹니다.

 

각 파일들의 name들은 동일하게 global scope에서 선언됬기에 충돌이 일어나는거죠. 따라서 이를 방지하기 위해 변수 환경을 분리할 필요가 있습니다.

 

따라서 다음과 같이 작성한다면 충돌을 피할 수 있겠습니다.

 

(function(){
  var name ='justin';
  console.log(name);
}()) //justin