함수

솜사탕·2023년 5월 6일
1

JavaScript

목록 보기
8/23

함수란 ?

함수는 자바스크립트에서 가장 중요한 핵심 개념이며
또 다른 자바스크립트의 핵심 개념인 스코프, 실행 컨텍스트, 클로저, 생성자 함수에 의한 객체 생성, 메서드, this, 프로토타입, 모듈화 등 모두 함수와 깊은 관련이 있다.

수학의 함수는 입력을 받아 출력을 내보내는 일련의 과정을 정의한다.
어떠한 재료를 투입받아 제품을 생상하는 기계와 같다고 볼수 있다.

즉 프로그래밍 언어의 함수도 수학의 함수와 같은 개념이다
지루할수 있으니 아래 예제를 보자

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

add(2, 5); // 7

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

이때의 함수도 입력을 받아서 출력을 내보낸다.

함수 내부로 입력을 전달받는 변수를 "매개 변수"
입력을 "인수"
출력을 "반환값"이라 한다.
함수는 값이며, 여러 개 존재할수 있으므로 특정 함수를 구별하기 위해
식별자인 함수 이름을 사용할 수 있다.

// hello 함수 정의

function hello(x, y) {
  console.log(x + y)
  return x + y;
}
}

함수 정의만으로 함수가 실행 되는 것은 아니다.
위 코드는 일련의 과정을 문으로 구현한 것 뿐
함수를 실행할려면 명시적으로 지시해야 한다.

// 함수 호출
hello(5, 3) // console.log(8);

// 함수 표현식 호출
var result = hello(1, 1);
console.log(result); // 2

함수를 사용하는 이유

함수는 필요할 때 여러번 호출할 수 있다.
즉 실행 시점을 개발자가 결정할 수 있고 몇 번이든 재사용이 가능하다.
동일한 작업을 반복적으로 수행해야 한다면 같은 코드를 여러번 작성하는것 보다
하나의 함수를 정의하여 함수를 재사용하는 것이 효율적이다
(코드량 줄어듬, 코드의 가독성, 유지보수 측면 등 장점이 많음)
이러한 부분을 코드의 재사용 측면에서 매우 유용하니까
자주 사용하자

함수 리터럴

자바스크립트의 함수는 객체 타입의 값이다.
따라서 숫자 값을 숫자 리터럴로 생성하고 객체를 객체 리터럴로 생성하는 것처럼
함수도 함수 리터럴로 생성할 수 있다.

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


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

함수 리터럴의 구성 요소는 다음과 같다.

리터럴 : 사람이 이해할 수 있는 문자 또는 약속된 기호를 사용해 값을 생성하는 표기 방식

즉 리터럴은 값을 생성하기 위한 표기법이며,
함수 리터럴도 평가되어 값을 생성하며, 이 값은 객체이며 함수는 객체다.

함수도 객체지만 일반 객체와는 다르고, 일반 객체는 호출할 수 없지만
함수는 호출할수 있다는 차이점이 존재한다.

함수 정의

JavaScript에서 함수를 정의하는 방법에는 4가지가 존재한다.

함수 선언문

아래 예제는 함수 선언문을 사용해 함수를 정의하는 방식이다.

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

// 함수호출
add(x,y)

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

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

// Error

함수 표현식

// 함수 표현식

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

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

함수 선언문과의 차이점이 존재하는데
코드를 보았을때

함수 표현식은 함수 이름으로 호출하는 것이 아닌
함수 객체를 가리키는 식별자를 호출하고 있다
즉 여기서의 함수 이름은 test이며, 식별자는 add 이다.

// 함수 표현식

var add = function (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)); // Error foo is not defined

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

// 함수 참조
console.dir(add); // f add(x, y)
console.dir(sub); // undefined

// 함수 호출
console.log(add(2,5)); // 7
console.log(sub(2,5)); // TypeError: sub is noa function

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

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

위 예제를 보면 함수 선언문으로 정의한 함수는 함수 선언문 이전에 호출할수 있으나
표현식은 이전에 호출할수 없다.

이러한 이유는 선언문으로 정의한 함수와 표현식으로 정의한 함수의 생성 시점이 다르기 때문이다.

모든 선언문이 그렇듯 함수 선언문도 코드가 한 줄씩 순차적으로 실행되는 시점인 런타임 이전에 자바스크립트 엔진에 의해 먼저 실행되고
선언문으로 정의할시 런타임 이전에 함수 객체가 먼저 생성되기 때문에
함수 이름과 동일한 이름의 식별자를 암묵적으로 생성하고 생성된 함수 객체를 할당한다.

즉 코드가 한줄씩 순차적으로 실행되기 시작하는 런타임에는 이미 함수 객체가 생성이 되어있고 함수 이름과 동일한 식별자에 할당까지 완료된 상태이다.
그래서 관련 함수에 호출 및 참조가 가능하다.

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

변수 호이스팅과, 함수 호이스팅의 미묘한 차이가 존재하는데
변수 호이스팅이 되는 키워드인
var 키워드를 사용할 경우 변수 선언문 이전에 변수를 참조하면 변수 호이스팅에 의해 undefined로 평가되지만 함수 선언문으로 호이스팅된 함수는
undefined가 아닌 호출이 가능하다.
둘의 차이가 존재하니 햇갈리지 말자

선언문 or 표현식 뭘 사용해 ?

상황마다 다르겠지만, 필자는 아직까지 개인적으로 선언문을 사용하고 있으며, JavaScript: The Good Parts의 저자인 Json JavaScript Object Notaion을 창안한 더글라스 크락포드는 함수 선언문 대신 함수 표현식을 사용할 것을 권장한다.

Function 생성자 함수

생성자 함수는 객체를 생성하는 함수를 말한다.

아래 예제를 보자

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

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

Function 생성자의 함수는 명확한 단점이 존재하는데,
우선 이러한 방식은 일반적이지도 않으며 바람직하지도 않다.
클로저를 생성하지 않으며 선언문 또는 표현식으로 생성한 함수와 다르게 동작한다.

화살표 함수(ES6)

ES6에서 도입된 화살표 함수는 function 키워드 대신 화살표(=>)를 사용해 좀 더 간략한 방법으로 함수를 선언할 수 있다.
화살표 함수는 익명 함수로 정의한다.

const add = (x, y) => x + y;
console.log(add(2,5)); // 7

화살표 함수가 기존의 함수 선언문, 표현식을 완전히 대체하기 위해 디자인 된것은 아니다.
호살표 함수는 기존의 함수보다 표현이 간략해지듯 내부 동작 또한 간략화 되어있다.

또한 화살표 함수는 생성자 함수로 사용할 수 없으며
기존 함수와 this 바인딩 방식이 다르고 프로토 타입 프로퍼티가 없으며
arguments 객체를 생성하지 않는다.
이부분에 대한 내용은 추후 깊게 다뤄보겠다.

다양한 함수의 형태

즉시 실행 함수

함수 정의와 동시에 즉시 호출되는 함수를 즉시 실행 함수라고 한다.
즉시 실행 함수는 단 한 번만 호출되며 다시 호출 할 수 없다.

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

코드 예제를 보면 즉시 실행 함수에는 현재 익명 함수로 되어있는데
즉시 실행 함수에는 익명 함수를 사용하는 것이 일반적이다.
또한 즉시 실행 함수는 반드시 그룹 연산자인 ( ... ) 로 감싸야 한다
그렇지 않으면 에러가 발생한다.

재귀 함수

자기 자신을 호출하는 것을 재귀 함수라고 한다.

재귀 함수의 목적은 반복되는 처리를 위해 사용된다.


//재귀 함수 호출 코드 예제
function countdown(n) {
  if(n < 0) return;
  console.log(n);
  countdown(n - 1); // 재귀 호출
}


countdown(10);

위 코드는 10부터 0 까지 출력하는 함수인데 반복되는 처리를 반복문 없이 구현할수 있다.

function countdown(n) {
  for(var i = n; i >= 0; i--);
  console.log(i);
}

countdown(10);

또한 재귀 함수는 자기 자신을 무한 호출한다.
그래서 재귀 호출을 멈출수 있는 탈출 조건을 꼭 만들어야한다.


function countdown(n) {
  if(n < 0) return; // n이 0 보다 작을 경우 return
  console.log(n);
  countdown(n - 1); // 재귀 호출
}

중첩 함수

함수 내부에 정의된 함수를 중첩 함수 또는 내부 함수라 한다.
그리고 중첩 함수를 포함하는 함수는 외부 함수라 부르며
중첩 함수는 외부 함수 내부에서만 호출할 수 있다.

function outer() {
var x = 1;
  // 중첩 함수
  function inner() {
    var y = 2;
    // 외부 함수의 변수를 참조 할 수 있다.
    console.log(x + y); // 3
  }
  innter();
}

outer();

콜백 함수

콜백함수는 간단하게 다른 함수에 매개변수로 넘겨준 함수를 말한다.

매개변수로 넘겨받은 함수는 일단 넘겨받고, 때가 되면 나중에 호출(called back)한다는 것이 콜백함수의 개념이다.

function result(count, TestOne, TestTwo) {
  count < 3 ? TestOne() : TestTwo();
}

function HandleTestOne() {
  console.log('어쩔티비');
}

function HandleTestTwo() {
  console.log('저쩔티비')
}

result(2, HandleTestOne, HandleTestTwo);

코드를 살펴보면 result, TestOne, TestTwo 총 3가지 함수를 선언하고

result 함수를 호출할 때 매개변수로 count에 숫자값을,

그리고 TestOne와 TestTwo에 각각 HandleTestOne과 HandleTestTwo함수를 전달했다.

여기서 HandleTestOne함수와 HandleTestTwo함수가 콜백함수 인 것이다.

result함수가 먼저 호출되고, 매개변수로 들어온 count의 값에 따라
HandleTestOne과 HandleTestTwo함수 둘 중 한 가지가 나중에 호출된다.

위 코드는 count가 2이기 때문에 HandleTestOne이 실행된다.

profile
공부공부공부공부공부공부

0개의 댓글