HyeGyeong
HyeGyeong 프로그래머를 꿈꾸며 JavaScript 공부 중

IIFE-즉시실행함수표현식 (Immediately Invoked Function Expressions)

IIFE-즉시실행함수표현식 (Immediately Invoked Function Expressions)

IIFE(Immediately Invoked Function Expressions)

1. IIFE이란?

한국어로 ‘즉시 호출 함수 표현식’라 한다.
기본 형태는 다음과 같다.

1
2
3
(function() {
  // Do fun stuff
})();

IIFE를 이해하기 위해서는 함수의 선언식(Declaration)과 함수의 표현식(Expression)의 차이점을 먼저 알아야 한다.
Mozilla Developer 사이트에서는 선언식과 표현식은 동일한 문법을 가지고 있지만 표현식에서는 함수명이 생략될 수 있다고만 기술하고 있다. 하지만 사용을 해보면 더 뚜렷한 차이가 있다. 호이스팅 영향의 유무이다.

호이스팅(hoisting: 감아올리다)이란? 변수의 정의가 그 범위에 따라 선언과 할당으로 분리되는 것을 의미한다. 변수의 선언이 최상위로 불려지고 그 변수에 할당되는 것은 나중에 이루어진다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
foo1(); // 성공!
function foo1() {
  // 선언식
  alert('foo');
}

foo2(); // 'foo2' is not defined.
var foo2 = function() {
  // 표현식
  alert('foo');
};
foo2(); // 성공!

alert(foo3); // 'foo3' is not defined.
(function foo3() {}); // 함수를 ()로 감싸줌.
alert(foo3); // 'foo3' is not defined.

괄호쌍이 익명의 함수를 감싸서 함수 선언(declaration)을 함수 표현식(expression)으로 표현할 수 있다.

1
2
3
4
5
6
(showName = function(name) {
  // 선언, 할당
  console.log(name || 'No Name');
})(); // 실행, No Name
showName('Betty'); // Betty
showName(); // No Name

‘showName’함수는 선언과 동시에 실행이 되고, 이름이 있기 때문에 나중에 호출할 수도 있다.

1
2
3
4
(function(name) {
  // 익명함수 선언
  console.log(name || 'No Name');
})(); // 실행, No Name

하지만 위의 경우 나중에 다시 호출할 수 없다. 부를 수 있는 방법이 없기 때문이다. ‘showName’과 익명함수의 뒤에 있는 두개의 괄호는 JS컴파일러에게 이 함수를 바로 호출하라고 요청한다. 이것을 IIFE라 한다.

IIFE를 사용하는 주된 이유는 스코프 문제를 해결하기 위함인데, 내가 IIFE를 사용한 부분 역시 순환문 내의 스코프 문제를 해결하기 위함이었다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
describe('China UnionPay', function() {
  var expect = chai.expect;
  var str;
  var prefixChk = function(start, end) {
    for (var i = start; i <= end; i++) {
      for (var j = 16; j <= 19; j++) {
        str = '1'.repeat(j - String(i).length);
        (function(i, str) {
          // IIFE
          // console.log(i+str);
          it(
            'has a prefix of ' + i + ' and a length of ' + String(j),
            function() {
              expect(detectNetwork(i + str)).to.equal('China UnionPay');
            }
          );
        })(String(i), str); // IIFE에 현재의 값을 바로 넘김
      }
    }
  };
  prefixChk(622126, 622925);
  prefixChk(624, 626);
  prefixChk(6282, 6288);
});

위의 경우 중첩된 for문에서 ()로 둘러싸인 함수에 따로 인수를 보내주지 않으면 for문을 모두 돌고 난 후 빠져나온 값이 들어간다. 굉장히 헷갈리고 이해하기 어려운 부분이지만 많이 사용하고 익숙해지는 수밖에 없을 것 같다. 전역에 변수를 추가하지 않아도 코드 충돌 없이 구현 가능하기 때문에 플러그인이나 라이브러리 등을 만들 때 많이 사용된다고 한다.