함수

일련의 과정을 문으로 구현하고 코드 블록으로 감싸서 하나의 실행 단위로 정의 한 것

// add는 함수이름, 괄호 안의 a와 b는 매개변수
function add(a, b) {
  return a + b; // 반환 값
}

// 위의 과정은 함수를 정의하는 과정이다

add(2, 3); // 함수 호출
// 2,3은 인수라고 부른다
  • 함수또한 값이며 여러개 존재 할 수 있으므로 함수를 구별하기위해 식별자인 함수이름을 사용할 수 있다

    • 식별자 네이밍을 통해 함수 내부의 코드를 이해하지 않고도 함수의 역할을 파악할 수 있다 (코드의 가독성을 높임)
  • 함수는 몇번이든 재 호출 할 수 있기 때문에 코드의 재사용성 측면에서 뛰어나고 그로인해 유지보수와 코드의 신뢰성 향상을 높이는 효과가 있음

함수 리터럴

const add = function add(a, b) {
  return a + b;
};
// 함수 리터럴은 function 키워드, 함수 이름, 매개변수 목록, 함수 몸체로 이루어짐

함수 리터럴에서 함수 이름은 함수 몸체에서만 참조 할 수 있는 식별자임

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

// 그룹 연산자의 피 연산자는 값으로 평가 될 수 있는 표현식이여아 하므로 위 코드는 함수 선언문이 아닌 함수 표현식으로 평가됨

add(2, 3); // ReferrenceError: add is not defined
// 함수 리터럴의 함수 이름은 함수 몸체내 에서만 참조 할 수 있으므로 호출 시 에러가 발생함

함수 정의

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

// 함수 표현식
const f = function add(a, b) {
  return a + b;
};

// Function 생성자 함수
const f1 = new Function('a', 'b', 'return a + b');

// ES6 화살표 함수
const f2 = (a, b) => a + b;

함수 선언문

  • 함수 선언문은 함수 이름을 생략 할 수 없음
  • 표현식이 아닌 문이기 때문에 콘솔에서 함수 선언문 실행 시 undefined를 출력함
const f = function add(a, b) {
  return a + b;
};

// 위 코드는 함수선언식을 변수의 값으로 할당하는 것 처럼 보이지만
// 자바스크립트 엔진이 위 함수를 기명 함수 리터럴로 해석하여 변수에 할당한다

자바스크립트 엔진은 함수 선언문을 해석해 함수를 할당할 함수의 이름과 동일한 식별자를 생성하고 거기에 생성된 함수객체를 할당한다

이로 인해 함수 선언문은 식별자에 할당되지 않았음에도 호출할 수 있다.

// 지금 까지의 함수 선언문의 내용을 의사코드로 표현해보자
const add = function add(a, b) {
  return a + b;
};

add(3, 5);
// 함수선언문의 함수이름을 동일한 이름을 가진 식별자에 자바스크립트 엔진이 할당하여 함수 객체를 가리키는 식별자를 호출 한다.

함수 표현식

  • 값의 성질을 가지는 함수를 일급 객체라고 함
const add = function (a, b) {
  return a + b;
};

// 함수 리터럴의 경우 함수 이름을 생략할 수 있음 (익명 함수)
// 일반적으로 함수 리터럴의 함수이름은 생략하여 사용함
add(3, 6); // 함수 호출시 함수이름이 아닌 함수 객체가 할당된 식별자로 호출함

함수의 생성 시점과 함수 호이스팅

함수 선언문으로 생성한 함수와 함수 표현식으로 선언한 함수는 생성시점이 다름

  • 함수 선언문이 코드의 선두로 끌어 올려진 것 처럼 풍작하는 것을 함수의 호이스팅이라고 함
  • 함수 표현식은 변수에 값을 할당하는 과정을 거치게 됨으로 할당문이 실행되는 시점에 평가되어 함수객체가 됨(변수 호이스팅)

Function 생성자 함수

const f1 = new Function('a', 'b', 'return a + b');

add(3, 5); // 8

생성자 함수로 선언한 함수는 클로저를 생성하지 않는 등 함수 선언문과 함수 표현식과는 다른게 동작함

const add = (function () {
  let a = 10;
  return new Function('x', 'y', 'return a + x + y');
})();

add(1, 2); // ReferrenceError: a is not defined

화살표 함수

ES6 에서 도입된 화살표 함수는 function 키워드 대신 화살표를 사용해 더 간결하게 함수를 선언 할 수 있다

// 항상 익명함수로 정의됨
const add = (a, b) => a + b;
  • 생성자 함수로 사용할 수 없음
  • this 바인딩 방식이 기존 함수와는 다름
  • prototype 프로퍼티가 없으며 arguments 객체를 생성하지 않음

함수호출

  • 함수는 함수를 가리키는 식별자와 한쌍의 소괄호의 함수 호출 연산자로 호출함
  • 함수 호출 연산자에는 인수를 담아 넘기며 ,로 구분하여 나열함
  • 함수가 호출되면 해당 함수로 실행흐름을 옮기고 매개변수에 인수가 할당되며 함수 몸체의 코드를 실행함

매개변수와 인수

  • 매개변수는 함수 선언 시 정의 하며 함수 몸체내부에서 변수와 동일하게 취금됨
  • 매개변수의 스코프는 함수 몸체 내부이므로 함수 외부에서 참조 할 수 없음
  • 매개변수와 인수의 개수는 동일하지 않을 수 있음 (동일 하지 않아도 에러가 발생하지 않음)
  • 초과된 인수는 함수몸체내의 arguments라는 프로퍼티로 보관됨
function add(a, b) {
  console.log(arguments); // [2, callee:f, Symbol(Symbol.iterator): f]
  return a + b;
}

add(2); // NaN
// 매개변수로 인수가 모두 전달되지 않았음에도 에러가 발생하지 않지만 함수의 의도대로 값을 반환하지 못함

인수확인

  • 자바스크립트는 매개변수와 인수의 개수가 동일한지에 대한 여부를 확인하지 않음
  • 동적 타입 언어이므로 매개변수의 타입을 사전에 지정할 수 없음

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

function changeVal(primitive, obj) {
  primitive += 100;
  obj.name = 'Kim';
}

let num = 100;
const person = { name: 'Lee' };

changeVal(num, person);

console.log(num); // 100; // 원시 값은 값에 의한 전달이 발생하기 때문에 전달받은 매개변수 primitive에 복사된 100이 전달되어 어떠한 부수효과도 발생하지 않음
console.log(person); // { name: "Kim" }
// 객체 값은 참조에 의한 전달이 발생하기 때문에 참조 값을 통해 객체를 변경하게 될 경우 원본에도 영향을 미치는 부수효과가 발생

원본 객체의 상태 변경을 막고 객체의 상태 변경이 필요한 경우에는 객체의 방어적 복사를 통해 원본 객체를 깊은복사를 하여 새로운 객체를 생성하고 재할당을 통해 교체함

외부 상태를 변경하지 않고 외부 상태에 의존적이지 않은 함수를 순수 함수 라고 함 순수함수를 통해 부수효과를 줄이고 프로그램의 안정성을 높이려는 방식의 프로그래밍을 함수형 프로그래밍 이라고 함

즉시 실행 함수

const result = (function (a, b) {
  return a + b;
})(3, 2);

result; // 5
  • 함수를 정의함과 동시에 호출되는 함수를 즉시 실행 함수라고 함
  • 한 번 호출되면 다시 호출할 수 없음
  • 기명 함수와 익명 함수 형태 모두 사용할 수 있으나 일반적으로 익명함수형태로 선언함
  • 반드시 그룹연산자() 로 감싸야 함
  • 즉시 실행 함수를 사용함 으로서 전역 스코프에 불 필요한 변수 선언 및 부수효과 발생을 방지 할 수 있음
  • 이미 존재하는 변수 및 함수와의 충돌을 방지하는 곳에서 쓰임

재귀 함수

  • 자기 자신을 호출하는(재귀 호출) 함수
  • 반복되는 처리가 필요할 시 반복문이 아닌 함수를 재귀적으로 호출하는 것으로 사용할 수 있음
function factorial(n) {
  if (n <= 1) return 1;

  return n * factorial(n - 1); // 함수 이름은 함수 몸체 내부에서 접근이 가능하기 때문에 자기 자신을 호출 할 수 있음
}
  • 자신을 무한적으로 호출하기 때문에 재귀 호출을 막기위한 탈출 조건이 반드시 필요하다

    • 무한 호출되면 스택 오버플로 에러가 발생함
  • 재귀함수는 반복문 없이 반복 처리를 구현할 수 있다는 점이 장점 이지만 무한루프에 빠질 수 있는 위험이 있기 떄문에 반복문보다 직관적으로 이해하기 쉬울 경우에 한정적으로 사용하는 것 이 바람직하다.

중첩함수

  • 함수 내부에 정의된 함수를 뜻함(내부 함수라고도 부름)
  • 중첩함수를 포함하는 함수는 외부함수
  • 외부함수 몸체 내부에 선언되기 때문에 외부함수 내부에서만 호출이 가능함
  • 외부함수를 돕는 헬퍼 함수의 역할을 함
  • 호이스팅이 발생할 수 있으니 if 문이나 for문등의 코드블록에서는 사용하지 않는 것을 추천
function outer() {
  let x = 1;

  function inner() {
    let y = 2;

    console.log(x + y); // 3
  }
  inner();
}

outer();

콜백 함수

function repeat(n, f) {
  for (let i = 0; i < n; i++) {
    f(i);
  }
}

const logAll = (n) => console.log(n);

repeat(5, logAll);
  • 함수의 매개변수를 통해 다른 함수의 내부에 전달되는 함수
  • 매개변수를 통해 콜백 함수를 전달받은 함수를 고차함수 라고 함
  • 콜백함수도 고차함수에 전달되어 헬퍼 함수 역할을 함, 대신 중첩함수와는 다르게 콜백함수는 외부에서 고차함수에 전달되기 때문에 자유롭게 교체 가능한 장점이 존재, 고차함수는 콜백 함수를 자신의 일부분으로 합성한다.
  • 고차함수에 의해 콜백함수가 호출되며 필요에 따라 콜백함수에 인수를 전달할 수 있음
document.getElementById('button').addEventListener('click', function () {});

setTimeout(function () {
  console.log('치킨');
}, 3000);

콜백함수는 함수형 프로그래밍 패러다임 뿐만아니라 비동기 처리(이벤트 처리, ajax, 타이머 함수 등)에서 활용되는 중요한 패턴이다.

순수함수와 비순수함수

  • 순수함수: 외부 상태에 의존, 변경하지 않는 부수효과가 없는 함수
  • 비순수함수: 외부 상태에 의존, 변경하는 부수효과가 있는 함수
// increment는 매개변수를 받아 값을 증가시킨 후 반환하는 순수함수
function increment(a) {
  return ++a;
}

let count = 0;
count = increment(count); // 1

// dcrement는 외부상태인 count에 의존하고 상태를 변경하는 비순수함수
function decrement() {
  return --count;
}

decrement(); // count === 0

함수형 프로그래밍은 순수 함수와 보조함수를 조합하여 외부 상태를 변경하는 부수효과를 최소화하여 불변성을 지향하는 프로그래밍 패러다임