[JS Basic] 함수

재오·2022년 9월 27일
1

JavaScript

목록 보기
9/48
post-thumbnail

함수 리터럴

자바스크립트의 함수는 객체 타입의 값이다. 앞에서 말했듯이 리터럴은 값을 생성하기 위한 표기법이다. 함수 리터럴도 평가되어 값을 생성하며, 이 값은 객체다. 즉 함수는 객체다. 함수 리터럴은 함수 이름과 매개변수, 함수 몸체로 구성되어 있다. 여기서 함수 이름은 생략할 수 있다.

함수 정의

함수 정의 방식으로 크게 4가지가 있지만 3가지를 중점으로 살펴보자.

  • 함수 선언문
  • 함수 표현식
  • 화살표 함수(ES6)

- 함수 선언문

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

함수 선언문은 함수 이름을 생략할 수 없다. 또한 함수 선언문은 표현식이 아닌 문이기 때문에 그 자체로 콘솔에서 실행하면 undefined가 출력된다. 자바스크립트 엔진은 함수 선언문을 함수 표현식으로 변환해 함수 객체를 생성한다고 생각할 수 있다.

함수 선언문은 자바스크립트 엔진에 의해 동일한 함수이름의 식별자가 생기기 때문에 함수이름으로 호출하더라도 오류없이 호출이 되지만, 표현식은 별도의 식별자를 붙여야 하기 때문에 함수 이름으로 호출을 하면 오류가 발생한다.

function foo() {console.log('foo');} // 함수 선언문
foo(); // foo

(function bar() {console.log('bar'); }); // 함수 표현식
bar(); // Reference Error

함수를 호출할 때에는 함수 이름이 아니라 식별자로 호출한다!

- 함수 표현식

자바스크립트의 함수는 객체 타입의 값이다. 이렇게 값의 성질을 갖는 객체를 일급 객체라고 한다. 함수 리터럴로 생성한 함수를 변수에 할당한 것을 함수 표현식이라 한다. 함수 표현식의 함수 이름은 생략하는 것이 일반적이다.

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

함수 선언문은 "표현식이 아닌 문"이고 함수 표현식은 "표현식인 문"이다.

- 함수 호이스팅

함수 선언문으로 함수를 정의하면 런타임 이전에 함수 객체가 먼저 생성된다. 자바스크립트 엔진에 의해 함수 이름과 동일한 이름의 식별자를 암묵적으로 생성하고 객체를 할당까지 해놓는다. 한마디로 런타임 이전에 모든 것을 다 만들어 놓은 셈이다. 하지만 함수 표현식으로 함수를 정의하면 함수 호이스팅이 아닌 변수 호이스팅이 발생한다.

한마디로 함수 표현식은 변수 호이스팅은 가능해 undefined 가 출력되지만 할당이 이루어지지 않은 것이다.

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

console.log(add(2,5)) // 7
console.log(sub(2,5)) // TypeError

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

var sub = function(x,y) {
	return x - y;
}

console.dir 은 함수 객체까지 출력한다. ex) f add(x, y)

함수 선언문으로 정의된 함수는 함수 선언문 이전에 호출할 수 있지만 함수 표현식으로 정의한 함수는 함수 표현식 이전에 호출할 수 없다.

- 화살표 함수

화살표 함수는 ES6에서 도입된 함수이다. function 키워드 대신 =>를 사용한다

화살표 함수는 항상 익명 함수로 정의한다.

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

함수 호출

- 매개변수와 인수

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

var result = add(1,2);

코드를 살펴보자. x와 y는 매개변수라고 하고 1과2는 인수라 정의한다. 암묵적으로 매개변수가 생성되면 매개변수의 값에는 undefined가 할당된다. 그리고 인수 1과2가 매개변수에 할당되고 함수 몸체가 실행된다.

매개변수의 개수가 인수보다 많은 경우, 할당되지 않은 매개변수의 값은 undefined가 된다.

매개변수의 개수가 인수보다 적은 경우, 초과된 인수는 무시된다.

모든 인수는 암묵적으로 arguments 객체의 프로퍼티로 보관된다.

- 인수 초기화

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

인수를 초기화하여 NaN이 출력되는 것을 방지할 수 있다.

- 반환문

return을 실행하면 함수 몸체를 빠져나간다. 그 이후에 쓰여진 코드는 전혀 실행하지 않는다. 반환문을 생략하면 undefined를 반환한다. return 이후에 줄바꿈을 하고 출력값을 작성하면 자바스크립트 엔진에 의해 자동으로 세미콜론이 추가되어 undefined가 출력된다. 반환값 이후에 코드는 모두 무시되기 때문이다.

참조에 의한 전달과 값에 의한 전달(매우 중요)

우리가 앞에서 배운 것을 상기해보자. 원시 값은 원본이 훼손되지 않는다. 객체는 원본이 훼손된다고 배웠다.

function changeVal(primitive, obj) {
	primitive += 100;
    obj.name = 'Kim';
}

var num = 100;
var person = { name: 'Lee' };

changeVal(num, person);

console.log(num); // 100
console.log(person); // {name: "Kim"}

primitive는 원시 값이고 obj는 객체이다. 객체는 원본을 훼손시켜서 Kim이라는 값으로 바뀌었다. 하지만 num의 값은 100이다. 왜그런지는 매우 어렵지만 간단하게 설명해보겠다.

먼저 num = 100이라는 메모리를 만들었다. 함수에 인수를 넣으면서 부터 num = 100이라는 메모리를 하나 더 복사해서 만들었다. 둘의 주소는 다르다. 그리고 +=을 실행함으로써 200이라는 공간이 하나 더 생긴 것이다. 그렇다면 현재 100 2개, 200 1개가 생긴 것이다. 하지만 num을 호출 한 것은 원시값이므로 100이 출력되는 것이다. 200을 출력하고 싶으면 새로운 변수를 만들어 할당하거나 return 값이 있으면 된다. 하지만 함수의 return이 없고 코드 밖에서 호출만 있으므로 원시 값이 나올 수 밖에...

다양한 함수의 형태

- 즉시 실행 함수

함수 정의와 동시에 즉시 호출되는 함수이다. 단 한번만 호출이 가능하며 다시 호출할 수 없다. 익명함수를 사용하는 것이 일반적이다. 표현식 처럼 반드시 ()로 감싸야 한다. 그렇지 않으면 오류가 발생한다.

(function (매개변수) {
	//
 }(인수));
var res = (function (a, b) {
	return a * b;
}(3, 5));

console.log(res); // 15

- 재귀 함수

재귀함수는 본인을 다시 호출하는 함수이다. 주의해야 할 점은 탈출 조건을 반드시 만들어야 한다는 점이고 함수 외부에서 호출할 때에는 반드시 함수를 가리키는 식별자로 호출해야 한다는 점이다.

var factorial = function foo(n) {
	if(n <= 1) return 1; // 1이하면 종료
    return n * factorial(n - 1);
    
};

console.log(factorial(5)); // foo로 호출하면 Error

- 중첩 함수

중첩함수는 외부 함수 내부에서만 호출 할 수 있다. 자신을 포함하는 외부 함수를 돕는 헬퍼 역할을 한다.

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

outer();

중첩함수는 외부 함수 안에서만 호출이 가능하다. ex) inner()

- 콜백 함수

콜백함수는 매개변수로 함수를 받는 것이다. 함수의 매개변수를 통해 함수의 내부로 전달되는 함수를 콜백 함수라고 하며 콜백 함수를 전달받은 함수를 고차함수라고 한다. 고차 함수는 콜백함수를 자신의 일부로 합성한다.

콜백함수는 익명 함수 리터럴로 곧바로 고차 함수에 전달하는 것이 일반적이다.

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

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

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

- 순수 함수와 비순수 함수

외부 상태에 의존하지도 않고 변경하지도 않는 함수를 순수 함수 라 하고, 외부 상태에 의존하거나 외부 상태를 변경하는 함수를 비순수 함수 라고 한다.

  • 순수 함수: 언제나 동일한 값을 반환하고 최소 하나 이상의 인수를 받는다.
  • 비순수 함수: 외부 상태를 변경한다.
//순수 함수
var count = 0;

function increase(n) {
	return ++n;
}

count = increase(count);
console.log(count); // 1

//비순수 함수
var count = 0;

function increase() {
	return ++count;
}

increase();
console.log(count); // 1

위 코드를 보면 값도 똑같이 나오고 함수 형태도 비슷한데 구분을 어떻게 하는지 궁금해할 것이다. 자세히 살펴보면 순수함수는 매개변수 n을 받는다. 매개변수 값에 따라 return 값이 고정되어 있다. 외부에 전혀 영향을 주지 않는다는 의미이다. 하지만 비순수 함수는 매개변수를 받지 않는다. 외부에 있는 count 변수를 직접 참조하고 직접 변경하는 것과 같다. 결국에는 값이 일정할 수가 없는 것이다.

순수 함수를 이용하는 것이 프로그램의 안정성을 높이려는 노력의 일환이라 할 수 있다.

profile
블로그 이전했습니다

0개의 댓글