자바스크립트를 배워보자 10일차 - 함수

0

Javascript

목록 보기
10/30

함수

1. 함수란?

자바스크립트에서 함수란 굉장히 중요한 개념이다. 단순히 c/c++ , java에서 보이는 함수와는 달리 자바스크립트에서의 핵심 개념인 스코프, 실행 컨텍스트, 클로저, this, 프로토타입, 모듈화 등이 있다.

힘수의 선언(정의)은 다음과 같다.

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

따로 함수의 반환 타입, 매개변수 타입을 안써주어도 된다.

2. 함수 리터럴

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

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

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

위에서 보면, 함수 리터럴을 변수에 할당한 것을 볼 수 있다. 리터럴은 사람이 보기편한 방법으로 값을 생성하는 표기법이다.

따라서 함수 리터럴도 값으로 표현되는데, 그 값은 객체이다. 따라서 함수는 객체이다.

그렇다고 객체와 함수가 완전 동일한 것은 아니다. 일반 객체는 호출할 수 없지만 함수는 호출할 수 있다.

또한, 일반 객체에는 없는 함수 객체만의 고유한 프포퍼티를 갖는다.

함수가 객체라는 것은 자바스크립트만의 아주 중요한 특징이다.

3. 함수 정의

다음의 4가지 방법이 있다.

  1. 함수 선언문
function add(x,y){
    return x + y;
}
  1. 함수 표현식
var add = function(x,y){
    return x+y;
}
  1. Function 생성자 함수
var add = new Function('x','y', 'return x + y');
  1. Arrow 함수(es6)
var add = (x,y) => {x+y};

변수에 함수를 담고, 함수를 호출하는 방법은 변수를 함수처럼 쓰기만하면 된다.

var add = function foo(x,y){
    return x + y;
}
console.log(add(1,2)); // 3
console.log(foo(1,2)); // error

다음과 같이 사용하면 된다. 그런데 문제는 foo함수로는 호출할 수 없다. 이유는 함수 선언식과 함수 표현식을 정확히 분류해야하기 때문이다.

함수 선언식은 객체에 함수를 담지 않는다. 때문에 함수를 부를 수 있는 변수가 필요하고 이를 위해 함수의 이름으로된 변수를 암묵적으로 만든다.

반면 함수 표현식은 객체에 함수를 담을 수 있다. 때문에 함수를 부를 수 있는 이름이 있기 때문에 해당 식별자를 이용하여 호출하면 된다. 위의 add가 바로 식별자가 되는 것이다. 따라서 굳이 foo 함수를 호출하기위해 foo 식별자를 만들 필요가 없다. 따라서 위는 함수 표현식이 된다.

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

var add = function(x,y){
    return x+ y;
} // 함수 표현식
console.log(foo(10,20)); // 30
console.log(add(10,20)); // 30

함수 선언문과 함수 표현식이 마치 닮아보이지만, 작동 원리는 매우다르다.

함수 선언식에서는 함수를 부를 변수가 없으니 함수 이름인 foo를 가지고 암묵적으로 함수를 호출할 수 있는 변수를 만든다.

함수 표현식은 함수를 호출할 수 있는 변수가 있으므로, 함수 이름이 없어도 된다.

따라서, 함수 표현식에서의 이름으로 함수를 호출할 변수를 굳이 생성할 필요가 없어, foo로 함수를 호출할 수가 없는 것이다.

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

다음의 예제를 확인해보자

console.dir(add); // [Function: add]
console.dir(sub); // undefined

console.dir(add(2,5)); // 7
console.dir(sub(2,5)); //TypeError: sub is not a function

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

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

분명 함수들이 정의된 곳은 사용되는 곳 아래에 있다. 그런데, 왜 함수 선언식은 함수가 호출이 되고, 함수 표현식은 함수가 호출이 안될까??

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

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

따라서 함수 선언문으로 함수를 정의하면 함수 객체가 먼저 생성되고 함수 이름과 동일한 함수 변수가 생성된다. 또한, 함수 변수에 함수 객체가 넣어지게 된다.

함수 호이스팅

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

함수 호이스팅과 변수 호이스팅은 다르므로 이를 구분하여 사용해야한다.

var 키워드를 사용한 함수 표현식과 var 키워드 없이 호출할 수 있는 함수 선언식은 런타임 이전에 자바스크립트 엔진에 의해 먼저 실행되어 식별자를 생성한다.

그러나 var 키워드로 선언된 변수는 undefined로 초기화되고, 함수 선언문을 통해 암묵적으로 생성된 식별자는 함수 객체로 초기화 된다.

변수 할당문은 런타임에 실행되는 시점에 값이들어가므로, 함수 표현식을 사용하면 함수 객체가 런타임에 해당 시점에 들어가는 것이다.

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

이에 따라 함수 선언문은 함수가 정의되기도 전에 호출되어 불려질 수 있다는 단점이 있어 함수 표현식을 사용하는 것이 좋다.

5. Function 생성자 함수

자주 사용안하므로 넘어가자

6. 화살표 함수 ( Arrow Function )

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

또한, 화살표 함수는 이름이 없는 익명 함수이다.

const add = (x,y) => {return x+y};
console.log(add(1,2)); // 3

화살표 함수는 표현만 간략한 것이 아니라, 내부 동작 또한 간략화 되어있다.

화살표 함수는 생성자 함수로 사용할 수 없고, this바인딩 방식, prototype 프로퍼티가 없고, arguments 객체를 생성하지 않는다.

화살표 함수에 대해서는 뒤에 더 자세히 알아보도록 하자

7. 함수의 매개변수와 인수

만약 함수의 인수를 매개변수보다 적게, 많이 주면 어떻게 될까??

  • 인수를 적게 줄 경우
function add(x,y){
    return x+ y;
}
console.log(add(2)); // NaN

매개변수 y에는 인수가 주어지지 않았는데, 이럴 때는 undefined로 할당된다.

2 + undefined을 했기 때문에 NaN이 나온 것이다.

  • 인수가 많은 경우
function add(x,y){
    return x+ y;
}
console.log(add(2,5,10)); // 7

초과된 인수를 매개변수에서 참조할 수 없는 것은 아니다. arguments 객체의 프로퍼티로 보관된다.

function add(x,y){
    console.log(arguments); // [Arguments] { '0': 2, '1': 5, '2': 10 }
    return x+ y;
}
console.log(add(2,5,10)); // 7

8. 반환문

return 반환에서 아무것도 써주지 않는다면 undefined가 나오게 된다.

function foo(){
    return;
}
console.log(foo()); // undefined

8. 콜백 함수

자바스크립트는 함수를 객체처럼 다룰 수 있기 때문에, 함수의 매개변수로 함수를 받을 수 있다.

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

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

결과

0
1
2
3
4
5
6
7
8
9

함수의 매개변수로 함수를 받을 수 있다. 자바스크립트 함수는 일급 객체이므로 함수의 매개변수를 통해 전달이 가능하다.

이처럼 함수의 매개변수를 통해 다른 함수의 내부로 전달되는 함수를 콜백 함수라고 하며, 매개 변수를 통해 함수의 외부에서 콜백함수를 전달받은 함수를 고차함수(High-order function, HOF)라고 한다.

매개변수를 통해 함수를 전달받거나, 반환값으로 함수를 전달하는 함수를 함수형 프로그래밍 패러다임에서 고차 함수라고 한다.

고차함수는 콜백함수를 자신의 일부분에 사용할 수 있고, 콜백 함수에 인수를 전달할 수 있다.

또한, 함수를 꼭 만들어서 전달할 필요없이, 바로 정의를해서 넣어줄수도 있다.

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

콜백함수로서 전달된 함수 리터럴은 고차 함수가 호출될 때마다 함수 객체를 생성한다. 따라서 콜백 함수를 다른 곳에서 호출할 수가 없다.

9. 순수함수 비순수 함수

  • 순수함수
    • 함수형프로그래밍에서 어떤 외부 상태에 의존하지도 않고, 변경하지도 않는 즉 부수 효과가 없는 함수를 순수 함수(pure function)라고 한다.
  • 비순수함수
    • 반대로 부수 효과가 있는 함수를 비순수 함수라고 한다.

순수 함수는 동일한 인수가 전달되면 언제나 동일한 값을 반환한다. 즉, 멱등성을 보장한다는 것이다.

오로지 매개변수를 통해 함수 내부로 전달된 인수에게만 의존해 반환값을 만든다는 것이다.

함수의 외부 상태에 의존하는 함수는 외부 상태에 따라 반환 값이 달라진다.

외부 상태에 의존하지 않는다는 특징말고도, 순수함수의 또 하나의 특징은 함수의 외부 상태를 변경하지 않는다는 것이다. 즉, 순수 함수는 어떤 외부 상태에도 의존하지 않으며 외부 상태를 변경하지도 않는 함수이다.

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

위는 비순수 함수이다.

함수 내부에서 외부 상태를 직접 참조하면서 외부 상태에 의존하게 되어 반환값이 변할 수 있고, 외부 상태도 바꿀 수 있다.

0개의 댓글