12장 Function (JS Deep Dive)

January·2022년 6월 1일
0
post-thumbnail

함수는 input을 받아 output을 내보내는 일련의 과정을 정의한다. 프로그래밍 언어의 함수는 일련의 과정을 문으로 구현하고 코드 블록으로 감싸서 하나의 실행 단위로 정의한 것이다.

함수를 사용하는 이유

  1. 코드의 재사용
    • 함수를 통해 중복을 제거하고 코드를 재사용할 수 있다.
  2. 유지보수의 편의성
    • 코드의 중복을 억제하고 재사용성을 높이기 때문.
  3. 코드의 신뢰성
    • 코드 중복이 억제되어 실수가 줄어든다.
  4. 코드의 가독성
    • 함수 내부 코드를 이해하지 않고도 함수의 역할을 파악하게 해주는 함수 식별자

함수 호이스팅

변수 할당문의 값은 할당문이 실행되는 시점, 즉 런타임에 평가되므로 함수 표현식의 함수 리터럴도 할당문이 실행되는 시점에 평가되어 함수 객체가 된다.

함수 표현식으로 함수를 정의하면 함수 호이스팅이 발생하는 것이 아니라 변수 호이스팅이 발생한다.

따라서 함수 표현식 이전에 호출을 하면 undefined가 출력된다.

함수 호출

매개변수는 함수 내부에서만 참조되는 변수이다. 함수는 매개변수와 인수의 개수가 일치하는지 확인하지 않는다. 적을 때에는 undefined를 내보내고 많을 때에는 초과된 인수는 무시된다. 자바스크립트는 동적타입 언어이기 때문에 매개변수의 타입을 사전에 지정하지 않아도 된다.

ES6에서 도입된 매개변수 기본값을 사용하면 함수 내에서 수행하던 인수 체크 및 초기화를 간소화할 수 있다. 매개변수 기본값은 매개변수에 인수를 전달하지 않았을 경우와 undefined를 전달한 경우에만 유효하다.

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

객체는 참조에 의한 전달방식으로 동작한다. 매개변수에 값을 전달할 때에 원시 값은 변경 불가능한 값이여서 직접 변경할 수 없다. 함수 안에서 재할당을 통해 새로운 값을 할당한다. 객체 차입 인수를 전달 받으면 객체는 변경 가능한 값이므로 재할당 없이 직접 할당된 객체를 변경한다.

const one = 100;
const name = { first: 'Kim' };

function change(one, name) {
	one += 50;
  	name.first = 'Lee';
}

change(one, name);

console.log(one);  // 100
console.log(name);  // {first: 'Lee'}

name에 부수효과가 생긴다. 이는 곧 코드의 복잡성을 증가시키고 가독성을 해치게 된다. 이를 해결하는 방법은 깊은복사를 통해 새로운 객체를 생성하거나 객체 변경을 추적하는 옵저버 패턴을 활용해야 한다.

즉시 실행 함수

즉시 실행 함수는 단 한 번만 호출된다. 그래서 익명함수가 일반적으로 사용된다. 기명함수는 즉시 실행 함수로 만든다면 함수 이름을 재사용할 수 없다.

그룹연산자로 함수를 감싸게 되는데 그룹연산자를 뺀다면

function now() {}();   // function now() {};();

함수 호출 연산자가 아니라 그룹 연산자로 해석이되어 에러가 발생한다.

재귀 함수

재귀 호출은 함수가 자기 자신을 호출한다. 그럼 재귀 함수는 재귀 호출을 수행하는 함수를 말한다. 반복문 없이도 구현할 수 있지만 탈출 조건을 반드시 만들어 줘야하고 스택 오버플로 에러(자료구조의 데이터가 넘쳤다.)를 발생시킬 수 있다.

function countdown(n) {
	if(n < 0) return;
  	console.log(n);
  	countdown(n-1);
}

countdown(10)

중첩 함수

중첩함수(내부함수)를 포함하는 함수는 외부 함수라 부른다. 중첩 함수는 외부 함수 내부에서만 호출 가능하고 자신을 포함해서 외부 함수를 돕는 헬퍼 함수의 역할을 한다.

콜백 함수

함수의 매개변수를 통해 다른 함수의 내부로 전달되는 함수를 콜백함수라 한다. 매개변수를 통해 함수의 외부에서 콜백 함수를 전달받은 함수를 고차 함수라 한다. 즉, 고차 함수는 콜백 함수를 자신의 일부분으로 합성한다.

function repeat(n, f) {  // 고차함수
	for(let i = 0; i < n; i++) {
    	f(i);
    }
}

let logAll = function(i) {  // 콜백함수
	console.log(i)
}

repeat(5, logAll)  // 0 1 2 3 4

중첩함수와의 차이점은 중첩함수는 고정되있다. 콜백함수는 자유롭게 교체 가능한 장점이 있다. 고차함수는 호출시점을 결정해서 콜백함수를 호출한다. 그리고 필요에 따라 콜백 함수에 인수를 전달 할 수 있다.

콜백함수는 고차함수 내부에만 호출된다면 익명 함수 리터럴로 정의하면서 곧바로 고차 함수에 전달하는 것이 일반적이다.

repeat(5, function (i) {
  console.log(i);
});

왜 익명함수일까?

익명 함수는 재사용 하지 않는, 한번만 사용할 함수를 위한 개념 리터럴(Literal) 방식으로 변수에 담겨 사용하는 함수이다.
단 한번만 사용되는(재사용이 필요없는) 함수의 경우, 불필요한 시간동안 메모리를 차지하지 않도록 익명함수로 구현한다면, 정확히 해당 함수가 필요한 위치에서만 해당 함수가 구현되고 사라지면서 메모리를 아낄 수 있게 된다.

만약 콜백함수를 전달받는 함수가 자주 호출된다면 콜백 함수를 정의한 후 함수 참조를 고차함수에 전달하다.

0개의 댓글