JavaScript - 함수 선언문과 함수 표현식, 함수 호이스팅과 변수 호이스팅

Juhyeong Kim·2022년 1월 3일
0

함수

함수 선언문과 함수 표현식, 함수 호이스팅과 변수 호이스팅의 차이를 확실하게 알아보자.

함수란?

함수는 입력된 값을 가공해서 결과물을 반환하는 코드 블럭이다.

함수를 사용하는 이유

  • 반복적인 작업을 동일한 코드로 여러 번 적다가 발생할 수 있는 실수를 방지할 수 있어 코드의 신뢰성이 높아진다.
let price = 0  // 물품 금액
let paidAmount = 0 // 지불한 금액
let change = 0   // 거스름돈 

price = 4800 
paidAmount = 10000
change = paidAmount - price // 5200 

price = 5500
paidAmount = 6000
change = paidAmount - price // 500

price = 400
paidAmount = 1000 
change = price - paidAmount // -600?? 실수로 반대로 빼서 완전히 잘못된 결과 반환
  • 특정 작업을 함수로 만들어 함수를 유용하게 재사용 가능
// 위의 반복되는 코드를 하나의 함수로 만들면서 실수 방지 가능
const changeCalculator = function(paidAmount, price) {
  return paidAmount - price;
}; 

let change = 0;

// 반복되는 동작을 하나의 함수로 만들어서 재사용
change = changeCalculator(10000, 4800); // 5200
change = changeCalculator(6000, 5500); // 500
change = changeCalculator(1000, 400); // 600
  • 반복적인 작업을 수정해야할 때 함수 하나만 수정하면서 유지보수의 편의성을 높여준다.
// 오늘 하루만 구매한 가격에 50% 할인 이벤트!!!!
const changeCalculator = function (paidAmount, price) {
  return paidAmount - (price * 0.5);
}; // 함수에서만 수정해주면서 아래 코드는 건드리지 않아도 됨.

let change = 0;

change = changeCalculator(10000, 4800); // 7600
change = changeCalculator(6000, 5500); // 3250
change = changeCalculator(1000, 400); // 800
  • 식별자에 할당한 이름만 보고 함수의 역할을 파악할 수 있어 코드의 가독성을 높여준다.
const changeCalculator = function (..., ...) {
 ...
 ...
}; // 아~ 거스름돈을 계산해주는 함수구나.

함수의 구성


자바스크립트에서 함수를 사용하려면 위의 그림처럼 두 가지 절차가 필요하다.

  • 함수 정의
  • 함수 호출

함수 정의를 먼저 자세히 알아보자.

함수 정의

함수를 사용하려면, 사용할 함수에 대해 미리 정의를 해야한다. 함수를 정의하는 방법에는 4가지가 있다.

  • 함수 선언문
function add(x, y) {
  return x + y;
}
  • 함수 표현식
const add = function (x, y) {
  return x + y;
};
  • Function 생성자 함수
const add = new Function('x', 'y', 'return x + y');
  • 화살표 함수
const add = (x, y) => x + y;

4가지 전부 함수를 정의하는 방법이지만 각 방법마다 조금씩 차이가 있다.

그 중에서 함수 선언문과 함수 표현식의 차이를 알아보자.

함수 표현식 vs 함수 선언문

자바스크립트의 함수는 객체이다. 객체를 객체 리터럴로 생성이 가능한 것 처럼, 함수도 함수 리터럴로 생성이 가능하다. 함수 리터럴은 function 함수이름(매개변수) {함수 몸체}로 표현된다.

함수 선언문, 함수 표현식 둘 다 함수 리터럴과 형태가 동일하다. 그러면 어떻게 함수 선언문과 함수 표현식을 구분하는지 한 번 알아보자.

// 함수 선언문
function add(x, y) {
  return x + y;
}

// 함수 표현식
const add = function add(x, y) {
  return x + y;
}

두 개 모두 함수 리터럴(function add(x, y) {...})로 만들어졌지만 자바스크립트가 문맥을 보고 선언문인지 표현식인지 판단하게 된다.

  • 함수 표현식
    변수를 선언을 하면 변수에는 값이 와야한다. 함수는 값처럼 변수에 할당할 수 있는 일급 객체이므로, 함수 리터럴로 생성한 함수 객체를 변수에 할당하면 함수 표현식으로 해석하게 된다. 함수 표현식일 경우 함수이름은 생략이 가능하고, 일반적으로 함수 표현식에서는 함수이름을 생략한다. 간혹 함수를 호출할 때 함수이름으로 호출한다고 생각할 수도 있는데, 함수는 함수이름으로 호출하는 게 아니라 함수 객체를 가리키는 식별자로 호출한다.
const add1 = function sum(x, y) {
  return x + y;
};

//함수이름을 생략한 함수 표현식
const add2 = function (x, y) {
  return x + y;
};

console.log(add1(1, 2)); // 3
console.log(sum(1, 2)); // ReferenceError: sum is not defined

console.log(add2(1, 2)); // 3
  • 함수 선언문
    함수 선언문은 function키워드로 시작하고 함수이름을 생략할 수 없다. 자바스크립트는 함수 선언문으로 해석하게 되면 함수이름으로 식별자를 암묵적으로 생성한다. 그렇기 때문에 함수를 선언하고 함수 외부에서도 호출할 수 있다.
// 함수 선언문
function add(x, y) {
  return x + y;
}
// 자바스크립트가 암묵적으로 식별자를 생성했기 때문에 함수 외부에서 호출가능
console.log(add(1, 2)); // 3

다시 한 번 말하지만, 함수 호출은 함수이름으로 하는 게 아닌 함수 객체를 가리키는 식별자로 호출한다.

함수 생성 시점과 호이스팅

console.log(add1(1, 2)); // 3
console.log(add2(1, 2)); // TypeError: add2 is not a function

function add1(x, y) {
  return x + y;
}
var add2 = function (x, y) {
  return x + y;
};

위의 코드를 실행해보면, 함수 선언문으로 정의한 함수는 함수 선언 전에 호출이 가능한 반면, 함수 표현식으로 정의한 함수는 함수 선언 전에 호출이 불가능하다.

이러한 차이가 나는 이유는 함수 생성 시점이 다르기 때문이다.

함수 생성 시점의 차이를 알아보기 전에 호이스팅에 대해 먼저 알아보자.

console.log(add1(1, 2)); // 3
console.log(str); // undefined

// 함수 선언문
function add1(x, y) {
  return x + y;
}
// 변수 선언문
var str = '호호'

함수 선언문과 변수 선언문을 정의하기 전에 식별자가 불려와지는 걸 볼 수 있다. 그 이유는 런타임 이전에 호이스팅이 발생했기 때문인데,

  • 함수 선언문
    런타임 이전에 자바스크립트 엔진에 의해 먼저 실행되면서 식별자가 생성되고, 함수 객체로 초기화가 된다.
  • 변수 선언문
    런타임 이전에 자바스크립트 엔진에 의해 먼저 실행되면서 식별자가 생성되고, undefined로 초기화가 된다.

결국 두 선언문은 런타임 이전에 식별자가 초기화되어 있어서, 선언문으로 함수와 변수가 정의되기 전에 식별자를 불러오는 게 가능하다.

이렇게 선언문이 코드의 선두로 끌어 올려진 것 처럼 동작하는 자바스크립트 고유의 특징을 호이스팅이라고 한다.

다시 함수 생성 시점의 차이가 나는 이유로 돌아오면,

console.log(add1(1, 2)); // 3
console.log(add2(1, 2)); // TypeError: add2 is not a function

function add1(x, y) {
  return x + y;
}
var add2 = function (x, y) {
  return x + y;
};

함수 선언문은 런타임 이전에 이미 함수 객체로 초기화가 되어 함수 정의 전에 호출할 수 있는 반면, 함수 표현식은 런타임 이전에 undefined로 초기화가 되었기 때문에 add2 is not a function이라는 에러가 발생하게 된다. 결국 함수 표현식은 런타임 도중 var add2 = function ... 위치에 왔을 때 함수가 정의되기 때문에 함수 선언문과 함수 생성 시점의 차이가 나는 것이다.

0개의 댓글