본문 바로가기
[코드잇]/JavaScript 백엔드 개발자

모던 자바스크립트 -함수-

by 수민띠 2023. 2. 22.

함수를 만드는 방법
1. 함수 선언(Function Declaration)
- function키워드로 함수를 선언하는 방식

function 함수이름(파라미터) {
  동작;
  return 리턴값;
 }


2. 함수 표현식(Function Expression)
- 함수 선언을 값처럼 활용해서 함수를 만드는 방식
- ex) 변수에 할당, 다른 함수의 아규먼트로 활용

const msg = function(){
  console.log('Hi');
}
msg();
//변수에 할당한 모습이 point가 아닌 값처럼 활용한다는 것이 point

+추가정보

표현식(Expression) - 결과가 하나의 값으로 표현되는 문장.  주로 식별자, 연산자, 리터럴 등으로 구성

장(Statement) - 프로그래밍에서 실행 가능한 최소의 코드단위. 한 줄이 될 수도 있고, {}으로 묶여 여러줄으로 표현

문장은 표현식을 포함한다. 결국 표현식은 문장의 한 형태


함수 선언과 함수표현식의 차이
1. 호이스팅
함수 선언 - 호이스팅으로 인해 함수 선언 이전에 함수호출 가능
함수 표현식 - 반드시 변수가 선언된 이후에 함수를 호출해야 정상적으로 동작
+추가 설명
함수 표현식은 일반적으로 변수에 할당해서 함수를 만들기 때문에 변수 특성상 변수 선언 이전에 접근할 수 없다.
var 키워드 변수가 호이스팅 되긴 하지만 선언문 자체만 호이스팅 되고, 할당된 값은 변수 선언 이후에 사용가능
2. 스코프
함수 선언 - 함수 스코프
함수 안에 선언된 함수는 함수 밖에서 호출할 수 없지만,
코드 블록에서 선언한 함수는 전역적으로 호출 가능
함수 표현식 - 할당된 변수에 따라 스코프 결정
var키워드로 선언한 변수에 할당 - 그 함수는 함수 스코프를 가지게 됨
let키워드로 선언한 변수에 할당 - 블록 스코프

각각의 장점
함수 선언
1. 변수선언과 함수선언을 명확하게 구분 가능
2. 자유로운 위치에서 함수 호출 가능
함수 표현식
1. 가독성 면에서 코드의 흐름을 쉽게 파악할 수 있음 (반드시 선언 이후에 호출할 수 있다는 점)
2. 변수의 스코프를 활용할 수 있음.

기명 함수 표현식(Named Function Expression)
함수 표현식으로 함수가 할당된 변수에는 자동으로 name이라는 프로퍼티가 생김.
이름이 없는 함수를 변수에 할당하면 변수의 name프로퍼티는 변수 이름을 문자열로 가지게 된다.

const a = function () {
  console.log('Hi');
};
console.log(a.name); // a

함수 표현식으로 함수를 만들 때 이름을 붙여줄 수 있는데, 이름이 있는 함수 표현식을 기명 함수 표현식이라고 부른다.
함수에 이름을 붙여주면 name프로퍼티는 함수 이름을 문자열로 가지게 된다.

const a = function testname() {
  console.log('Hi');
};
console.log(a.name); //testname

함수 이름은 외부에서 함수를 호출할 때는 사용할 수 없다.

const a = function testname() {
  console.log('Hi');
};
testname(); //ReferenceError


외부에서 호출할 수 없는데 왜 이름을 굳이 붙여 줄까?
기명 함수 표현식은 일반적으로 함수 내부에서 함수 자체를 가리킬 때 사용 된다.

/*아규먼트로 숫자 값을 전달하고 전달받은 값이
0이 될 때까지 자기 자신을 호출하는 재귀 함수를 함수표현식으로 작성.*/
let countdown = function(n) {
 console.log(n);
  if (n === 0) {
    console.log('End!');
  }else {
    countdown(n -1);
  }
 };
 
 countdown(5);

만약 이 함수를 복사하려고 다른 변수에 담았다가 countdown변수에 담긴 값이 변하게 되면 문제가 발생한다.

let countdown = function(n) {
  console.log(n);
  if (n === 0) {
    console.log('End!');
  } else {
    countdown(n - 1);
  }
};

let myFunction = countdown;

countdown = null;

myFunction(5); // TypeError

마지막 줄에서 myFunction함수를 호출했을 때, 함수가 실행되긴 하지만, 6번째 줄 동작을 수행할 때 호출하려는 countdown함수가 12번에서 null 값으로 변경되었기 때문에 함수가 아니라는 TypeError가 발생.
이런 상황을 방지하기 위해 함수 내부에서 함수 자신을 사용하려고 하면 함수표현식에서는 기명함수표현식을 사용하는 것이 안전함.

let countdown = function printCountdown(n) {
  console.log(n);
  if (n === 0) {
    console.log('End!');
  } else {
    printCountdown(n - 1);
  }
};

let myFunction = countdown;

countdown = null;

myFunction(5); // 정상적으로 동작


즉시 실행 함수(Immediately Invoked Function Expression, IIFE),,
함수 선언과 동시에 즉시 실행되는 함수
함수 선언 부분을 소괄호로 감싼 다음 함수를 실행하는 소괄호를 한 번 더 붙인다.

(function () {
  console.log('Hi');
})();

(function (x,y) {
  console.log(x + y);
})(10,20);

주의할 점은 함수에 이름을 지어주더라도 외부에서 재사용할 수 없다.

(function sayHi(){
  console.log('Hi')
})();
sayHi(); //ReferenceError

따라서 일반적으로 이름이 없는 익명 함수를 사용한다.(재귀적인 구조를 만들고자 할 때는 이름이 필요)

즉시 실행 함수 활용
즉시 실행 함수는 프로그램 초기화 기능에 많이 활용되거나,

(function init() {
  //프로그램이 실행될 때 기본적으로 동작할 내용들
})();

재사용이 필요 없는 일회성 동작을 구성할 때 활용한다.
ex) 함수의 리턴값을 바로 변수에 할당하고 싶을 때

const firstName = 'su';
const lastName = 'min';

const greetingMessage = (function () {
  const fullName = `${firstName} ${lastName}`;
  return `My name is ${fullName}`;
})();

즉시 실행 함수에서 사용하는 변수들은 함수 내에서만 유효하다. 활용하면, 일시적으로 사용할 변수의 이름들을 조금 자유롭게 작성 가능

값으로서 함수
typeof 연산자로 함수의 타입을 출력해 보면 function이 출력되지만 구체적인 타입은 객체이다.
console.dir()로 콘솔에 출력해 보면 조금 더 확실하게 알아볼 수 있다.
console.dir() - 개발자가 객체의 속성을 쉽게 얻을 수 있도록 지정된 JavaScript 객체의 모든 속성을 콘솔에서 볼 수 있는 방법

const sayhi = function() {
  console.log('Hi');
}

console.dir(0);
console.dir('sumin');
console.dir(true);
console.dir(null);
console.dir(undefined);
console.dir({});
console.dir([]);
console.dir(sayhi);

펼쳐 보면 여러 개의 프로퍼티를 가지는 객체 형태를 하고 있다.
즉, JS에서는 함수는 객체 타입의 값으로 평가된다.

값으로 평가되는 함수 활용
JS의 함수는 어디에서나 할당될 수 있고, 다양한 형태로 호출될 수 있다.

1. 함수표현식으로 함수를 선언 / 호출

const printJS = function () {
  console.log('JavaScript');
};
printJs();

2. 객체 안에 프로퍼티로 함수를 선언 / 프로퍼티에 접근해서 함수를 호출

const myObject = {
  name: 'sumin',
  sayHi: function(name){
    console.log(`Hello ${name}`);
  }
};
myObject.sayHi('world');

3. 배열의 요소로 함수를 선언 / 배열의 요소에 접근해서 함수를 호출 (흔하게 사용하지 않음)

const myArray = [
  'sumin',
  function() {
    console.log(`Hello`);
  }
];
myArray[1](); // Hello

4. 다른 함수의 파리미터로 전달

콜백 함수 - 다른 함수의 파라미터에 전달된 함수
ex) DOM 이벤트를 다룰 때 파라미터를 전달하는 부분에서 함수 선언

const myBtn = document.querySelector('#myBtn');

myBtn.addEventListener('click', function (){
  console.log('button is clicked!');
});

5. 함수를 호출할 때 미리 선언된 함수를 전달하면서 조건에 따라 함수가 나중에 호출되거나 호출되지 않는 동작을 구현할 수 있다. (콜백 함수 - getSuccess와 getFail)

function makeQuiz(quiz, answer, success, fail) {
  if (prompt(quiz) === answer) {
    console.log(success());
  }else{
    console.log(fail());
  }
};

function getSuccess(){
  return '정답';
};

function getFail(){
  return '오답';
};
const question = '5 + 3 = ?';
makeQuiz(question, '8', getSuccess, getFail);

(우)8 입력 결과

6. 어떤 함수의 리턴값을 함수로 지정
고차 함수(Higher Order Function) - 함수를 리턴하는 함수

function getPrintHi() {
  return function(){
    console.log('Hi');
  };
};
const sayHi = getPrintHi();
sayHi(); // Hi

고차함수는 위 코드처럼 변수에 호출된 값을 할당해서 활용하기도 하고, 특별한 경우 이중 괄호를 사용해서 고차 함수로 리턴되는 함수를 바로 호출할 수 있다.

function getPrintHi() {
  return function(){
    console.log('Hi');
  }
}
getPrintHi()(); //Hi