[JavaScript] 함수(1)

Lpla

·

2021. 4. 18. 21:08

반응형

 

1. 함수란?

자바스크립트에서 함수는 가장 중요한 문법 중 하나이다.

함수란 일련의 과정을 문으로 구현하고 코드 블록으로 감싼 하나의 실행 단위로 정의한 것이다.

함수는 입력을 받아서 출력을 내보낸다. 이때 입력을 전달받는 변수를 매개변수(parameter), 입력을 인수(argument), 출력을 반환값(return value)이라고 한다.

 

함수는 함수 정의를 통해 생성되지만 정의만으로 함수가 실행되는 것은 아니다.

함수를 실행하기 위한 입력, 즉 인수를 매개변수를 통해 함수에 전달하면서 함수의 실행을 지시해야 한다. 이를 함수 호출이라고 한다.

함수를 호출하면 코드 블록에 담긴 문들이 실행되고 결과값을 반환(반환값)한다.

 

// 함수 정의
function add(x, y) {
  return x + y;
}

// 함수 호출
var result = add(2, 5);

// 함수 add에 인수 2, 5를 전달하면서 호출하면 반환값 7을 반환한다.
console.log(result); // 7

 

 

2. 함수를 사용하는 이유

함수는 동일 작업을 반복적으로 수행해야 할 때 효율적이다. 

코드 중복을 억제하고 재사용성을 높여 유지보수를 편리하게 하고 가독성을 향상시킨다.

 

var x, y, result;

x = 1;
y = 2;
reseult = x + y; // 3

x = 3;
y = 4;
result = x + y; // 7

x = 5;
y = 6;
result = x + y; // 11
function add(x, y) {
  return x + y;
}

add(1, 2); // 3
add(3, 4); // 7
add(5, 6); // 11

 

 

3. 함수 리터럴

리터럴은 사람이 이해할 수 있는 문자 또는 약속된 기호를 사용해 값을 생성하는 표기법을 말한다.

자바스크립트에서 함수는 객체 타입의 값이다. 따라서 숫자 값을 숫자 리터럴로 생성하고 객체를 객체 리터럴로 생성하는 것처럼 함수도 함수 리터럴로 생성할 수 있다.

함수는 객체지만 일반 객체와는 달리 함수는 호출할 수 있다.

 

함수 리터럴의 구성 요소는 다음와 같다.

 

구성 요소 설명
함수 이름 식별자 네이밍 규칙 준수해야함
함수 몸체 내에서만 참조할 수 있음
함수 이름 생략 가능(이름이 있으면 기명 함수, 없으면 익명함수)
메개변수 목록 0개 이상의 매개변수를 소괄호로 감싸고 쉼표로 구분
함수 몸체 내에서 변수와 동일하게 취급
함수 몸체 함수 호출에 의해 실행
식별자 네이밍 규칙
식별자는 특수문자를 제외한 문자, 숫자, 언더바(_), 달러($)를 포함할 수 있다.
단 첫글자는 문자, 언더바(_), 달러($)로 시작해야 한다.
예약어는 식별자로 사용할 수 없다.

 

 

4. 함수 정의

함수를 정의하는 방법에는 네 가지가 있다.

 

4-1. 함수 선언문

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

// 함수 호출
console.log(add(2, 5)); // 7

 

함수 선언문은 함수 이름을 생략할 수 없다.

 

// 함수 선언문은 함수 이름을 생략할 수 없다.
function (x, y) {
  return x + y;
}
// SyntaxError: Function statements require a function name

 

함수 선언문은 표현식이 아닌 문이다. 따라서 함수를 선언하면 undefined가 출력된다.

표현식이 아닌 문은 변수에 할당할 수 없다. 함수 선언문도 문이므로 변수에 할당할 수 없다.

하지만 함수 선언문이 변수에 할당되는 것으로 보이는 예시가 있다.

 

// 함수 선언문은 표현식이 아닌 문이므로 변수에 할당할 수 없다.
// 하지만 함수 선언문이 변수에 할당되는 것처럼 보인다.
var add = function add(x, y) {
  return x + y;
};

// 함수 호출
console.log(add(2, 5)); // 7

 

자바스크립트 엔진은 코드의 문맥에 따라 동일한 함수 리터럴을 함수 선언문으로 해석하는 경우와 함수 리터럴로 해석하는 경우가 있기 때문에 발생하는 현상이다.

함수 이름이 있는 함수 리터럴을 단독으로 사용하면 함수 선언문으로 해석하고, 함수 리터럴이 값으로 평가되어야 하는 문맥, 예를 들어 함수 리터럴을 변수에 할당하거나 피연산자로 사용하면 함수 리터럴 표현식으로 해석한다.

 

// 기명 함수 리터럴을 단독으로 사용하면 함수 선언문으로 해석된다.
// 함수 선언문에서는 함수 이름을 생략할 수 없다.
function foo() { console.log('foo'); }
foo(); // foo

// 함수 리터럴을 피연산자로 사용하면 함수 선언문이 아니라 함수 리터럴 표현식으로 해석된다.
// 함수 리터럴에서는 함수 이름을 생략할 수 있다.
(function bar() { console.log('bar'); });
bar(); // ReferenceError: bar is not defined

 

위 예제에서 foo는 함수 선언문으로 해석된다.

하지만 그룹 연산자 () 내에 있는 bar는 함수 선언문으로 해석되지 않고 함수 리터럴 표현식으로 해석된다.

그룹 연산자의 피연산자는 값으로 평가될 수 있는 표현식이어야 하고 함수 선언문은 피연산자로 사용할 수 없다.

 

4-2. 함수 표현식

자바스크립트의 함수는 객체 타입의 값이다.

값처럼 변수에 할당할 수 있고 프로퍼티 값이 될 수 있고 배열의 요소가 될 수도 있다. 이를 일급 객체라고 한다.

자바스크립트의 함수는 일급 객체이다. 즉 함수를 값처럼 자유롭게 사용할 수 있다는 의미다.

따라서 함수 리터럴로 생성한 함수를 변수에 할당할 수 있다. 이러한 함수 정의 방식을 함수 표현식이라 한다.

 

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

console.log(add(2, 5)); // 7

 

4-3. 함수 생성 시점과 호이스팅

자바스크립트 엔진은 함수 선언문의 함수 이름으로 식별자를 암묵적으로 생성하고 생성된 함수 객체를 할당하여 함수 표현식과 유사하게 동작한다.

하지만 함수 선언문과 함수 표현식은 차이점이 존재한다.

 

// 함수 참조
console.dir(add); // ƒ add(x, y)
console.dir(sub); // undefined

// 함수 호출
console.log(add(2, 5)); // 7
console.log(sub(2, 5)); // TypeError: sub is not a function

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

// 함수 표현식
var sub = function (x, y) {
  return x - y;
};

 

함수 선언문으로 생성한 함수는 함수 선언문 이전에 호출할 수 있다.

하지만 함수 표현식으로 생성한 함수는 함수 표현식 이전에 호출할 수 없다.

둘 사이의 함수 생성 시점이 다르기 때문이다.

모든 선언문은 코드가 한줄씩 순차적으로 실행되는 런타임 이전에 자바스크립트 엔진에 의해서 먼저 실행된다.

따라서 함수 선언문으로 함수를 정의하면 런타임 이전에 함수 객체가 먼저 생성되고 함수 이름과 동일한 이름의 식별자를 암묵적으로 생성하고 함수 객체를 할당한다.

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

함수 표현식은 변수에 할당되는 값이 함수 리터럴인 문이다. 따라서 함수 표현식으로 함수를 정의하면 함수 호이스팅이 아니라 변수 호이스팅이 발생한다.

 

4-4. Function 생성자 함수

자바스크립트가 기본 제공하는 Function 생성자 함수에 매개변수 목록과 함수 몸체를 전달하여 new 연산자와 함께 호출하면 함수 객체를 생성해서 반환한다.

하지만 이 방식은 일반적이지 않으며 바람직하지도 않다.

Function 생성자 함수로 생성한 함수는 클로저를 생성하지 않는 등 함수 선언문이나 함수 표현식으로 생성한 함수와 다르게 동작한다.

 

var add = new Function('x', 'y', 'return x + y');

console.log(add(2, 5)); // 7

 

4-5. 화살표 함수

ES6에서 도입된 화살표 함수는 function 키워드 대신 => 를 사용해 좀 더 간단히 함수를 선언할 수 있다.

화살표 함수는 항상 익명 함수로 정의한다.

 

// 화살표 함수
const add = (x, y) => x + y;
console.log(add(2, 5)); // 7

 

화살표 함수는 함수 선언문이나 함수 표현식을 완전히 대체할 수 없으며 차이점이 존재한다.

자세한 내용은 이번 시간에 다루지 않는다.

 

 

5. 함수 호출

5-1. 매개변수와 인수

 

함수를 실행하기 위해 필요한 값을 함수 외부에서 함수 내부로 전달할 필요가 있는 경우, 매개변수를 통해 인수를 전달한다.

인수는 값으로 평가될 수 있는 표현식이어야 한다.

인수는 함수를 호출할 때 지정하며, 개수와 타입에 제한이 없다.

 

함수는 매개변수의 개수와 인수의 개수가 일치하는지 체크하지 않는다.

일반적으로 매개변수와 인수의 개수를 같지만 그렇지 않아도 에러가 발생하지 않는다.

인수가 할당되지 않은 매개변수의 값은 undefined다.

 

function add(x, y) {
  return x + y;
}

console.log(add(2)); // NaN

 

매개변수보다 인수가 더 많은 경우 초과된 인수는 무시된다.

정확하게는 그냥 버려지는 것이 아니고 암묵적으로 arguments 객체의 프로퍼티로 보관된다.

function add(x, y) {
  return x + y;
}

console.log(add(2, 5, 10)); // 7

 

5-2. 인수 확인

function add(x, y) {
  return x + y;
}

console.log(add(2));        // NaN
console.log(add('a', 'b')); // 'ab'

일반적으로 위 add 함수는 숫자 타입의 인수를 받아 그 합계를 반환하는데 목적을 두고 있다.

하지만 인자를 적절히 입력하지 않아도 자바스크립트 문법상 어떠한 문제가 없기 때문에 의도했던 값과 전혀 다른 값이 반환된다.

따라서 함수를 정의할 때 적절한 인수가 전달되었는지 확인할 필요가 있다.

 

function add(x, y) {
  if (typeof x !== 'number' || typeof y !== 'number') {
    // 매개변수를 통해 전달된 인수의 타입이 부적절한 경우 에러를 발생시킨다.
    throw new TypeError('인수는 모두 숫자 값이어야 합니다.');
  }

  return x + y;
}

console.log(add(2));        // TypeError: 인수는 모두 숫자 값이어야 합니다.
console.log(add('a', 'b')); // TypeError: 인수는 모두 숫자 값이어야 합니다.

 

인수의 개수는 arguments 객체를 통해 확인하거나 단축 평가를 사용해 매개변수에 기본값을 할당할 수 있다.

 

function add(a, b, c) {
  a = a || 0;
  b = b || 0;
  c = c || 0;
  return a + b + c;
}

console.log(add(1, 2, 3)); // 6
console.log(add(1, 2)); // 3
console.log(add(1)); // 1
console.log(add()); // 0

 

ES6에서 도입된 매개변수 기본값을 사용하면 보다 간단하게 나타낼 수 있다.

 

function add(a = 0, b = 0, c = 0) {
  return a + b + c;
}

console.log(add(1, 2, 3)); // 6
console.log(add(1, 2)); // 3
console.log(add(1)); // 1
console.log(add()); // 0

 

5-3. 매개변수의 최대 개수

ECMAScript 사양에서는 매개변수의 최대 개수를 명시적으로 제한하고 있지 않다.

매개변수가 많아지면 함수를 호출할 때 전달해야 할 인수의 순서를 고려해야 하고 함수를 이해하기 어렵게 만든다.

따라서 최대 3개 이상을 넘지 않는 것을 권장한다.

 

 

5-4. 반환문

함수는 return 키워드와 표현식(반환값)으로 이루어진 반환문을 사용해 실행 결과를 함수 외부로 반환할 수 있다.

반환문은 함수의 실행을 중단하고 함수 몸체를 빠져 나간다. 따라서 반환문 이후에 다른 문이 존재하더라도 실행되지 않는다.

 

function multiply(x, y) {
  return x * y; // 반환문
  // 반환문 이후에 다른 문이 존재하면 그 문은 실행되지 않고 무시된다.
  console.log('실행되지 않는다.');
}

console.log(multiply(3, 5)); // 15

 

반환문은 return 키워드 뒤에 오는 표현식을 평가해 반환한다. 표현식을 지정하지 않으면 undefined가 반환된다.

반환문은 생략할 수 있으며 함수 몸체의 마지막 문까지 실행한 후 undefined를 반환한다.

 

function foo () {
  return;
}

console.log(foo()); // undefined

 


출처

모던 자바스크립트 Deep Dive(2020, 이웅모)

반응형