영화 목록 사이트 제작 개인 프로젝트 하면서 사용하고 있었지만 이해가 제대로 되지 않은 채 사용한 부분이었던 함수를 다시 정리해보려한다.

1. 함수

함수 정의 방식예시
함수 선언문function add(x, y) {
  return x + y;
}
함수 표현식var add = function(x, y)

1-1. 함수 선언문

fucntion add(x, y) {
	return x + y;
}
  • 함수 이름을 생략할 수 없다.
  • 표현식이 아닌 문
    - 변수에 할당할 수 없다.

1-2. 함수 표현식

var add = function(x, y) {
	return x+ y;
}
  • 자바스크립트의 함수는 객체 타입의 값, 값처럼 변수에 할당할 수도 있고, 프로퍼티 값이 될 수도 있으며, 배열의 요소가 될 수도 있다.
    -> 이런 성질을 갖는 객체는 일급객체라고 한다.

2. 함수 생성 시점과 호이스팅

// 함수 참조
console.dir(add);	// f add(x, y)
console.dir(sub);	// undefined
// 함수 호출
console.log(add(2, 5));	// 7
console.log(sub(2, 5));	// Type Error!!
// 함수 선언문
function add(x, y) {
  return x + y;
}
// 함수 표현식
var sub = function(x, y) {
  return x - y;
}
  • 함수 선언문으로 정의한 함수와 함수 표현식으로 정의한 함수의 생성 시점이 다름

  • 함수 선언문으로 정의한 함수는 함수 선언문 이전에 호출할 수 있다.

  • 함수 표현식으로 정의한 함수는 함수 표현식 이전에 호출할 수 없다.

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

⚠️ 함수 호이스팅과 변수 호이스팅은 미묘한 차이가 있음

  • 변수 할당문의 값은 할당문이 실행되는 시점인 런타임에 평가된다.
    -> 함수 표현식의 함수 리터럴도 할당문이 실행되는 시점에 평가되어 함수 객체가 된다.
    -> 함수 표현식으로 함수를 정의하면 함수 호이스팅이 아닌 변수 호이스팅이 발생한다.

3. 다양한 함수들

3-1. 즉시 실행 함수

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

3-2. 재귀 함수

  • 자기 자신을 호출하는 함수
  • 반복 처리를 위해 사용
  • 자신을 무한 재귀 호출하므로 멈출 수 있는 탈출 조건을 반드시 만들어야한다.
function countDown(n) {
	if(n < 0) return;
  consol.log(n);
  countDown(n - 1);	// 재귀 호출
}

countDown(10);
  • 팩토리얼
  • 1부터 자기 자신까지의 모든 양의 정수의 곱
function factorial(n) {
  
	// 탈출 조건: n이 1 이하일때 재귀 호출을 멈춘다.
  if(n <= 1) return 1;
  
  	// 재귀 호출
  return n * factorial(n - 1);
}

console.log(factorial(0));	// 0! = 1
console.log(factorial(1));	// 1! = 1
console.log(factorial(2));	// 2! = 2 * 1 = 2
console.log(factorial(3));	// 3! = 3 * 2 * 1 = 6
  • 함수 표현식으로!
var factorial = function foo (n) {
  
	// 탈출 조건: n이 1 이하일때 재귀 호출을 멈춘다.
  if(n <= 1) return 1;
  
  	// 재귀 호출
  return n * factorial(n - 1);
  // === return n * foo(n - 1);
}

3-3. 중첩 함수

  • 함수 내부에 정의된 함수를 중첩 함수, 내부함수라고 한다.
  • 그 중첩 함수를 초함하는 함수는 외부 함수라고 부른다.
  • 중첩 함수는 외부 함수 내부에서만 호출할 수 있다.
  • 함수 자신을 포함하는 외부 함수를 돕는 헬퍼 함수의 역할을 한다.
function outer() {
	var x = 1;
  
  // 중첩 함수
  fucntion inner() {
  	var y = 2;
    // 외부 함수의 변수를 참조할 수 있다.
    console.log(x + y);
  }
  inner();
}
outer();

3-4. 콜백 함수

  • 콜백 함수 : 함수의 매개변수 통해 다른 함수의 내부로 전달되는 함수
  • 고차 함수 : 매개변수를 통해 함수의 외부에서 콜백 함수를 전달받은 함수
  • 고차 함수는 콜백 함수를 자신의 일부분으로 합성한다.
  • 고차 함수는 매개변수를 통해 전달 받은 콜백 함수의 호출 시점을 결정해서 호출한다.

콜백 함수는 고차 함수에 의해 호출되며 이때 고차함수는 필요에 따라 콜백 함수에 인수를 전달할 수 있다.
-> 고차 함수에 콜백 함수 전달할 때 콜백 함수를 호출하지 않고 함수 자체를 전달해야 한다.

// 외부에서 전달받은 함수를 n만큼 반복 호출
fucntion 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
  • 콜백 함수가 고차 함수 내부에서만 호출된다면 콜백 함수를 익명 함수 리터럴로 정의하면서 고차 함수에 전달하는 것이 일반적.
  • 이 때 콜백 함수로서 전달된 함수 리터럴은 고차 함수가 호출될 때마다 평가되어 함수 객체를 생성한다.
// 익명 함수 리터럴을 콜백 함수로 고차 함수에 전달한다.
// 익명 함수 리터럴은 repeat 함수를 호출할 때마다 평가되어 함수 객체를 생성한다.
repeat(5, function(i) {
	if(i % 2) console.log(i);
});
  • 콜백 함수를 다른 곳에서도 호출할 필요가 있거나, 콜백 함수를 전달받는 함수가 자주 호출된다면 함수 외부에서 콜백 함수를 정의한 후 함수 참조를 고차 함수에 전달하는 편이 효율적이다.
  • 콜백 함수를 익명 함수 리터럴로 정의하면서 곧바로 고차 함수에 전달하면 고차 함수가 호출될 때마다 콜백 함수가 생성된다.
// logOdds 함수는 단 한 번만 생성된다.
var logOdds = function (i) {
	if(i % 2) console.log(i);
};

// 고차 함수에 함수 참조를 전달한다.
repeat(5, logOdds);
  • 콜백 함수는 함수형 프로그래밍 패러다임뿐만 아니라 비동기 처리(이벤트 처리, Ajax 통신, 타이머 함수 등)에 활용되는 중요한 패턴이다.
document.getElementById('myBtn').addEventListener('click', function() {
	console.log('clicked!');
})

// 콜백 함수 사용한 비동기 처리
// 1초 후에 메시지를 출력
setTimeout(function() {
	console.log('1초 경과');
}, 1000);
  • 콜백 함수는 배열 고차 함수에서도 사용된다.
  • map, filter, reduce
// map
var res = [1, 2, 3].map(function(item) {
	return item * 2;
});

console.log(res);	// [2, 4, 6]

// filter
res = [1, 2, 3].filter(function(item) {
	return item % 2;
});

console.log(res); 	// [1, 3]

// reduce
res = [1, 2, 3].reduce(function (acc, cur) {
	return acc + cur;
}, 0);

console.log(res);	// 6

3-5. 순수 함수, 비순수 함수

외부 상태: 전역 변수, 서버 데이터 파일, Console, DOM 등

순수 함수

  • 어떤 외부 상태에 의존하지도 않고 변경하지도 않는, 즉 부수 효과가 없는 함수
  • 동일한 인수가 전달되면 언제나 동일한 값을 반환
  • 오직 매개변수를 통해 함수 내부로 전달된 인수에게만 의존해 값을 생성, 반환한다.
  • 함수의 외부 상태에 의존하는 함수는 외부 상태에 따라 반환값이 달라진다.
  • 외부 상태에는 의존하지 않고 함수 내부 상태에만 의존하고 그 내부 상태가 호출될 때마다 변화하는 값이라면 순수 함수가 아니다. 예) 현재 시간
  • 일반적으로 최소 하나 이상의 인수를 전달받는다.(인수를 전달 받지 않는 순수 함수는 언제나 동일한 값 반화, 상수와 마찬가지)
  • 인수를 변경하지 않는 것이 일반적, 순수함수는 인수의 불변성을 유지한다.
// 현재 카운트
var count = 0;

// 순수 함수는 동일한 인수가 전달되면 언제나 동일한 값 반환
function increase(n) {
	return ++n;
}

// 순수 함수가 반환한 결과 값을 변수에 재할당해서 상태를 변경
count increase(count);
console.log(count);	// 1


count increase(count);
console.log(count);	// 2

비순수 함수

  • 외부 상태에 의존하거나 외부 상태를 변경하는, 즉 부수 효과가 있는 함수
  • 코드의 복잡성을 증가시키며 비순수 함수를 최대한 줄이는 것은 부수 효과를 최대한 억제하는 것과 같다.
var count = 0;

// 비순수 함수
function increase() {
 	return ++count;	// 외부 상태에 의존하며 외부 상태를 변경한다.
}

// 비순수 함수는 외부 상태를 변경, 상태변화 추적하기 어려워짐
increase();
console.log(count);	// 1

increase();
console.log(count);	// 2

함수형 프로그래밍은 순수 함수와 보조 함수의 조합을 통해 외부 상태를 변경하는 부수 효과를 최소화해서 불변성을 지향하는 프로그래밍 패러다임이다.
로직 내 존재하는 조건문과 반복문을 제거하여 복잡성 해결, 변수 사용을 억제하거나 생명주기를 최소화해서 상태 변경을 피해 오류를 최소화하는 것을 목표로 한다.

참고자료: 모던 자바스크립트 deep dive - 이웅모 저

0개의 댓글