JS. 함수 표현식

MJ·2022년 8월 29일
0

Java Script

목록 보기
24/57
post-thumbnail

함수 표현식

  • 함수를 선언하는 방법은 2가지가 있습니다. 함수 선언문과 함수 표현식
    javascript에서는 함수를 하나의 값으로 취급합니다.

  • 함수 선언 이외에도 함수를 표현식으로 생성하는 것을 살펴보겠습니다.


1.1 함수 선언문과 표현식의 차이

// 1. 함수 선언문 
function sayHi() {
 alert('Hello'); 
}


// 2. 함수 표현식 ( 함수를 변수에 할당 )
let sayHi = function() {
 alert('Hello'); 
}

alert(sayHi);


/* 
함수 표현식 : let sayHi = function() { alert('Hello'); }
1) sayHi 변수에 익명 함수를 저장 합니다. ( 이름 없는 함수를 익명 함수라고 한다 )
2) 변수에 함수를 할당하면, 하나의 값으로 저장 됩니다. 함수의 코드가 저장됨
3) 변수에 저장된 함수 코드는, 변수를 호출하면 함수 기능이 수행 됩니다.
*/


alert(sayHi);
1) sayHi 변수를 출력합니다. sayHi 변수에는 함수코드가 들어있으므로, function () { ~~~ } 
   코드 자체가 값으로 출력 됩니다.
2) 함수도 하나의 값으로 이루어져 있다고 확인할 수 있습니다.


/* */
실제 함수처럼 동작하게 하려면 함수를 저장한 변수에 () 괄호를 붙여주면 됩니다.
sayHi() → sayHi 변수에 저장된 함수가 호출됩니다. 
alert('Hello')가 동작된다.

코드 확인


1.2 변수에 함수 삽입

function sayHi() {	// 함수 선언문으로 생성
 alert('Hello');
}				

let func = sayHi;	// func라는 변수에 sayHi 함수의 값을(코드부분) 할당

func();				// alert창에 Hello가 출력된다.
sayHi();			// 위와 동일하다.


/* 해석 */
let func = sayHi;
1) func 변수에 sayHi 함수가 갖고 있는 (코드)를 할당합니다.

func();	
1) func 변수를 호출 합니다. > func 변수에는 sayHi 함수의 코드가 값으로 할당 되어 있다.
2)sayHi() 함수가 호출 됩니다. > alert('Hello') 출력

sayHi();
1) sayHi 함수를 호출하므로 func()와 동일하게 작동합니다.


/* */
함수도 하나의 값이여서 변수에 저장할 수 있다.

1.3 변수에 함수가 할당되는 동작 원리

let sayHi = function() {	// 익명 함수의 코드를 sayHi 변수에 저장
  alert( "Hello" );
};

let func = sayHi;			// 익명 함수의 코드를 func 변수에 저장


/* */
함수 표현식 방식으로, 변수에 함수를 저장할때는 구문의 끝에 ';' 세미콜론을 붙여야 한다.
함수 선언문 방식은 상관 없다.

⚠️ 함수명이 아니라 함수를 호출해서 변수에 할당하면?

위 예제에서 func 변수에 sayHi()함수자체를 할당해버리면 어떻게 될까요?
sayHi() 함수가 호출되고 함수의 반환 값이 func변수에 저장될 것 입니다.

let func = sayHi(); // undefined
// sayHi 함수는 return 지시자가 없으므로 함수가 종료되면서 undefined를 반환 합니다.
// 즉, func 변수는 undefined 값이 저장됩니다.
*
*
*
*
alert(func);	// undefined 출력
func();			// 오류 발생



고차 함수

  • 다른 함수안에서 작동하는 함수를 고차 함수라고 합니다.

  • 함수를 인수로 받아서, 함수로 어떤 작업을 하는 함수.

function callTwice(func){
    func();
    func();
}

function rollDie(){
    const roll = Math.floor(Math.random() * 6) + 1;
    console.log(roll);
}

callTwice(rollDie);	// 난수 값이 2번 출력됩니다.


/* 
callTwice 함수를 호출할 때, 인수 값으로 rollDie() 함수에 해당되는 코드를 값으로 전달하면
func 매개변수로 인해 rollDie() 함수가 2번 호출하게 됩니다.
*/

2.1 고차함수 반복문 에서 사용

function callTen(func){
	for(let i=0; i<10; i++){
		func();	// 10번 출력
    }
}

function rollDie(){
    const roll = Math.floor(Math.random() * 6) + 1;
    console.log(roll);
}

callTen(rollDie);	// 난수가 10번 출력된다.



반환 함수

  • 익명함수를 사용해서 함수의 반환값을 여러 변수에 넣어서 사용할 수 있다.
function makeMysteryFunc() {
    const rand = Math.random();

    if (rand > 0.5) {
        return function () {
            console.log('Good');
        }
    }
    else {
        return function () {          // 익명함수를 반환
            console.log('Bad');
        }
    }
}

const Mystery = makeMysteryFunc();
Mystery();


/* 
1. makeMysteryFunc 함수 호출
2. rand 변수의 값을 확인하고 조건문과 비교한다.
3. 난수의 값이 if문의 조건에서 참이라면, return 지시자로 인해 익명함수의 값이 반환된다. (함수 자체의 코드)
4. 반환된 값(함수)을 변수 Mystery에 저장한다.
5. Mystery 변수를 호출하면, 익명함수를 실행할 수 있다.
*/

3.1 인수값을 넘겨서 함수 반환받기

/* 함수를 만들어주는 함수 만들기 (팩토리 함수) */

function makeBetweenFunc(min, max){
    return function(num){
        return console.log(num >= min && num <= max);
    }

}

const isChild = makeBetweenFunc(0, 18);
isChild(17);    // true

const isAdult = makeBetweenFunc(19,60);
isAdult(61);    // false


/* 
반환 함수를 통해서, 여러가지 조건의 상황에서 함수를 적재적소 사용할 수 있다.
동일한 함수를 사용해서 isChild와 isAdult변수에 각기 다른 결과값을 반환하는 함수를 만들었습니다.


1. makeBetweenFunc 함수를 호출
2. 함수는 인자값으로 2가지의 값을 전달받습니다.
3. 2가지 값을 매개변수로 저장하고, 익명함수를 반환한다.
4. 반환된 익명함수를 변수에 저장합니다
5. 변수에 저장된 함수를 인수값을 전달해서 호출하면, 익명함수의 num 값과 
변수에 저장했을 당시 함수의 min, max 매개변수의 값과 비교해서 결과 값을 논리형으로
출력합니다.
*/



콜백 함수

  • 인수로 전달된 함수를 함수 내부에서 다시 호출하는 것을 콜백함수라고 합니다.

  • 매개변수가 3개 있는 함수를 통해서 콜백 함수를 알아보겠습니다.

❤️ 콜백 함수 사용 

function ask(question, yes, no) {
  if (confirm(question)) yes()		// confirm 메서드의 결과가 참이라면, 매개변수 yes에 저장된 함수 호출
  else no();						// 메서드의 결과가 거짓이라면, 매개변수 no에 저장된 함수 호출
}

function showOk() {					// 콜백 함수
  alert( "동의하셨습니다." );
}

function showCancel() {				// 콜백 함수
  alert( "취소 버튼을 누르셨습니다." );
}

/* 함수 호출 : 3개의 인수를 전달 (문자열, 함수, 함수) */ 
ask("동의하십니까?", showOk, showCancel);


/* 해석 */
ask("동의하십니까?", showOk, showCancel);
1) ask 함수를 호출하고, 인수 값으로 문자열과 함수 2개를 전달.


function ask(question, yes, no)
1) 매개 변수로 전달된 인수 값을 저장 한다.
  question : "동의하십니까?"
  yes : showOk의 함수 코드
  no : showCancel의 함수 코드


if (confirm(question)) yes()		
else no();
1) 질문 메세지창을 출력하고, 메세지창에 출력되는 문자열에 question 매개변수를 전달. 
2) 사용자는 '동의하십니까?' 문자열을 확인하고, 확인 | 취소 버튼을 누를 수 있다.
3) 확인을 누를 경우 yes() => showOK 함수가 호출됨
4) 취소를 누를 경우 no() => showCancle 함수가 호출됨


/* */
confirm 함수는 사용자가 확인을 누르면 true의 값을, 취소를 누르면 false값을 반환한다.
그래서 if문의 조건으로 사용할 수 있다.

❤️ 콜백함수를 익명함수로 사용하기

function ask(question, yes, no) {	// 전달받은 인수를 저장
  if (confirm(question)) yes()		// yes() 호출하면, 첫 번째 익명함수가 호출된다
  else no();						// no() 호추하면, 두 번째 익명함수가 호출된다.
}

ask("동의하십니까?",
function { alert('동의하셧습니다'); },	// 첫 번째 익명함수, 함수를 생성해서 인수를 전달
function { alert('취소하셨습니다.'); }	// 두 번째 익명함수, 함수를 생성해서 인수를 전달
);


/* */
이 예제는 위 예제와 동일합니다.
함수를 호출하는 곳에서만 콜백함수를 사용할때는 익명 함수로 만들어서 사용하는게 가독성이 좋습니다.
익명 함수는 자신을 생성하고 호출한 `ask` 함수를 제외하고는 다른 곳에서 사용할 수 없습니다.

❗ 전역에서 함수를 생성한것이 아니기에, 외부에서 접근할 수 없는 캡슐화가 된다.

함수는 동작을 나타내는 값입니다.

문자열이나, 숫자등의 일반적인 값들은 데이터라고 합니다.
특정한 동작을 대변하는 값인 함수를 변수간에 전달할 수 있고, 동작이 필요한 경우에
변수에 저장된 값을 ( ) 괄호를 이용해 호출함으로써 값(동작)을 실행할 수 있는 함수가 됩니다.



함수 표현식과 선언문 차이점

  • 함수를 선언하는 방법과, 함수를 표현해서 변수에 저장하는 방법의 차이점을 살펴보겠습니다.
❤️ 차이점 1. 문법

// 함수 선언문
function sum(a,b) {
 return a + b; 
}

함수 선언문에서 함수는, 주요 코드 흐름 중간에 독자적인 구문형태로 존재한다.


// 함수 표현식 
let sum = function(a,b) {
  return a + b;
}

함수 표현식에서 함수는, 표현식이나 구문구성 내부에 생성됩니다. 
함수가 할당연산자를 통해서, sum 변수로 생성되었습니다.

❤️ 차이점 2. 함수의 생성 단계

[함수의 표현식], 실제 실행의 흐름이 해당 함수에 도달했을 때 함수를 생성합니다.

실행 흐름이 함수에 도달했을 때 부터 함수를 사용할 수 있는 것 입니다.

선언문과 표현식의 생성단계 차이점을 확인해보면, 함수 표현식은 스크립트가 실행되고

실행의 흐름이 `let sum = function()...`에 도달했을 때 함수가 생성됩니다.

이 때 부터 함수를 호출하고 인수로 전달하는 것이 가능합니다.

//

[함수 선언문], 선언문이 정의되기 이전부터 함수를 호출할 수 있습니다.

같은 코드블럭에 있다면 스크립트가 어느 위치던 상관없이 함수를 호출할 수 있습니다. (전역)

이러한 방식이 가능한 이유는, `Javascript`의 내부 알고리즘 때문 입니다.

스크립트를 실행하기 전, 준비단계에서 전역에 선언된 함수를 찾고, 해당 함수를 생성합니다. 

스크립트가 진짜로 실행되기 전 "초기화단계"에서 함수 선언방식으로 정의한 함수를 
생성하는 것 입니다.

❤️ 예시 

sayHi("John"); // Hello, John

function sayHi(name) {
  alert( `Hello, ${name}` );
}


/* */
위처럼 함수 선언문으로 작성된 함수는, 같은 코드블럭 이라면 어떤 위치에서도 함수를 호출할 수 있습니다.

--------------------------------------------------

sayHi("John"); // error!

let sayHi = function(name) {  // (*) 마술은 일어나지 않습니다.
  alert( `Hello, ${name}` );
};


/* */
함수 표현식으로 생성된 함수는, 자신을 생성한 스크립트 이후에 실행코드에서만 호출할 수 있어
위치에 따른 제한이 생깁니다. 실행 흐름이 매우 늦습니다.

❤️ 차이점 3. 스코프

엄격 모드(use strict)에서는 함수 선언문이 코드 블록 내에 위치하면 해당 함수는 블록 내
어디서든 접근할 수 있습니다. 

하지만 블록 밖에서는 함수에 접근하지 못합니다.

하지만 함수 표현식을 사용해서 전역에 변수를 미리 생성해두면, 다른 코드블럭의 함수를
사용할 수 있습니다. ( 물론 함수 표현식으로 함수가 선언된 이후에 호출해야 한다 )

❤️ 함수 선언문은 같은 함수가 코드블럭이 아니라면 호출할 수 없다.

let age = prompt("나이를 알려주세요.", 18);

// 조건에 따라 함수를 선언함
if (age < 18) {

  function welcome() {
    alert("안녕!");
  }

} else {

  function welcome() {
    alert("안녕하세요!");
  }

}

// if나 else문에서 생성한 함수를, 전역에서 호출하면 오류 발생
welcome(); // Error: welcome is not defined


/* */
사용자로부터 입력받은 값을 함수를 선언해서 비교합니다. 입력받은 값에 따라서 각각 다른 메세지창을
동작해야합니다. 그리고 마지막에 그 값을 자신을 호출한 함수에 반환합니다.

welcome() 함수는 if문 코드 블럭 안에서 생성되었습니다. if문 코드블럭을 벗어난 전역에서
welcome() 함수를 호출하면 오류가 발생합니다. 

함수 선언문 방식은 어떤 위치에서든 함수를 호출할 수 있지만, 호출의 첫 번째 조건은 함수와
함수를 호출하는 곳은 같은 코드 블럭에 존재해야 합니다. 

welcome(); 함수 호출은, if문을 벗어난 외부에서 호출했기 때문에 오류가 발생하는 것 입니다.

❤️ 함수 표현식은 스코프의 기준이 자유롭다
: 다만 실행 흐름 이후에 생성된 코드여야 한다.


let age = prompt('나이를 알려주세요', 18);

let welcome;	// 코드 블럭 밖에서 함수를 호출할 수 있게 변수 선언

if ( age < 18 ) {
  
 welcome = function() {	// 함수를 생성해서 welcome 변수에 할당합니다.
  alert('안녕'); 
 };
  
} else {
  
 welcome = function() {	// 함수를 생성해서 welcome 변수에 할당합니다.
 alert('안녕하세요 !');	
 };

}

welcome();	// 제대로 동작합니다.


/* */
함수 선언문은 같은 코드 블럭내에서만 접근이 가능하지만, 함수 표현식은 코드 블럭이 다르더라도
전역에 있는 변수를 통해서 다른 코드 블럭에 있는 함수를 접근할 수 있습니다.

------------------------------------------------------------

let age = prompt('나이를 알려주세요', 18);

let welcome = (age < 18) ? 
function() { alert('안녕'); }; :
function() { alert('안녕하세요 !'); }; 

welcome();


/* */
해당 코드는 IF문을 ? 연산자로 간결하게 만든 것 입니다.

🔔 함수 표현식과 선언문에 관한 정리

Q. 함수 선언문과 함수 표현식 중에서 어떤 것이 더 효율적인가?
A. 함수 선언문 사용을 고려해보는 것이 좋다. 함수 선언문으로 함수를 정의하면 함수가
생성된 위치에 상관없이, 어떤 위치에서든 함수에서 접근할 수 있기 때문이다.


또한, 코드의 가독성도 좋아진다. let value = function() { .. } 보다는
function value() { .. } 이 보기 편하다.

조건에 따라서 함수 표현식을 사용해야할 경우에는 함수 표현식을 사용합시다.



정리

함수는 값이다. 따라서 함수를 변수에 할당하거나 다른 변수에서 함수를 사용할 수 있다.


함수를 선언문 방식으로 정의하면, 독립된 구문 형태로 존재한다. 같은 코드블럭 내부라면
위치에 상관없이 어디서든 접근할 수 있다.

함수를 표현식 방식으로 정의하면, 같은 코드블럭 내부여도 함수를 생성한 코드라인 이후
에서만 접근할 수 있다. 하지만 코드 블럭 밖에서 표현식을 통해서 내부의 함수를 접근할
수 있다. ( 스코프의 기준을 자유롭게 한다 )


콜백 함수는, 함수 내부에서 다른 함수를 호출해서 사용하는 것을 의미한다.

profile
프론트엔드 개발자가 되기 위한 학습 과정을 정리하는 블로그

0개의 댓글