Frontend/Study

[모던 자바스크립트 Deep Dive] 12장 함수

BeNI 2023. 6. 8. 18:02
728x90

12장  함수

 

1. 함수란 ?

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

함수 정의를 통해 생성하고, 함수 호출로 실행한다.

 

2. 함수를 사용하는 이유

  • 코드의 재사용성
  • 유지보수의 편의성
  • 코드의 신뢰성/가독성

 

3. 함수 리터럴 

📌 구성 요소 : 함수 이름, 매개변수 목록, 함수 몸체

일반 객체는 호출할 수 없지만, 함수는 호출할 수 있으며 고유의 프로퍼티를 갖는다.

함수는 "객체" 이다.

 

 

4. 함수 정의

1) 함수 선언문

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

console.dir(add); // console.log와 다르게 객체의 프로퍼티까지 출력
console.log(add(2,3));

함수 선언문은 표현식이 아닌 "문" 이다. (undefined가 출력됨)

 

함수 선언문은 표현식이 아닌 문 이지만 함수 리터럴 표현식과 형태가 동일하기 때문에 변수에 할당될 수 있다.

하지만, 함수를 생성하는 내부 동작에 차이가 있다.

 

  함수 선언문 함수 리터럴
생성 여부 함수 객체를 생성
호출 방식 함수 이름으로 호출 가능 함수 이름으로 호출 불가능
(함수 몸체 내에서만 참조 가능)

왜 함수 선언문은 함수 이름으로 호출이 가능할까 ?

자바스크립트 엔진은 함수 이름과 동일한 이름의 식별자를 암묵적으로 생성하고, 거기에 함수 객체를 할당한다.

 

📌 함수는 함수 이름으로 호출하는 것이 아닌 함수 객체를 가르키는 식별자로 호출한다.

* 따라서 함수 선언문으로 생성한 함수는 자바스크립트 엔진이 암묵적으로 생성한 식별자로 호출을 하게 된다.

 

2) 함수 표현식

  • 값의 성징을 갖는 객체를 일급 객체라고 함
  • 자바스크립트의 함수는 일급 객체다.
  • 함수 리터럴로 생성한 함수 객체를 변수에 할당하는 방식을 함수 표현식이라고 함
  • 함수 리터럴의 함수 이름은 생략 가능하다.
var add = function (x, y) {
  return x + y;
};

 

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

함수 선언문으로 정의한 함수는 함수 선언문 이전에 호출 가능하다.

(함수 표현식으로 정의한 함수는 불가능, 하지만 참조는 가능하다)

왜 why ? 모든 선언문은 런타임 이전에 자바스크립트 엔진에 의해 먼저 실행된다.

따라서 함수 선언문 이전에 함수를 참조/호출 가능한 현상을 호이스팅 이라고 한다.

 

👉 함수 표현식으로 함수를 정의 했을 때는 함수 호이스팅이 아닌 변수 호이스팅에 의해 undefined로 평가된다.

console.dir(add); // f add(x,y)
console.dir(sub); // undefined

console.log(add(2,4)); // 6
console.log(sub(2,4)); // Error


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

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

* 함수 표현식을 쓸 때는 반드시 함수 표현식 이후 참조/호출 해야 한다.

  하지만 함수 호이스팅은 위의 규칙을 무시하기 때문에 더그라스 크락포드는 함수 표현식을 더 권장한다.

 

3) Function 생성자 함수

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

* new 연산자 없이 호출 도 가능하다. 

 

해당 방식은 일반적이지도 않고, 바람직하지 않다. (클로저 생성x, 일반 함수와 다르게 동작)

 

 

4) 화살표 함수

- es6에 새롭게 도입된 익명 함수

const add = (x, y) => x + y;

- 생성자 함수로 사용 불가능, this 바인딩 방식 다록, prototype 프로퍼티가 없고, arguments 객체를 생성하지 않음 !

 

 

5. 함수 호출

1) 매개변수와 인수

  • 인수 : 값으로 평가될 수 있는 표현식, 개수와 타입에 제하니 없다
  • 매개변수 : 함수를 정의할 때 선언, 함수 몸체 내부에서 변수와 동일하게 취급된다.

- 함수 호출 시 함수 몸체 내에서 암묵적으로 매개변수가 생성되고 undefined로 초기화 된 후 인수가 순서대로 할당됨

- 매개변수와 인수의 개수가 동일하지 않아도 에러는 나지 않는다. 부족할 시 초기값이  undefined 값이기 때문

  초과 시에는 무시되지만 암묵적으로 arguments 객체의 프로퍼티로 보관된다.

 

2) 인수 확인

- 자바스크립트 경우 타입 체크를하지 않기에 함수 정의 시 적절한 인수가 전달되었는지 확인 필요

- es6에서 도입된 매개변수 기본값을 사용하면 인수 체크 및 초기화를 간편히 할 수 있다.

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

 

3) 매개변수의 최대 개수

- 일반적으로 최대 3개 이상을 넘지 않는 것을 권장 (그 이상이면 객체를 이용)

* 이상적인 함수는 한가지 일만 해야하며 가급적 작게 만들어야 한다.

 

4) 반환문

  • 함수의 실행을 중단하고 함수 몸체를 빠져나감
  • return 키워드 뒤에 오는 표현식을 평가해 반환한다. (기본값 undefiend)

* 함수 바깥에서 사용하면 문법 에러가 발생한다.

 

 

 

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

- 매개변수로 받는 원시 값은 값 자체가 복사되고, 객체는 참조 값이 복사된다.

- 객체의 변경을 추적하려면 옵저버 패턴 등을 통해 대응 해야한다.

- 또다른 해결 방법으로는 객체를 불변 객체로 만들어 동작하게 하는 방법이 있다. 

 

 

7. 다양한 함수의 형태

1) 즉시 실행 함수(IIFE)

- 함수 정의와 동시에 즉시 호출되는 함수를 즉시 실행함수라고 한다

(function () {
  var a = 4;
  return a;
}());

- 보통은 익명함수 지만, 기명함수로 실행할 수 도 있다. (하지만 해당 식별자로 다시 호출 불가능)

 

- 즉시 호출 함수의 다양한 형태(보통 첫 번째 방식을 많이 쓴다)

(function () {
  // ...
}()); 

(function () {
  // ...
})(); 

!function () {
  // ...
}(); 

+function () {
  // ...
}();

 

2) 재귀 함수

- 함수가 자기 자신을 호출하는 것을 재귀 호출이라고 함

- 반드시 종료 조건이 있어야 한다. 

- 함수 표현식으로 생성시 해당 식별자로 내부에서 호출해야 한다.

 

3) 중첩 함수

- 함수 내부에 정의된 함수를 중첩 함수라고 한다.

- 중첩 함수는 외부 함수 내부에서만 호출 될 수 있다.

 

4) 콜백 함수

- 함수의 매개변수를 통해 다른 함수의 내부로 전달 되는 함수를 콜백 함수라고 한다.

- 매개 변수를 통해 함수의 외부에서 콜백 함수로 전달받은 함수를 고차 함수라고 한다.

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

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

repeat(4, logAll);

- 고차 함수는 매개변수를 통해 전달받는 콜백 함수의 호출 시점을 결정해서 호출가능하다.

- 따라서 콜백 함수는 고차 함수에 의해 호출되며, 고차 함수는 필요에 의해 콜백 함수에 인수를 전달 할 수 있다.

- 비동기 처리, 배열 고차 함수 등에서 자주 사용되는 패턴이다.

 

5) 순수 함수와 비순수 함수

  • 순수 함수 : 외부 상태에 의존하지도 않고 변경하지도 않는 부수효과가 없는 함수
  • 비순수 함수 : 외부 상태에 의존하거나 외부 상태를 변경하는, 즉 부수 효과가 있는 함수

- 순수 함수는 동일한 인수가 전달되면 언제나 동일한 값을 반환하는 함수다. 

  따라서 인수를 변경하지 않는 것이 일반적이다.

var count = 0;

// 순수 함수
function increase(n) {
  return ++n;
}

// 비순수 함수
function decrease() {
  return --count;
}

count = increase(count); // 1
decrease(); // count = 0;

비 순수 함수는 외부 상태를 변경하므로 상태 변화를 추적하기 어렵다.

 

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

 

 

 

728x90