본문 바로가기
Javascript

함수 선언식 && 표현식 && 호이스팅

by F.E.D 2018. 4. 17.

함수 선언식 && 표현식 && 호이스팅

함수 선언식

1
2
3
4
5
6
7
8
//Function Declaration(함수 선언식)
 
function FunctionalDeclare(){
    return 'My name is Function Declaration';
}
FunctionDeclare(); // Feed == > 'My name is Function Declaration'
 
 
cs


함수 선언은 함수의 정의를 나타내는 문장으로 해석된다.

따라서 코드해석에 따른 수행결과가 존재하지 않는다는 의미이기도 하다.

Statement라는 개념을 잘 잡고 가야하는데, 함수 선언문이 Statement라고 하는 말은 곧 코드 블럭 자체는 실행가능한 코드가 아니라는 것이다. 콘솔에서 아무리 실행해도 return 값이 없다. 따라서 java의 Class와 같이 호출을 해줘야 하는 수동적인 개념이라고 봐도 될 것 같다.


함수 표현식

1
2
3
4
5
6
7
//Function Expression(함수 표현)
 
var FunctionalExpress = function(){
    return 'My name is Function Expression';
}
FunctionalExpress(); // Feed == > 'My name is Function Expression'
 
cs



함수표현식은 Function Literal 이다.
함수표현식은 실행 가능한 코드로 해석 되어지거나 변수나 데이터구조에 할당되어지고 있음을 의미한다.
즉, 해당 코드블럭이 코드실행에 따른 결과값을 가지거나 특정 변수에 할당된 값으로 존재한다.

함수표현이 실행코드로서 해석되기 위해서는 ()를 이용하여 함수를 감싸 주어야 한다.
자기호출함수 라고도 한다. 
재귀함수와는 다른개념이며 재귀함수는 함수 안에 자신을 호출하여 반복호출 하지만 자기호출함수는 해석과 동시에 실행된다. 즉시실행함수IIFE(IIFE, Immediately Invoke Function Expression)라고도 한다. 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//anonymous function expression
var foo = function() {
    console.log('hello');
};
 
//named function expression
var foo = function foo() {
    console.log('hello');
};
 
// self invoking function expression
(function foo() {
    console.log('hello');
})();
cs

위 코드에서 named function expression는 foo 라는 변수에 foo 라는 함수를 할당하고 있다. 
해당 함수의 이름은 함수밖에서는 사용할수 없다. 이는 재귀호출외에는 사용하지 않는 편이다.
표현식은 콘솔에 찍어보면 자기호출함수를 이용하여 콘솔에서 실행결과를 볼 수 있다.

함수 표현식과 선언식은 함수호출방법에서는 큰 차이가 없지만 호이스팅 관점에서는 차이가 있다.

함수 선언식은 호이스팅에 영향을 받는다. 
일반적인 프로그래밍에서는 함수선언방식을 쓴다.
JS 특성상 사용할 수 있는 함수 표현식을 쓰는 것은 호이스팅에 영향을 받지 않는다.




호이스팅?

인터프린터가 자바스크립트 코드를 해석함에 있어서 전역 영역에 선언된 코드블럭을 최상위로 끌어올리는 것을 호이스팅이라 한다. 변수의 정의가 그 범위에 따라 선언과 할당으로 분리되는 것을 의미한다.
즉, 변수가 함수내에서 정의되었을 경우 선언이 함수의 최상위로, 함수 밖에서 변수가 선언되었을 경우는 전역 컨텍스트의 최상위로 바뀐다는 것입니다. 변수선언이 초기화할 시에 발생하는 것이 아니라 최상위로 호이스팅 되므
로 다음과 같은 상황을 볼 수 있습니다.

선언문은 항시 자바스크립트 엔진 구동시 가장 최우선으로 해석한다고 이해하면 이해가 쉬울지도 모르겠다.


1
2
3
4
5
6
7
8
9
10
11
// Hoisting
function Hoisiting(){
    console.log('myName: ' + name);
    var name = "YM";
    console.log('myNameIs : ' +  name);
}
Hoisting();
// myName : undefined
// myNameIs : YM
// myName이 undefined인 이유는 지역변수 name 이 호이스트 되었기 때문이다.
 
cs

위 함수 Hoisting을 해석해보겠습니다.

1
2
3
4
5
6
7
8
// 위의 함수 Hoisting 을 해석해봅시다.
function Hoisiting(){
    var name// name 변수는 호이스팅 됩니다. 할당은 이후에 발생, 따라서 이 때는 undefined로 찍힙니다.
    console.log('myName: ' + name); 
    var name = "YM"
    console.log('myNameIs : ' +  name);
}
Hoisting();
cs




호이스팅 될 때 함수 선언은 변수 선언을 덮습니다.

1
2
3
4
5
6
7
8
9
// 아래의 두 변수와 함수는 naming으로 이름이 같다.
var naming; // string
 
function naming(){
    console.log("YM");
//호이스팅이 되면서 함수선언은 변수명을 덮습니다.
console.log(typeof naming); // function
 
cs


반대로 변수에 값이 할당이 일단 되면, 변수가 함수선언을 덮습니다!!

1
2
3
4
5
6
7
var naming = "YM";
 
function naming(){
    console.log("YM");
}
 
console.log(typeof naming); //string
cs


"strict mode"에서는 변수를 선언하고 값을 할당하지 않은 상태로 typeof 할 시에 오류가 발생한다. 
함수선언이 변수선언을 덮기전에 strict(엄격하게 검사)하여 오류를 발생하는 것이므로
항상 변수선언을 할시에는 값을 할당하는 습관을 들이는 것이 좋다.


다시 돌아와서 함수표현식은 함수선언식과 달리 호이스팅에 영향을 받지 않는다.

당연히 함수표현식은 변수 선언과 동시에 함수를 생성하므로 호이스팅에 영향을 받지 않는다.
하지만, 함수 선언식은 브라우저가 자바스크립트를 해석할 때 자동으로 제일 위의 변수에 값이 있는지를 해석 한 다음 그에 따라 함수값이 변수선언을 덮거나 변수선언이 함수선언을 덮는 호이스팅이 일어나는 것이다.


1
2
3
4
5
6
7
8
9
10
11
//실행 전
logMyname();
sumNumbers();
 
function logMyname(){
    return 'YM';
}
 
var sumNumbers = function(){
    return 20 + 30;
};
cs


호이스팅으로 위의 자바스크립트를 해석하면 이렇게 해석된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
//실행 시
function logMyname(){
    return "YM";
}
 
var sumNumbers;
 
logMyname(); // "YM"
sumNumbers(); //Uncaught TypeError : sumNumbers is not a function
 
sumNumbers = function(){
    return 20 + 30;
};
cs

위 코드의 결과는 결과적으로 타입에러이다.

함수 표현식 sumNumbers 에서 var 가 호이스팅이 적용되어 상단으로 올라갔다.

실제 sumNumbers에 할당된 function은 호출 된 이후에 선언되므로, sumNumbers는 함수로 인식하지 않고 변수로 인식한다.

추가적으로 함수선언에서의 호이스팅, 함수표현에서의 호이스팅 예제를 보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
//함수 선언 호이스팅
foo();
function foo() {
    console.log('hi');
};
//hi
 
//함수 표현 호이스팅
foo();
var foo = function() {
    console.log('hi');
};
//Error 
cs

위 코드에서 볼 수 있듯이 함수 표현은 변수에 함수리터럴을 할당하기 때문에 Hoisting이 되지 않고 error가 발생한다. 이러한 이유로 선언문은 항상 최상위에 작성하는 것을 권고한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//문제1
function foo(){
    function bar() {
        console.log('hello');
    }
 
    return bar();
 
    function bar() {
        console.log('world');
    }
}
foo();
 
//문제2
function foo(){
    var bar= function() {
        console.log('hello');
    };
    return bar();
    var bar = function() {
         console.log('world');
    };
}
foo();
cs

위의 2문제가 어떻게 해석되는지 확인해보자.

문제1의 답

1
2
3
4
5
6
7
8
9
10
function foo(){
    function bar() {
        console.log('hello');
    }
 
   function bar() {
        console.log('world');
    }
    return bar();
}
cs

전역 영역에 선언된 함수 foo()를 hoisting 하여 해석한다. 
이 때 foo의 선언문인 블럭 안의 내용 또한 같이 호이스팅 되어 올라간다.
return bar(); 라는 실행문은 런타임에서 해석되기 때문에 function bar로 선언된 함수 첫번째 두번째가 각각 해석되어지고 마지막으로 return 이 런타임 단계에서 해석된다.

문제2의 답

1
2
3
4
5
6
7
8
9
function foo(){
    var bar= undefined;
    var bar= undefined;
    bar= function() {
        console.log('hello');
    };
   return bar();
}
 
cs

문제2는 아예 실행조차되지 않는다.
함수 표현식으로 hoisting을 사용한 함수는 지양하는 편이 좋다고 한다.

표현식은 위와 같이 hoisting을 피하는 방법보다는 closure에서 유용하다고 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function tabsHandler(index) {
    return function tabClickEvent(event) {
        // 바깥 함수인 tabsHandler 의 index 인자를 여기서 접근할 수 있다.
        console.log(index); // 탭을 클릭할 때 마다 해당 탭의 index 값을 표시
    };
}
 
var tabs = document.querySelectorAll('.tab');
var i;
 
for (i = 0; i < tabs.length; i += 1) {
    tabs[i].onclick = tabsHandler(i);
}
 
cs

하지만 알아두면 모두에게 자양분에 되지 않을까 하는 생각이다 : )




원문을 포함하여 이글을 공유해주신 chanlee님께 감사드립니다.


호이스팅을 제대로 모르더라도 함수와 변수를 가급적 코드 상단부에서 선언하면, 호이스팅으로 인한 스코프 꼬임 현상은 방지할 수 있다.




출처 :
http://insanehong.kr/post/javascript-function/
https://joshua1988.github.io/web-development/javascript/function-expressions-vs-declarations/
http://chanlee.github.io/2013/12/10/javascript-variable-scope-and-hoisting/
http://javascriptissexy.com/javascript-variable-scope-and-hoisting-explained/(원문)


'Javascript' 카테고리의 다른 글

[JS] GITHUB이 Jquery를 삭제하다  (0) 2019.01.26
[JS] Map vs ForEach  (2) 2018.08.25
[JS] 8번째 데이터 타입 BigInt  (0) 2018.06.07
[jQuery] $.grep 과 $.map  (0) 2018.05.29
ES 2017 및 ES 2018에 대한 새로운 기능 (1)  (0) 2018.04.15

댓글