본문 바로가기
JavaScript Study

JavaScript - #15. 함수(2)

by KMS_99 2023. 8. 29.

함수를 정의 하였으면, 함수를 호출해야한다.

함수 호출법에 대해서 알아보자

 

- 함수의 호출

1) 매개 변수와 인수

함수를 실행시키기 위해 정의 된 함수에 건내주어야 하는 값이 있을 수도 있다.

이때 매개변수를 통해 인수를 전달 할 수 있다.

인수는 값으로 평가 되어야하며 개수는 제한이 없다.

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

console.log(add(1,2));

함수가 정의 될때 매개변수를 선언할 수 있으며 이 코드에서는 x, y 두개의 매개변수를 선언하였다.

매개변수는 함수 코드블럭 내에서 변수처럼 사용된다. 함수 외부에서는 사용 불가능 하다.

 

이 매개변수에 전달 된 인자는 1, 2로 순서에 맞게 함수에 전달될 때 x=1, y=2로 전달된다고 생각하면 된다.

 

매개변수의 개수와 인수의 개수는 같지 않아도 되며 매개변수보다 인수가 많은 경우 초과되는 인수를 무시하며, 적다면 순서에 맞게 할당되지 못한 매개변수는 undefined의 값으로 초기화 된다.

 

이때 초과된 인자는 버려지지 않고 암묵적으로 arguments 객체의 프로퍼티로 보관된다.

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

console.log(argTest(1,2,3));

결과 :

[Arguments] { '0': 1, '1': 2, '2': 3 }

결과 값을 보았을 때 객체가 반환 된다.

결과 값의 객체에는 순서가 key값으로 들어가며 순서에 따른 인수가 할당되어있는 프로퍼티들이 출력된다.

이때 초과되어 무시된 3의 인수가 저장되어있음을 알 수 있다.

 

그렇다면 매개변수의 타입은 정해져있을까?

아니다. 매개변수의 타입은 인수에 따라서 동적으로 변경된다. 

따라서 다음과 같은 코드도 오류 없이 동작된다.

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

console.log(argTest('a',2));

결과 :

a2

이러한 결과가 나오는 이유는 매개변수 x에 문자열 타입 'a'가 들어갔고, y에는 숫자타입 2가 들어갔다.

코드 실행 문을 보았을 때 x+y라는 값을 반환하게 되고 이때 피연산자에 문자열이 있어 +가 문자열 연결 연산자로 동작하게 된다. 따라서 'a2'라는 문자열이 반환되었다.

이 결과가 오류 없이 나오는 이유는 매개변수가 미리 타입을 지정하지 않기 때문이다.

 

따라서 우리는 원하는 타입의 인수가 적절히 전달되었는지 확인할 필요가 있다.

function argCheck(x,y){
    if(typeof x !== 'number' || typeof y !=='number') {
        throw new TypeError('모든 인수는 숫자 값이여야합니다');
    }

    return x+y;
}

// y에 undefined 타입이 할당
console.log(argCheck(2));
// x, y에 문자열 타입이 할당
console.log(argCheck('a','b'));

이 코드와 같이 논리연산자를 통해 인자값의 타입을 체크할 수 있다.

 

추가로 인자의 개수가 매개변수의 개수보다 적을 때 남는 매개변수의 값을 undefined가 아닌 원하는 수로 초기화 하고 싶을 때 단축평가를 유용하게 사용할 수도 있다.

function argNumCheck(x,y,z){
    x = x||0;
    y = y||0;
    z = z||0;

    return `${x}+${y}+${z} = ${x+y+z}`;
}

// y에 undefined 타입이 할당
console.log(argNumCheck(2,3));
console.log(argNumCheck(2,3,4));

결과 : 

2+3+0 = 5
2+3+4 = 9

 

ES6에서는 매개변수의 기본값을 설정해 줄 수 있는 매개변수 기본값이라는 것이 도입되었다. 

매개변수에 undefined가 전달 된 경우 매개변수에는 지정해준 기본값이 초기화된다.

매개변수 기본값으로 위 코드를 작성해보자.

function argNumCheckES6(x=0, y=0, z=0){
    return `${x}+${y}+${z} = ${x+y+z}`;
}

console.log(argNumCheckES6(2,3));
console.log(argNumCheckES6(2,3,4));

결과 :

2+3+0 = 5
2+3+4 = 9

ES6문법을 사용하여 더욱 간단하게 사용할 수 있음을 알 수 있다.

 

 

위에서 언급했듯 매개변수의 개수제한은 없다.

그렇다면 이상적인 매개변수의 수는 얼마나 될까?

답은 0개이다.

매개변수가 많으면 많을 수록 함수가 복잡하다는 뜻이며, 바람직한 함수의 사용은 함수가 한가지 일만 실행하는 것이다.

따라서 권장하는 개수는 3개 이상 넘지 않는것이다.

 

 

2. 반환문

함수의 결과를 반환할 때는 return 키워드를 사용하여 반환한다.

 

return 키워드의 역할은 두가지이다.

1. 함수의 결과를 반환한다.

2. 함수를 종료시킨다.

 

1번의 경우는 이전에도 많이 다루어 대부분 이해했다고 생각한다.

그렇다면 2번은 무슨 뜻일까?

 

다음 코드와 결과값을 참고해서 이해하자.

function multiply(x,y){
    return x*y;
    console.log(x*y); // 실행안됨
}

console.log(multiply(3,5)) //15

결과 :

15

return 은 결과값을 반환하는 동시에 함수의 함수 실행문을 중단하고 빠져나온다.

따라서 return 다음줄에 있는 console.log는 실행되지 않는다.

 

추가로 return은 생략이 가능하며, 생략 시 undefined를 반환한다.


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

이전 객체를 배울 때 참조에 의한 전달과 값에 의한 전달을 배운적이 있다.

매개변수에도 같은 개념이 동일하게 적용된다.

다음 코드를 보자.

let person = {name:'Kim'};
let num = 1;

function user (primitive, obj){
    primitive = 10;
    obj.name = 'Lee'
}

user(num, person);

console.log(person);
console.log(num);

값에의한 전달을하는 숫자타입과 참조에 의한 전달을 하는 객체가 함수에 인자로 전달되는 상황이다.

매개변수에 인자가 전달될 때 숫자타입 매개변수는 새로운 공간에 값을 복사한 값에 의한 전달을 하게 되고,

객체타입의 매개변수는 객체 자체의 리터럴을 참조하는 주소값을 참조하는 참조에 의한 전달을 하게된다.

이 결과 함수 내부에서 매개변수의 변화가 발생하였을 때, 숫자타입은 새로운 공간에서의 값의 변동이 생기며 원래의 원시값에 영향을 미치지 않는다. 하지만 객체타입은 결론적으로 동일한 객체 리터럴을 가리키기 때문에 객체 리터럴의 변동 역시 공유하게되어 원본에 영향을 미친다.

이를 해결하는 방법은 객체를 불변의 객체로 만들어 사용하거나 원시값처럼 변경불가능한 값으로 동작하게 만드는 방법이 있다. 추후 관련내용이 나왔을 때 이해하면 되겠다.

 

자바스크립트는 함수형 프로그래밍을 지향하며, 외부상태에 의존하지 않고 외부상태를 변경하지도 않는 순수 함수를 사용하는 것이 중요하다. 따라서 부수효과가 없도록 프로그래밍 해야할 것이다.


- 다양한 함수의 형태

1) 즉시 실행 함수

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

즉시실행함수는 일회성을 가지고 있다.

(function test(){
    console.log("이 함수는 즉시 실행 함수입니다.");
}());

test(); // ReferenceError 발생

 함수 선언문을 사용 할 때는 그룹 연산자를 꼭 사용해야한다(...)

함수 리터럴을 먼저 평가해야하기 때문이다.

 

2) 재귀함수

함수가 자기 자신을 호출하는 것을 말한다.

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

countdown(10);

코드 내에 반복문을 사용하는 다음과 같은 경우를 재귀함수로 변경하면 다음과 같이 표현이 가능하다.

function count(n){
    if(n<=0) return; 
    console.log(n);
    count(--n);
}

count(10);

재귀함수는 무한루프에 빠질 가능성이 크기에 직관적일 때만 사용하는 것이 좋다.

 

3. 중첩함수

중첩함수는 함수 내부에서 함수를 정의하는 것을 뜻한다. 

이때 중첩함수를 포함하는 함수를 외부함수라고 부른다.

중첩함수는 대부분 외부함수를 도와주는 헬퍼함수의 역을 한다.

function outer(){
    let x = 1;
    
    function inner(){
        let y=2;
        console.log (x*y);
    }

    inner();
}

outer();

 

4. 콜백함수

콜백함수는 매개변수로 사용되는 함수를 뜻하며, 이때 외부에서 콜백함수를 전달 받는 함수를 고차함수라고 한다.

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

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

repeat(5,logAll);

추후 배열 부분에서 많이 다뤄질 예정이다.