함수

Jeong Ha Seung·2021년 12월 30일

함수는 객체 타입의 값이다. 따라서 이름(식별자)를 붙일 수 있다.

함수 리터럴

함수 리터럴은 function 키워드,함수 이름,매개 변수 목록, 함수 몸체로 구성된다.

// 변수에 함수 리터럴을 할당
var f = function add(x, y) {
  return x + y;
};

함수는 객체지만 일반 객체와는 다르게 호출이 가능하다.

함수 정의

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

함수 선언문

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

// 함수 참조
// console.dir은 console.log와는 달리 함수 객체의 프로퍼티까지 출력한다.
// 단, Node.js 환경에서는 console.log와 같은 결과가 출력된다.
console.dir(add); // ƒ add(x, y)

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

함수 선언문은 함수 리터럴과 형태가 동일하지만 함수 이름을 생략할 수 없다.

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

함수 선언문은 표현식이 아닌 문이다. 콘솔에서 실행했을 때 완료 값으로 undefined가 출력된다. 만약 표현식이 문이라면 undefined 대신 표현식이 평가되어 생성된 함수가 출력될 것이다.

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

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

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

함수 이름은 함수 몸체 내에서만 참조할 수 있는 식별자다.

그래서 함수 몸체 외부에서는 함수 이름으로 함수를 참조할 수 없으므로 호출도 불가능하다.

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

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

함수는 함수 이름으로 호출하는 것이 아니라 함수 객체를 가리키는 식별자로 호출해야 한다.

함수 표현식

자바스크립트의 함수는 값처럼 변수에 할당할 수도 있고 프로퍼티 값이 될 수도 있으며 배열의 요소가 될 수도 있다. 이처럼 값의 성질을 갖는 객체를 일급 객체라 한다.

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

// 함수 객체를 가리키는 식별자로 호출
console.log(add(2, 5)); // 7

// 함수 이름으로 호출하면 ReferenceError가 발생한다.
// 함수 이름은 함수 몸체 내부에서만 유효한 식별자다.
console.log(foo(2, 5)); // ReferenceError: foo is not defined

함수 생성 시점과 호이스팅

// 함수 참조
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;
}; //함수 표현식으로 함수를 정의하면 함수 호이스팅이 발생하는 것이 아니라 변수 호이스팅이 발생한다.
함수 호이스팅 vs 변수 호이스팅

var 키워드를 사용한 변수 선언문과 함수 선언문은 런타임 이전에 자바스크립트 엔진에 의해 먼저 실행되어 식별자를 생성한다는 점에서 동일하다.

하지만 var 키워드로 선언된 변수undefined로 초기화되고
함수 선언문으로 생성된 식별자는 함수 객체로 초기화된다.

Function 생성자 함수

  • 생성자 함수: 객체를 생성하는 함수
var add = new Function('x', 'y', 'return x + y');

console.log(add(2, 5)); // 7
var add1 = (function () {
  var a = 10;
  return function (x, y) {
    return x + y + a;
  };
}());

console.log(add1(1, 2)); // 13

var add2 = (function () {
  var a = 10;
  return new Function('x', 'y', 'return x + y + a;');
}());

console.log(add2(1, 2)); // ReferenceError: a is not defined
//생성자 함수로  Return하면 ReferenceError 발생

arrow Function

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

함수 호출

매개변수와 인수

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

// 함수 호출
// 인수 1과 2는 매개변수 x와 y에 순서대로 할당되고 함수 몸체의 문들이 실행된다.
var result = add(1, 2);
function add(x, y) {
  return x + y;
}

console.log(add(2)); // NaN,에러가 발생하지는 않는다.
function add(x, y) {
  return x + y;
}

console.log(add(2, 5, 10)); // 7 ,초과된 인수 10은 버려지지는 않고 ,모든 인수는 암묵적으로 arguments 객체의 프로퍼티로 보내진다.

인수 확인

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

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

위 코드는 문법상 문제가 전혀 없으므로 아무런 이의제기 없이 코드를 실행할 것이다.

이러한 상황이 발생한 이유는 다음과 같다

  • 자바스크립트 함수는 매개변수와 인수가 개수가 일치하는지 확인하지 않는다.
  • 자바스크립트는 동적 타입 언어이기 때문에 매개변수의 타입을 사전에 확인 불가능하다.

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

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: 인수는 모두 숫자 값이어야 합니다.

인수가 전달되지 않은 경우 단축 평가를 사용해서 매개변수에 기본값을 할당하는 방법도 있다.

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에서 도입된 매개변수 기본값을 사용하면 인수 체크 및 초기화를 간단하게 할 수 있다.

매개변수 기본값은 매개변수에 인수를 전달하지 않았을 경우와 undefined를 전달한 경우에만 유효하다.

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

반환문

function multiply(x, y) {
  return x * y; // 반환문
}

// 함수 호출은 반환값으로 평가된다.
var result = multiply(3, 5);
console.log(result); // 15
function foo () {
  return; //return을 생략해도 console값은 undefined로 동일하다.
}

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

참조에 의한 전달과 외부 상태의 변경

// 매개변수 primitive는 원시값을 전달받고, 매개변수 obj는 객체를 전달받는다.
function changeVal(primitive, obj) {
  primitive += 100;
  obj.name = 'Kim';
}

// 외부 상태
var num = 100;
var person = { name: 'Lee' };

console.log(num); // 100
console.log(person); // {name: "Lee"}

// 원시값은 값 자체가 복사되어 전달되고 객체는 참조값이 복사되어 전달된다.
changeVal(num, person);

// 원시값은 원본이 훼손되지 않는다.
console.log(num); // 100

// 객체는 원본이 훼손된다.
console.log(person); // {name: "Kim"}

다양한 함수 형태

즉시 실행 함수

함수 정의와 동시에 즉시 호출되는 함수, 단 한 번만 호출되며 다시 호출 불가능하다.

// 익명 즉시 실행 함수
(function () {
  var a = 3;
  var b = 5;
  return a * b;
}());
// 기명 즉시 실행 함수
(function foo() {
  var a = 3;
  var b = 5;
  return a * b;
}());

foo(); // ReferenceError: foo is not defined

그룹 연산자 내의 기명 함수는 함수 리터럴로 평가되며 함수 이름은 몸체에서만 참조할 수 있는 식별자이므로 즉시 실행 함수를 다시 호출할 수는 없다.

콜백 함수

// 외부에서 전달받은 f를 n만큼 반복 호출한다
function repeat(n, f) {
  for (var i = 0; i < n; i++) {
    f(i); // i를 전달하면서 f를 호출
  }
}

var logAll = function (i) {
  console.log(i);
};

// 반복 호출할 함수를 인수로 전달한다.
repeat(5, logAll); // 0 1 2 3 4

var logOdds = function (i) {
  if (i % 2) console.log(i);
};

// 반복 호출할 함수를 인수로 전달한다.
repeat(5, logOdds); // 1 3

위 코드처럼 함수의 매개변수를 통해 다른 함수의 내부로 전달되는 함수를 콜백 함수 라 하며, 매개 변수를 통해 함수 외부에서 콜백 함수를 전달 받은 함수를 고차 함수라 한다.

0개의 댓글